├── .gitignore ├── .npmignore ├── LICENSE.md ├── README.md ├── assets ├── elk.json ├── factory.jpg ├── loader.css ├── lut.png └── road.jpg ├── index.html ├── index.js ├── package.json └── src ├── add-background.js ├── add-mesh.js ├── load-json-model.js ├── load-texture.js ├── shaders ├── bg.frag ├── bg.vert ├── horse.frag └── horse.vert └── viewer.js /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | assets/ 7 | !assets/elk.json 8 | !assets/lut.png 9 | !assets/factory.jpg 10 | !assets/road.jpg 11 | !assets/loader.css -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | test 7 | test.js 8 | demo/ 9 | .npmignore 10 | LICENSE.md -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2015 Matt DesLauriers 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust 2 | 3 | [![experimental](http://badges.github.io/stability-badges/dist/experimental.svg)](http://github.com/badges/stability-badges) 4 | 5 | [![screen](http://i.imgur.com/AJF3bBv.jpg)](http://mattdesl.github.io/rust) 6 | 7 | http://mattdesl.github.io/rust 8 | 9 | Experiments with THREE.js and glslify. Some glslify features: 10 | 11 | - vertex animations using glsl-noise and glsl-easings 12 | - color grading with glsl-lut 13 | - background gradient with noise using glsl-film-grain 14 | 15 | ## License 16 | 17 | MIT, see [LICENSE.md](http://github.com/mattdesl/rust/blob/master/LICENSE.md) for details. 18 | -------------------------------------------------------------------------------- /assets/factory.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/rust/833b9e8c4ebfadb3041ea76099f49a7f2adffe50/assets/factory.jpg -------------------------------------------------------------------------------- /assets/loader.css: -------------------------------------------------------------------------------- 1 | /* Modified from: http://projects.lukehaas.me/css-loaders/ */ 2 | .loader { 3 | margin: 0px auto; 4 | font-size: 10px; 5 | font: sans-serif; 6 | position: relative; 7 | text-indent: -9999em; 8 | border-top: 0.2em solid rgba(0, 0, 0, 0.2); 9 | border-right: 0.2em solid rgba(0, 0, 0, 0.2); 10 | border-bottom: 0.2em solid rgba(0, 0, 0, 0.2); 11 | border-left: 0.2em solid #000; 12 | -webkit-transform: translateZ(0); 13 | -ms-transform: translateZ(0); 14 | transform: translateZ(0); 15 | -webkit-animation: spin 1.1s infinite linear; 16 | animation: spin 1.1s infinite linear; 17 | } 18 | .loader, 19 | .loader:after { 20 | border-radius: 50%; 21 | width: 3em; 22 | height: 3em; 23 | position: absolute; 24 | margin: auto; 25 | top: 0; 26 | left: 0; 27 | right: 0; 28 | bottom: 0; 29 | } 30 | @-webkit-keyframes spin { 31 | 0% { 32 | -webkit-transform: rotate(0deg); 33 | transform: rotate(0deg); 34 | } 35 | 100% { 36 | -webkit-transform: rotate(360deg); 37 | transform: rotate(360deg); 38 | } 39 | } 40 | @keyframes spin { 41 | 0% { 42 | -webkit-transform: rotate(0deg); 43 | transform: rotate(0deg); 44 | } 45 | 100% { 46 | -webkit-transform: rotate(360deg); 47 | transform: rotate(360deg); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /assets/lut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/rust/833b9e8c4ebfadb3041ea76099f49a7f2adffe50/assets/lut.png -------------------------------------------------------------------------------- /assets/road.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattdesl/rust/833b9e8c4ebfadb3041ea76099f49a7f2adffe50/assets/road.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | rust 7 | 8 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { polyfill } from 'es6-promise' 2 | polyfill() 3 | 4 | import THREE from 'three' 5 | import domready from 'domready' 6 | import viewer from './src/viewer' 7 | import loadGeometry from './src/load-json-model' 8 | import loadTexture from './src/load-texture' 9 | import assign from 'object-assign' 10 | 11 | import addMesh from './src/add-mesh' 12 | import addBackground from './src/add-background' 13 | 14 | domready(() => { 15 | const app = viewer({ 16 | alpha: false, 17 | canvas: document.querySelector('canvas'), 18 | preserveDrawingBuffer: false, 19 | antialias: true 20 | }) 21 | 22 | setTimeout(() => app.canvas.style.display = 'block', 10) 23 | 24 | const texOpt = { 25 | minFilter: THREE.LinearFilter, 26 | generateMipmaps: false, 27 | wrapS: THREE.RepeatWrapping, 28 | wrapT: THREE.RepeatWrapping 29 | } 30 | 31 | const loadTextures = Promise.all([ 32 | loadTexture('assets/factory.jpg', texOpt), 33 | loadTexture('assets/road.jpg', texOpt) 34 | ]) 35 | 36 | Promise.all([ 37 | loadGeometry('assets/elk.json'), 38 | loadTextures, 39 | loadTexture('assets/lut.png', { 40 | minFilter: THREE.LinearFilter, 41 | flipY: false, 42 | generateMipmaps: false 43 | }) 44 | ]) 45 | .then(result => { 46 | let [ geo, textures, lut ] = result 47 | addMesh(app, assign({}, geo, { 48 | textures, lut 49 | })) 50 | }) 51 | .then(null, (err) => { 52 | console.error("Got error") 53 | console.error(err.stack) 54 | }) 55 | .then(() => { 56 | document.querySelector('.loader').style.display = 'none' 57 | }) 58 | 59 | addBackground(app) 60 | app.controls.enabled = false 61 | app.start() 62 | }) 63 | 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rust", 3 | "version": "1.0.0", 4 | "description": "experiments", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "private": true, 8 | "author": { 9 | "name": "Matt DesLauriers", 10 | "email": "dave.des@gmail.com", 11 | "url": "https://github.com/mattdesl" 12 | }, 13 | "dependencies": { 14 | "canvas-fit": "^1.4.0", 15 | "domready": "^1.0.7", 16 | "eases": "^1.0.6", 17 | "es6-promise": "^2.1.0", 18 | "glsl-easings": "^1.0.0", 19 | "glsl-film-grain": "^1.0.2", 20 | "glsl-lut": "^1.1.0", 21 | "glsl-noise": "0.0.0", 22 | "glslify-hex": "^2.0.1", 23 | "glslify-import": "0.0.1", 24 | "load-json-xhr": "^3.0.1", 25 | "object-assign": "^2.0.0", 26 | "raf-loop": "^1.0.1", 27 | "three": "^0.70.0", 28 | "three-orbit-controls": "^69.0.4", 29 | "three-orbit-viewer": "^69.3.0", 30 | "xtend": "^4.0.0" 31 | }, 32 | "devDependencies": { 33 | "babelify": "^6.0.2", 34 | "browserify": "^9.0.8", 35 | "budo": "^3.0.4", 36 | "errorify": "^0.2.4", 37 | "garnish": "^2.1.3", 38 | "glslify": "^2.1.2", 39 | "uglify-js": "^2.4.20", 40 | "watchify": "^3.1.1" 41 | }, 42 | "scripts": { 43 | "build": "browserify index.js -t babelify -t glslify | uglifyjs -cm > bundle.js", 44 | "start": "budo index.js:bundle.js --live -v -t babelify -t glslify -p errorify | garnish" 45 | }, 46 | "glslify": { 47 | "transform": [ 48 | "glslify-hex" 49 | ] 50 | }, 51 | "keywords": [], 52 | "repository": { 53 | "type": "git", 54 | "url": "git://github.com/mattdesl/rust.git" 55 | }, 56 | "homepage": "https://github.com/mattdesl/rust", 57 | "bugs": { 58 | "url": "https://github.com/mattdesl/rust/issues" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/add-background.js: -------------------------------------------------------------------------------- 1 | import THREE from 'three' 2 | 3 | var glslify = require('glslify') 4 | 5 | export default function(app) { 6 | const resolution = new THREE.Vector2(app.width, app.height) 7 | const mat = new THREE.ShaderMaterial({ 8 | vertexShader: glslify('./shaders/bg.vert'), 9 | fragmentShader: glslify('./shaders/bg.frag'), 10 | uniforms: { 11 | iResolution: { type: 'v2', value: resolution }, 12 | iGlobalTime: { type: 'f', value: 0 }, 13 | }, 14 | depthTest: false, 15 | depthWrite: false, 16 | side: THREE.DoubleSide 17 | }) 18 | 19 | const geom = new THREE.PlaneGeometry(500, 500) 20 | const mesh = new THREE.Mesh(geom, mat) 21 | mesh.rotation.y = -Math.PI 22 | app.scene.add(mesh) 23 | 24 | app.on('tick', dt => { 25 | mat.uniforms.iGlobalTime.value += dt/1000 26 | }) 27 | 28 | app.on('resize', ({ width, height }) => { 29 | resolution.set(width, height) 30 | }) 31 | } -------------------------------------------------------------------------------- /src/add-mesh.js: -------------------------------------------------------------------------------- 1 | import THREE from 'three' 2 | import assign from 'object-assign' 3 | var glslify = require('glslify') 4 | // var ease = require('eases/sine-in-out') 5 | 6 | export default function(app, assets) { 7 | const resolution = new THREE.Vector2(app.width, app.height) 8 | const mat = new THREE.ShaderMaterial({ 9 | vertexShader: glslify('./shaders/horse.vert'), 10 | fragmentShader: glslify('./shaders/horse.frag'), 11 | uniforms: assign({}, THREE.UniformsLib.lights, { 12 | iResolution: { type: 'v2', value: resolution }, 13 | iChannel0: { type: 't', value: assets.textures[0] }, 14 | iChannel1: { type: 't', value: assets.textures[1] }, 15 | iLookup: { type: 't', value: assets.lut }, 16 | opacity: { type: 'f', value: 1 }, 17 | morphTargetInfluences: { type: 'f', value: 0 }, 18 | diffuse: { type: 'c', value: new THREE.Color(0xffffff) }, 19 | iGlobalTime: { type: 'f', value: 0 }, 20 | }), 21 | shading: THREE.FlatShading, 22 | lights: true, 23 | morphTargets: true, 24 | defines: { 25 | USE_MORPHTARGETS: '', 26 | USE_MAP: '' 27 | } 28 | }) 29 | 30 | const directionalLight = new THREE.DirectionalLight(0xffffff, 1); 31 | directionalLight.position.set(6, 10, -10); 32 | app.scene.add(directionalLight); 33 | 34 | app.scene.add(new THREE.HemisphereLight(0xecfbff, 0xb0ddff, 0.75)) 35 | 36 | const mesh = new THREE.Mesh(assets.geometry, mat) 37 | mesh.scale.multiplyScalar(0.01) 38 | mesh.position.set(-0.1, -0.6, 0) 39 | // mesh.rotation.y = 140 * Math.PI/180 40 | mesh.rotation.y = 90 * Math.PI/180 41 | app.scene.add(mesh) 42 | 43 | const animation = new THREE.MorphAnimation(mesh) 44 | animation.play() 45 | let time = 0 46 | 47 | app.on('tick', (dt) => { 48 | time += dt/1000 49 | mat.uniforms.iGlobalTime.value = time 50 | animation.update(dt) 51 | 52 | // rotate mesh? 53 | // let rotation = Math.sin(time) * 0.5 + 0.5 54 | // rotation = ease(rotation) * 2 - 1 55 | // rotation = rotation * Math.PI*0.5 56 | // mesh.rotation.y = Math.PI/2 + rotation*0.1 57 | }) 58 | 59 | app.on('resize', ({ width, height}) => { 60 | resolution.set(width, height) 61 | }) 62 | } -------------------------------------------------------------------------------- /src/load-json-model.js: -------------------------------------------------------------------------------- 1 | import THREE from 'three' 2 | import json from 'load-json-xhr' 3 | 4 | export default function(path) { 5 | return new Promise((resolve, reject) => { 6 | const loader = new THREE.JSONLoader(); 7 | json(path, (err, data) => { 8 | if (err) 9 | reject(err) 10 | else { 11 | resolve(loader.parse(data)) 12 | } 13 | }) 14 | }) 15 | } -------------------------------------------------------------------------------- /src/load-texture.js: -------------------------------------------------------------------------------- 1 | import THREE from 'three' 2 | import assign from 'object-assign' 3 | 4 | export default function(path, opt={}) { 5 | return new Promise((resolve, reject) => { 6 | THREE.ImageUtils.loadTexture(path, undefined, 7 | tex => { 8 | assign(tex, opt) 9 | resolve(tex) 10 | }, 11 | () => { 12 | reject(new Error(`could not load image ${path}`)) 13 | }) 14 | }) 15 | } -------------------------------------------------------------------------------- /src/shaders/bg.frag: -------------------------------------------------------------------------------- 1 | // varying vec2 vUv; 2 | uniform float iGlobalTime; 3 | uniform vec2 iResolution; 4 | 5 | const vec3 color1 = #fff; 6 | const vec3 color2 = #e1edf7; 7 | 8 | #pragma glslify: grain = require('glsl-film-grain') 9 | 10 | void main() { 11 | vec2 uv = gl_FragCoord.xy / iResolution.xy; 12 | 13 | vec2 aspect = vec2(iResolution.x / iResolution.y, 1.0); 14 | float zoom = 0.5; 15 | float dist = length((uv - 0.5) * aspect * zoom); 16 | 17 | vec3 color = mix(color1, color2, dist); 18 | 19 | float grainSize = 1.0; 20 | float g = grain(uv, iResolution / grainSize, iGlobalTime*2.0); 21 | color -= mix(0.0, g*0.3, dist); 22 | 23 | gl_FragColor.rgb = color; 24 | gl_FragColor.a = 1.0; 25 | } -------------------------------------------------------------------------------- /src/shaders/bg.vert: -------------------------------------------------------------------------------- 1 | // varying vec2 vUv; 2 | void main() { 3 | gl_Position = projectionMatrix * 4 | modelViewMatrix * 5 | vec4(position, 1.0); 6 | } -------------------------------------------------------------------------------- /src/shaders/horse.frag: -------------------------------------------------------------------------------- 1 | varying vec2 vUv; 2 | uniform float iGlobalTime; 3 | uniform sampler2D iChannel0; 4 | uniform sampler2D iChannel1; 5 | uniform sampler2D iLookup; 6 | uniform vec3 diffuse; 7 | uniform float opacity; 8 | varying vec3 vLightFront; 9 | varying vec3 vReflect; 10 | 11 | #pragma glslify: lut = require('glsl-lut') 12 | 13 | void main() { 14 | vec4 diffuseColor = vec4(diffuse, opacity); 15 | vec3 outgoingLight = diffuseColor.rgb * vLightFront; 16 | 17 | vec2 uv = vUv * 0.5 + 0.5; 18 | uv.y -= 0.04; //offset coords a bit 19 | // uv.x += -0.0; 20 | vec4 color1 = texture2D(iChannel0, uv); 21 | vec4 color2 = texture2D(iChannel1, uv); 22 | 23 | float fade = smoothstep(0.7, 0.4, uv.y); 24 | //mix two images 25 | vec4 color = mix(color1, color2, fade); 26 | //N% tonemap 27 | vec4 toned = mix(color, lut(color, iLookup), 0.45); 28 | //show some of the model 29 | outgoingLight = mix(outgoingLight, toned.rgb, 0.90); 30 | 31 | gl_FragColor = vec4(outgoingLight, opacity); 32 | } -------------------------------------------------------------------------------- /src/shaders/horse.vert: -------------------------------------------------------------------------------- 1 | varying vec2 vUv; 2 | varying vec3 vLightFront; 3 | 4 | uniform float iGlobalTime; 5 | uniform vec2 iResolution; 6 | uniform float morphTargetInfluences[ 4 ]; 7 | 8 | const float zoom = 2.0; 9 | const vec2 offset = vec2(0.0, -0.5); 10 | 11 | vec3 transformDirection( in vec3 normal, in mat4 matrix ) { 12 | return normalize( ( matrix * vec4( normal, 0.0 ) ).xyz ); 13 | } 14 | 15 | //env 16 | varying vec3 vReflect; 17 | 18 | //lighting 19 | uniform vec3 ambientLightColor; 20 | #if MAX_DIR_LIGHTS > 0 21 | uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ]; 22 | uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ]; 23 | #endif 24 | 25 | #if MAX_HEMI_LIGHTS > 0 26 | uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ]; 27 | uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ]; 28 | uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ]; 29 | #endif 30 | 31 | vec3 lighting(in vec3 normal) { 32 | vec3 lightColor = vec3(0.0); 33 | vec3 transformedNormal = normalize( normal ); 34 | 35 | #if MAX_DIR_LIGHTS > 0 36 | for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) { 37 | vec3 dirVector = transformDirection( directionalLightDirection[ i ], viewMatrix ); 38 | 39 | float dotProduct = dot( transformedNormal, dirVector ); 40 | vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) ); 41 | lightColor += directionalLightColor[ i ] * directionalLightWeighting; 42 | } 43 | #endif 44 | #if MAX_HEMI_LIGHTS > 0 45 | for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) { 46 | vec3 lVector = transformDirection( hemisphereLightDirection[ i ], viewMatrix ); 47 | 48 | float dotProduct = dot( transformedNormal, lVector ); 49 | float hemiDiffuseWeight = 0.5 * dotProduct + 0.5; 50 | float hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5; 51 | lightColor += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight ); 52 | } 53 | #endif 54 | lightColor += ambientLightColor; 55 | return lightColor; 56 | } 57 | 58 | #pragma glslify: noise = require('glsl-noise/simplex/4d') 59 | #pragma glslify: ease = require('glsl-easings/quartic-in-out') 60 | #pragma glslify: circ = require('glsl-easings/sine-in-out') 61 | 62 | void main() { 63 | float anim = ease(sin(iGlobalTime) * 0.5 + 0.5); 64 | vec3 objectNormal = vec3(normalMatrix * normal); 65 | 66 | //morphtargets 67 | vec3 morphed = vec3(0.0); 68 | morphed += (morphTarget0 - position) * morphTargetInfluences[0]; 69 | morphed += (morphTarget1 - position) * morphTargetInfluences[1]; 70 | morphed += (morphTarget2 - position) * morphTargetInfluences[2]; 71 | morphed += (morphTarget3 - position) * morphTargetInfluences[3]; 72 | morphed += position; 73 | 74 | vec3 center = vec3(0.0, 60.0, 50.0); 75 | vec3 dir = normalize(position - center); 76 | float spin = circ(sin(iGlobalTime)*0.5+0.5)*2.0-1.0; 77 | 78 | 79 | // float ripple = clamp(distance(position.xyz, center) / (100.0), 0.0, 1.0); 80 | // ripple = smoothstep(1.0, 0.85, ripple); 81 | // spin = mix(spin, ripple, (sin(iGlobalTime)*0.5+0.5)); 82 | // spin = mix(spin, spin + ripple, 0.2); 83 | 84 | float n = noise(vec4(normal.xyz * (spin * dir * 8.0), iGlobalTime)); 85 | // float explode = circ((sin(iGlobalTime*0.5)*0.5+0.5))*2.0-1.0; 86 | morphed += (n * 4.0); 87 | 88 | vec4 projected = projectionMatrix * 89 | modelViewMatrix * 90 | vec4(morphed, 1.0); 91 | 92 | gl_Position = projected; 93 | 94 | //lighting 95 | vLightFront = lighting(objectNormal); 96 | 97 | //uvs 98 | vec2 screenPos = projected.xy / projected.w; 99 | vUv = screenPos; 100 | vUv.x *= iResolution.x / iResolution.y; 101 | } -------------------------------------------------------------------------------- /src/viewer.js: -------------------------------------------------------------------------------- 1 | import THREE from 'three' 2 | import loop from 'raf-loop' 3 | 4 | import assign from 'object-assign' 5 | import domready from 'domready' 6 | import fitter from 'canvas-fit' 7 | 8 | 9 | const OrbitControls = require('three-orbit-controls')(THREE) 10 | 11 | export default function(opt) { 12 | opt = assign({}, opt) 13 | 14 | const dpr = Math.min(2, window.devicePixelRatio) 15 | const canvas = opt.canvas || document.createElement('canvas') 16 | const fit = fitter(canvas, window, dpr) 17 | 18 | const renderer = new THREE.WebGLRenderer(assign({ 19 | canvas: canvas 20 | }, opt)) 21 | 22 | const gl = renderer.getContext() 23 | 24 | const app = loop(draw) 25 | const scene = new THREE.Scene() 26 | const camera = new THREE.OrthographicCamera(-1, 1, 1, -1) 27 | const controls = new OrbitControls(camera, canvas) 28 | camera.position.copy(new THREE.Vector3(0, 0, -1.6)) 29 | camera.lookAt(new THREE.Vector3()) 30 | updateCamera() 31 | 32 | app.render = renderer.render.bind(renderer, scene, camera) 33 | 34 | //render each frame unless user wants to do it manually 35 | if (opt.rendering !== false) 36 | app.on('render', app.render.bind(app)) 37 | 38 | assign(app, { 39 | renderer, 40 | scene, 41 | camera, 42 | controls, 43 | gl, 44 | canvas 45 | }) 46 | 47 | window.addEventListener('resize', resize, false) 48 | renderer.setClearColor(0xf7f7f7, 1) 49 | process.nextTick(resize) 50 | return app 51 | 52 | function draw() { 53 | app.emit('render') 54 | } 55 | 56 | function resize() { 57 | fit() 58 | const width = window.innerWidth 59 | const height = window.innerHeight 60 | const size = { width, height } 61 | 62 | renderer.setSize(width, height) 63 | updateCamera() 64 | 65 | // camera.aspect = width / height 66 | assign(app, size) 67 | app.emit('resize', size) 68 | } 69 | 70 | function updateCamera () { 71 | const width = window.innerWidth 72 | const height = window.innerHeight 73 | const aspectX = width > height ? (width / height) : 1 74 | const aspectY = width > height ? 1 : (1 / (width / height)) 75 | camera.left = -1 * aspectX 76 | camera.right = 1 * aspectX 77 | camera.top = 1 * aspectY 78 | camera.bottom = -1 * aspectY 79 | camera.updateProjectionMatrix() 80 | } 81 | } --------------------------------------------------------------------------------