├── screenshot.jpg ├── screenshot2.jpg ├── assets ├── bokeh │ ├── c1.png │ ├── e2.jpg │ ├── pentagon.png │ └── pentagon2.png └── textures │ ├── example6.jpg │ ├── t1 │ ├── depth.jpg │ ├── normal.jpg │ └── example.jpg │ ├── t3 │ ├── depth.jpg │ ├── example.jpg │ └── example2.jpg │ ├── texture1.bmp │ ├── texture1.png │ ├── texture2.png │ └── ExportedFont1.bmp ├── main.css ├── libs ├── shaders │ ├── shaderpass.js │ ├── postproc.js │ ├── line.js │ └── quad.js ├── scenes │ ├── codrops-article │ │ ├── v1.js │ │ ├── v2.js │ │ ├── v3.js │ │ ├── v4.js │ │ └── v5.js │ ├── curl-noise-plane.js │ ├── curl-noise-sphere.js │ ├── trees.js │ ├── cat-and-trees.js │ └── procedural-city.js ├── geometries │ ├── line.js │ └── quad.js ├── globals.js ├── utils.js ├── createScene.js ├── perlinNoise.js ├── curlNoise.js ├── main.js └── dependencies │ └── OrbitControls.js ├── photo_upload.php ├── LICENSE ├── index.html └── README.md /screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/screenshot.jpg -------------------------------------------------------------------------------- /screenshot2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/screenshot2.jpg -------------------------------------------------------------------------------- /assets/bokeh/c1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/assets/bokeh/c1.png -------------------------------------------------------------------------------- /assets/bokeh/e2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/assets/bokeh/e2.jpg -------------------------------------------------------------------------------- /assets/bokeh/pentagon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/assets/bokeh/pentagon.png -------------------------------------------------------------------------------- /assets/bokeh/pentagon2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/assets/bokeh/pentagon2.png -------------------------------------------------------------------------------- /assets/textures/example6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/assets/textures/example6.jpg -------------------------------------------------------------------------------- /assets/textures/t1/depth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/assets/textures/t1/depth.jpg -------------------------------------------------------------------------------- /assets/textures/t1/normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/assets/textures/t1/normal.jpg -------------------------------------------------------------------------------- /assets/textures/t3/depth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/assets/textures/t3/depth.jpg -------------------------------------------------------------------------------- /assets/textures/texture1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/assets/textures/texture1.bmp -------------------------------------------------------------------------------- /assets/textures/texture1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/assets/textures/texture1.png -------------------------------------------------------------------------------- /assets/textures/texture2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/assets/textures/texture2.png -------------------------------------------------------------------------------- /assets/textures/t1/example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/assets/textures/t1/example.jpg -------------------------------------------------------------------------------- /assets/textures/t3/example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/assets/textures/t3/example.jpg -------------------------------------------------------------------------------- /assets/textures/t3/example2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/assets/textures/t3/example2.jpg -------------------------------------------------------------------------------- /assets/textures/ExportedFont1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Domenicobrz/Blurry/HEAD/assets/textures/ExportedFont1.bmp -------------------------------------------------------------------------------- /main.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | width: 100%; 3 | height: 100%; 4 | 5 | overflow: hidden; 6 | margin: 0; 7 | } -------------------------------------------------------------------------------- /libs/shaders/shaderpass.js: -------------------------------------------------------------------------------- 1 | let shaderpassv = ` 2 | varying vec2 vUv; 3 | 4 | void main() { 5 | gl_Position = vec4(position.xy, 0.0, 1.0); 6 | 7 | vUv = uv; 8 | }`; 9 | 10 | let shaderpassf = ""; -------------------------------------------------------------------------------- /photo_upload.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Domenicobrz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /libs/scenes/codrops-article/v1.js: -------------------------------------------------------------------------------- 1 | function setGlobals() { 2 | pointsPerFrame = 50000; 3 | 4 | cameraPosition = new THREE.Vector3(0, 0, 115); 5 | cameraFocalDistance = 100; 6 | 7 | minimumLineSize = 0.005; 8 | 9 | bokehStrength = 0.02; 10 | focalPowerFunction = 1; 11 | exposure = 0.009; 12 | distanceAttenuation = 0.002; 13 | 14 | useBokehTexture = true; 15 | bokehTexturePath = "assets/bokeh/pentagon2.png"; 16 | 17 | backgroundColor[0] *= 0.8; 18 | backgroundColor[1] *= 0.8; 19 | backgroundColor[2] *= 0.8; 20 | } 21 | 22 | let rand, nrand; 23 | let vec3 = function(x,y,z) { return new THREE.Vector3(x,y,z) }; 24 | 25 | function createScene() { 26 | Utils.setRandomSeed("3926153465010"); 27 | 28 | rand = function() { return Utils.rand(); }; 29 | nrand = function() { return rand() * 2 - 1; }; 30 | 31 | for(let i = 0; i < 10; i++) { 32 | lines.push( 33 | new Line({ 34 | v1: vec3(i, 0, 15), 35 | v2: vec3(i, 10, 15), 36 | 37 | c1: vec3(5, 5, 5), 38 | c2: vec3(5, 5, 5), 39 | }) 40 | ); 41 | } 42 | } -------------------------------------------------------------------------------- /libs/shaders/postproc.js: -------------------------------------------------------------------------------- 1 | let postprocv = ` 2 | 3 | varying vec2 vUv; 4 | 5 | void main() { 6 | gl_Position = vec4(position.xy, 0.0, 1.0); 7 | 8 | vUv = uv; 9 | }`; 10 | 11 | let postprocf = ` 12 | 13 | uniform sampler2D texture; 14 | uniform float uSamples; 15 | uniform float uExposure; 16 | uniform vec3 uBackgroundColor; 17 | uniform vec3 uResolution; 18 | uniform vec3 uCameraPosition; 19 | 20 | varying vec2 vUv; 21 | 22 | 23 | void main() { 24 | 25 | float chromaticAberrationStrength = 0.0; 26 | // uncomment if you want CA 27 | // chromaticAberrationStrength = length(vec2(0.5, 0.5) - vUv) * 0.0015; 28 | 29 | vec4 color = vec4( 30 | texture2D(texture, vUv + vec2(chromaticAberrationStrength, 0.0)).r, 31 | texture2D(texture, vUv).g, 32 | texture2D(texture, vUv + vec2(-chromaticAberrationStrength, 0.0)).b, 33 | 1.0 34 | ); 35 | 36 | if(color.x < 0.0) color.x = 0.0; 37 | if(color.y < 0.0) color.y = 0.0; 38 | if(color.z < 0.0) color.z = 0.0; 39 | 40 | const float gamma = 1.0; //2.2; 41 | vec3 hdrColor = (color.rgb) / (uSamples * uExposure); 42 | 43 | 44 | 45 | // reinhard tone mapping 46 | vec3 mapped = hdrColor / (hdrColor + vec3(1.0)); 47 | 48 | // gamma correction 49 | mapped = pow(mapped, vec3(1.0 / gamma)); 50 | 51 | 52 | gl_FragColor = vec4(uBackgroundColor + mapped, 1.0); 53 | }`; -------------------------------------------------------------------------------- /libs/geometries/line.js: -------------------------------------------------------------------------------- 1 | class Line { 2 | constructor(arg) { 3 | if(arguments.length === 12) { 4 | this.x1 = arguments[0]; 5 | this.y1 = arguments[1]; 6 | this.z1 = arguments[2]; 7 | 8 | this.x2 = arguments[3]; 9 | this.y2 = arguments[4]; 10 | this.z2 = arguments[5]; 11 | 12 | this.c1r = arguments[6]; 13 | this.c1g = arguments[7]; 14 | this.c1b = arguments[8]; 15 | 16 | this.c2r = arguments[9]; 17 | this.c2g = arguments[10]; 18 | this.c2b = arguments[11]; 19 | 20 | this.v1 = new THREE.Vector3(this.x1, this.y1, this.z1); 21 | this.v2 = new THREE.Vector3(this.x2, this.y2, this.z2); 22 | } else { 23 | this.x1 = arg.v1.x; 24 | this.y1 = arg.v1.y; 25 | this.z1 = arg.v1.z; 26 | 27 | this.x2 = arg.v2.x; 28 | this.y2 = arg.v2.y; 29 | this.z2 = arg.v2.z; 30 | 31 | this.c1r = arg.c1.x; 32 | this.c1g = arg.c1.y; 33 | this.c1b = arg.c1.z; 34 | 35 | this.c2r = arg.c2.x; 36 | this.c2g = arg.c2.y; 37 | this.c2b = arg.c2.z; 38 | 39 | this.v1 = arg.v1; 40 | this.v2 = arg.v2; 41 | } 42 | } 43 | 44 | set c1(value) { 45 | this.c1r = value.x; 46 | this.c1g = value.y; 47 | this.c1b = value.z; 48 | } 49 | 50 | set c2(value) { 51 | this.c2r = value.x; 52 | this.c2g = value.y; 53 | this.c2b = value.z; 54 | } 55 | } -------------------------------------------------------------------------------- /libs/globals.js: -------------------------------------------------------------------------------- 1 | // camera parameters 2 | var cameraPosition = new THREE.Vector3(0, 0, 100); 3 | var cameraTarget = new THREE.Vector3(0, 0, 0); 4 | 5 | var cameraFocalDistance = 49.19; 6 | var bokehStrength = 0.025; 7 | var exposure = 0.0019; 8 | var distanceAttenuation = 0; 9 | // set to 1 to have non-linear increase in focal strength 10 | var focalPowerFunction = 0; 11 | 12 | // how big lines should be on screen when they're in the focal plane 13 | var minimumLineSize = 0.015; 14 | 15 | // how many render calls are made each frame 16 | var drawCallsPerFrame = 50; 17 | 18 | var quadsTexturePath = "assets/textures/texture1.png"; 19 | 20 | 21 | var motionBlurFrames = 1; // needs to be an integer >= 1 22 | 23 | // wether each line has assigned a quantity of points proportional to its length or a fixed number instead 24 | var useLengthSampling = true; 25 | 26 | // if $useLengthSampling is false, every line will by rendered by default with $pointsPerLine points 27 | var pointsPerLine = 500; 28 | var pointsPerQuad = 500; 29 | 30 | // if $useLengthSampling is true, every line will be drawn with an amount of points that is proportional to the line's length, 31 | // use $pointsPerFrame to determine how many points will be drawn in a single drawcall. Keep in mind that each line is drawn with 32 | // at least one point 33 | var pointsPerFrame = 50000; 34 | var quadPointsPerFrame = 50000; 35 | 36 | // animation params 37 | var millisecondsPerFrame = Infinity; 38 | var framesCount = 200; 39 | var captureFrames = false; 40 | 41 | // additional bokeh params 42 | var useBokehTexture = false; 43 | var bokehTexturePath = "assets/bokeh/c1.png"; 44 | var backgroundColor = [21/255, 16/255, 16/255]; -------------------------------------------------------------------------------- /libs/utils.js: -------------------------------------------------------------------------------- 1 | let Utils = { }; 2 | 3 | function sfc32(a, b, c, d) { 4 | return function() { 5 | a >>>= 0; b >>>= 0; c >>>= 0; d >>>= 0; 6 | var t = (a + b) | 0; 7 | a = b ^ b >>> 9; 8 | b = c + (c << 3) | 0; 9 | c = (c << 21 | c >>> 11); 10 | d = d + 1 | 0; 11 | t = t + d | 0; 12 | c = c + t | 0; 13 | return (t >>> 0) / 4294967296; 14 | } 15 | } 16 | 17 | function xmur3(str) { 18 | for(var i = 0, h = 1779033703 ^ str.length; i < str.length; i++) 19 | h = Math.imul(h ^ str.charCodeAt(i), 3432918353); 20 | h = h << 13 | h >>> 19; 21 | return function() { 22 | h = Math.imul(h ^ h >>> 16, 2246822507); 23 | h = Math.imul(h ^ h >>> 13, 3266489909); 24 | return (h ^= h >>> 16) >>> 0; 25 | } 26 | } 27 | 28 | var seed = xmur3("apples"); 29 | Utils.rand = sfc32(seed(), seed(), seed(), seed()); 30 | 31 | Utils.setRandomSeed = function(string) { 32 | seed = xmur3(string); 33 | Utils.rand = sfc32(seed(), seed(), seed(), seed()); 34 | } 35 | 36 | Utils.getSeed = function() { 37 | return seed(); 38 | } 39 | 40 | Utils.hslToRgb = function(h, s, l) { 41 | var r, g, b; 42 | 43 | if(s == 0){ 44 | r = g = b = l; // achromatic 45 | }else{ 46 | var hue2rgb = function hue2rgb(p, q, t){ 47 | if(t < 0) t += 1; 48 | if(t > 1) t -= 1; 49 | if(t < 1/6) return p + (q - p) * 6 * t; 50 | if(t < 1/2) return q; 51 | if(t < 2/3) return p + (q - p) * (2/3 - t) * 6; 52 | return p; 53 | } 54 | 55 | var q = l < 0.5 ? l * (1 + s) : l + s - l * s; 56 | var p = 2 * l - q; 57 | r = hue2rgb(p, q, h + 1/3); 58 | g = hue2rgb(p, q, h); 59 | b = hue2rgb(p, q, h - 1/3); 60 | } 61 | 62 | return [r, g, b]; 63 | } -------------------------------------------------------------------------------- /libs/geometries/quad.js: -------------------------------------------------------------------------------- 1 | class Quad { 2 | constructor(x, y, z, us, vs, ue, ve) { 3 | let xx = x || 0; 4 | let yy = y || 0; 5 | let zz = z || 0; 6 | 7 | this.center = new THREE.Vector3(xx, yy, zz); 8 | this.width = 1; 9 | this.height = 1; 10 | this.weight = 1; 11 | 12 | this.v1 = new THREE.Vector3(xx - this.width * 0.5, yy - this.height * 0.5, zz); 13 | this.v2 = new THREE.Vector3(xx + this.width * 0.5, yy - this.height * 0.5, zz); 14 | this.v3 = new THREE.Vector3(xx - this.width * 0.5, yy + this.height * 0.5, zz); 15 | 16 | this.col = new THREE.Vector3(1, 1, 1); 17 | 18 | this.uv1 = new THREE.Vector2(us || 0, vs || 0); 19 | this.uv2 = new THREE.Vector2( 20 | ue !== undefined ? ue : 1, 21 | ve !== undefined ? ve : 1); 22 | 23 | return this; 24 | } 25 | 26 | color(r,g,b) { 27 | this.col.set(r,g,b); 28 | 29 | return this; 30 | } 31 | 32 | rotate(ax, ay, az, angle) { 33 | 34 | this.v1.applyAxisAngle(new THREE.Vector3(ax, ay, az), angle); 35 | this.v2.applyAxisAngle(new THREE.Vector3(ax, ay, az), angle); 36 | this.v3.applyAxisAngle(new THREE.Vector3(ax, ay, az), angle); 37 | 38 | return this; 39 | } 40 | 41 | translate(x, y, z) { 42 | 43 | this.v1.add(new THREE.Vector3(x, y, z)); 44 | this.v2.add(new THREE.Vector3(x, y, z)); 45 | this.v3.add(new THREE.Vector3(x, y, z)); 46 | 47 | return this; 48 | } 49 | 50 | scale(x, y, z) { 51 | 52 | if(y === undefined) y = x; 53 | if(z === undefined) z = x; 54 | 55 | this.v1.multiply(new THREE.Vector3(x, y, z)); 56 | this.v2.multiply(new THREE.Vector3(x, y, z)); 57 | this.v3.multiply(new THREE.Vector3(x, y, z)); 58 | 59 | return this; 60 | } 61 | } -------------------------------------------------------------------------------- /libs/scenes/codrops-article/v2.js: -------------------------------------------------------------------------------- 1 | function setGlobals() { 2 | pointsPerFrame = 50000; 3 | 4 | cameraPosition = new THREE.Vector3(0, 0, 115); 5 | cameraFocalDistance = 100; 6 | 7 | minimumLineSize = 0.005; 8 | 9 | bokehStrength = 0.02; 10 | focalPowerFunction = 1; 11 | exposure = 0.009; 12 | distanceAttenuation = 0.002; 13 | 14 | useBokehTexture = true; 15 | bokehTexturePath = "assets/bokeh/pentagon2.png"; 16 | 17 | backgroundColor[0] *= 0.8; 18 | backgroundColor[1] *= 0.8; 19 | backgroundColor[2] *= 0.8; 20 | } 21 | 22 | let rand, nrand; 23 | let vec3 = function(x,y,z) { return new THREE.Vector3(x,y,z) }; 24 | 25 | function createScene() { 26 | Utils.setRandomSeed("3926153465010"); 27 | 28 | rand = function() { return Utils.rand(); }; 29 | nrand = function() { return rand() * 2 - 1; }; 30 | 31 | computeWeb(); 32 | computeSparkles(); 33 | } 34 | 35 | function computeWeb() { 36 | let r1 = 35; 37 | let r2 = 17; 38 | for(let j = 0; j < r2; j++) { 39 | for(let i = 0; i < r1; i++) { 40 | let phi1 = (j + i * 0.075) / r2 * Math.PI * 2; 41 | let theta1 = i / r1 * Math.PI - Math.PI * 0.5; 42 | 43 | let phi2 = (j + (i+1) * 0.075) / r2 * Math.PI * 2; 44 | let theta2 = (i+1) / r1 * Math.PI - Math.PI * 0.5; 45 | 46 | 47 | let x1 = Math.sin(phi1) * Math.cos(theta1); 48 | let y1 = Math.sin(theta1); 49 | let z1 = Math.cos(phi1) * Math.cos(theta1); 50 | 51 | let x2 = Math.sin(phi2) * Math.cos(theta2); 52 | let y2 = Math.sin(theta2); 53 | let z2 = Math.cos(phi2) * Math.cos(theta2); 54 | 55 | 56 | lines.push( 57 | new Line({ 58 | v1: vec3(x1,z1,y1).multiplyScalar(15), 59 | v2: vec3(x2,z2,y2).multiplyScalar(15), 60 | c1: vec3(5,5,5), 61 | c2: vec3(5,5,5), 62 | }) 63 | ); 64 | } 65 | } 66 | } 67 | function computeSparkles() { } -------------------------------------------------------------------------------- /libs/scenes/curl-noise-plane.js: -------------------------------------------------------------------------------- 1 | function createScene(frames) { 2 | for(let j = -50; j < 50; j++) { 3 | for(let i = -50; i < 50; i++) { 4 | let x1 = i * 0.25; 5 | let y1 = 0; 6 | let z1 = j * 0.25; 7 | 8 | let x2 = (i+1) * 0.25; 9 | let y2 = 0; 10 | let z2 = j * 0.25; 11 | 12 | let v1 = curlNoise(new THREE.Vector3(x1 * 0.1, y1 * 0.1, z1 * 0.1 + 200)); 13 | let v2 = curlNoise(new THREE.Vector3(x2 * 0.1, y2 * 0.1, z2 * 0.1 + 200)); 14 | 15 | let colorMult = 0.1; 16 | 17 | colorMult *= Math.exp(-Math.abs(j * 0.025)); 18 | 19 | lines.push({ 20 | x1: x1 + v1.x, 21 | y1: y1 + v1.y, 22 | z1: z1 + v1.z, 23 | 24 | x2: x2 + v2.x, 25 | y2: y2 + v2.y, 26 | z2: z2 + v2.z, 27 | 28 | c1r: 1 * colorMult, 29 | c1g: 1 * colorMult, 30 | c1b: 1 * colorMult, 31 | 32 | c2r: 1 * colorMult, 33 | c2g: 1 * colorMult, 34 | c2b: 1 * colorMult, 35 | }); 36 | 37 | 38 | if(Math.random() < 0.6) { 39 | 40 | let y = Math.random(); 41 | if(Math.random() > 0.8) { 42 | colorMult *= 4; 43 | } 44 | 45 | lines.push({ 46 | x1: x1 + v1.x, 47 | y1: y1 + v1.y, 48 | z1: z1 + v1.z, 49 | 50 | x2: x1 + v1.x, 51 | y2: y1 + v1.y + y, 52 | z2: z1 + v1.z, 53 | 54 | c1r: 1 * colorMult, 55 | c1g: 1 * colorMult, 56 | c1b: 1 * colorMult, 57 | 58 | c2r: 1 * colorMult, 59 | c2g: 1 * colorMult, 60 | c2b: 1 * colorMult, 61 | }); 62 | 63 | } 64 | 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
79 |

press o or p to change focal length

80 |

press k or l to change bokeh strength

81 |

press v or b to change exposure

82 |

press u or i to change distance attenuation

83 |

press m to change focal strength equation

84 |

press h to hide this menu

85 |
86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /libs/scenes/curl-noise-sphere.js: -------------------------------------------------------------------------------- 1 | function createScene(frames) { 2 | let nrings = 150; 3 | 4 | for(let j = 0; j < nrings; j++) { 5 | let angle = (j / nrings) * Math.PI; 6 | let p = new THREE.Vector3(0, 0, 1); 7 | p.applyAxisAngle(new THREE.Vector3(1, 0, 0), angle); 8 | 9 | let rad = p.y; 10 | let z = p.z; 11 | 12 | let trad = 5.75; 13 | 14 | let nsegments = 70 + Math.abs(Math.floor(rad * 360)); 15 | 16 | for(let i = 0; i < nsegments; i++) { 17 | 18 | let a1 = (i / nsegments) * Math.PI * 2; 19 | let a2 = ((i+1) / nsegments) * Math.PI * 2; 20 | 21 | let frad = trad; 22 | if(Math.random() > 0.92) frad = (frad + 0.15); 23 | 24 | let x1 = Math.cos(a1) * rad * frad; 25 | let y1 = Math.sin(a1) * rad * frad; 26 | let z1 = z * frad; 27 | 28 | let x2 = Math.cos(a2) * rad * frad; 29 | let y2 = Math.sin(a2) * rad * frad; 30 | let z2 = z * frad; 31 | 32 | let noiseSpeed = 0.5; 33 | let noiseStrength1 = 0.1 + curlNoise(new THREE.Vector3(x1 * noiseSpeed * 0.3, y1 * noiseSpeed * 0.3, z1 * noiseSpeed * 0.3)).x * 0.7; 34 | let noiseStrength2 = 0.1 + curlNoise(new THREE.Vector3(x2 * noiseSpeed * 0.3, y2 * noiseSpeed * 0.3, z2 * noiseSpeed * 0.3)).x * 0.7; 35 | let v1 = curlNoise(new THREE.Vector3(x1 * noiseSpeed, y1 * noiseSpeed, z1 * noiseSpeed)).multiplyScalar(noiseStrength1); 36 | let v2 = curlNoise(new THREE.Vector3(x2 * noiseSpeed, y2 * noiseSpeed, z2 * noiseSpeed)).multiplyScalar(noiseStrength2); 37 | 38 | let colorMult = 0.1; 39 | let colorMult2 = 0.1; 40 | 41 | 42 | let ldir = new THREE.Vector3(-0.2, -0.35, -0.5); 43 | ldir.normalize(); 44 | ldir.multiplyScalar(-1); 45 | 46 | let normal = new THREE.Vector3(x1, y1, z1); 47 | normal.normalize(); 48 | 49 | let diffuse1 = Math.pow(Math.max(normal.dot(ldir), 0.0), 3.0); 50 | let diffuse2 = Math.pow(Math.max(normal.dot(ldir), 0.0), 1.5); 51 | colorMult *= diffuse1; 52 | colorMult2 *= diffuse1; 53 | colorMult += 0.002; 54 | colorMult2 += 0.002; 55 | 56 | 57 | let t = 1; 58 | 59 | lines.push({ 60 | x1: x1 + v1.x, 61 | y1: y1 + v1.y, 62 | z1: z1 + v1.z, 63 | 64 | x2: x2 + v2.x, 65 | y2: y2 + v2.y, 66 | z2: z2 + v2.z, 67 | 68 | c1r: 1 * colorMult, 69 | c1g: 1 * colorMult * t, 70 | c1b: 1 * colorMult * t, 71 | 72 | c2r: 1 * colorMult, 73 | c2g: 1 * colorMult * t, 74 | c2b: 1 * colorMult * t, 75 | }); 76 | 77 | if(Math.random() > 0.975) { 78 | 79 | let rc = 2; 80 | let rd1 = 1 + Math.random() * 0.2 81 | let rd2 = rd1 + Math.pow(Math.random(), 2) * 0.2; 82 | if(Math.random() > 0.8) { 83 | rc = 6; 84 | } 85 | 86 | lines.push({ 87 | x1: (x1 + v1.x) * rd1, 88 | y1: (y1 + v1.y) * rd1, 89 | z1: (z1 + v1.z) * rd1, 90 | 91 | x2: (x1 + v1.x) * rd2, 92 | y2: (y1 + v1.y) * rd2, 93 | z2: (z1 + v1.z) * rd2, 94 | 95 | c1r: rc * colorMult2, 96 | c1g: rc * colorMult2, 97 | c1b: rc * colorMult2, 98 | 99 | c2r: rc * colorMult2, 100 | c2g: rc * colorMult2, 101 | c2b: rc * colorMult2, 102 | }); 103 | } 104 | 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blurry 2 | Simulating depth of field with particles on a shader 3 | 4 | 5 | 6 | 7 | 8 | ------ 9 | [Live demo here](https://domenicobrz.github.io/webgl/projects/DOFlinesrenderer/) 10 | 11 | How to use 12 | ====== 13 | 14 | Inside `libs/createScene.js` you can code the scene you want to render, only lines and quads are supported atm, here's an example on how to populate the `lines` array: 15 | 16 | ```javascript 17 | function createScene(frame) { // frame is used to make animations, I'll update soon the readme to explain how that's done 18 | // lines is a global array 19 | lines.push({ 20 | // first vertex of the line 21 | x1: x1, 22 | y1: y1, 23 | z1: z1, 24 | 25 | // second vertex of the line 26 | x2: x2, 27 | y2: y2, 28 | z2: z2, 29 | 30 | // color of the first vertex, can take values bigger than 1.0 31 | c1r: 1, 32 | c1g: 0, 33 | c1b: 0, 34 | 35 | // color of the second vertex, can take values bigger than 1.0 36 | c2r: 0, 37 | c2g: 0, 38 | c2b: 1, 39 | 40 | // optional, if $useLengthSampling is set to true this variable will change the weight this lines has in the distribution of points for each line 41 | weight: 1, 42 | }); 43 | 44 | 45 | let quad = new Quad(0,0,0, /* <- starting position */ 0,0,1,1 /* texture uvs */) 46 | .scale(0.5) 47 | .color(100, 50, 10) 48 | .translate(0, 1, 0) 49 | .rotate(0, 0, 1, /* <- rotation axis */, 0.5 /* <- rotation angle */) 50 | // quads is a global array 51 | quads.push(quad); 52 | } 53 | ``` 54 | 55 | Quads can make use of a single texture, specified in `libs/globals.js` 56 | 57 | ------ 58 | 59 | You can change various parameters of the renderer by adding a `setGlobals()` function inside `libs/createScene.js` 60 | 61 | ```javascript 62 | function setGlobals() { 63 | // camera parameters 64 | cameraPosition = new THREE.Vector3(0, 0, 100); 65 | cameraTarget = new THREE.Vector3(0, 0, 0); 66 | 67 | cameraFocalDistance = 49.19; 68 | bokehStrength = 0.095; 69 | exposure = 0.0019; 70 | // set to 1 to have non-linear increase in focal strength 71 | focalPowerFunction = 0; 72 | 73 | // how much light fades as you get out of the focal plane 74 | distanceAttenuation = 0; 75 | 76 | // how big lines should be on screen when they're in the focal plane 77 | minimumLineSize = 0.015; 78 | 79 | // how many render calls are made each frame 80 | drawCallsPerFrame = 5; 81 | 82 | // texture used by quads when specifying uvs 83 | quadsTexturePath = "assets/textures/ExportedFont1.bmp"; 84 | 85 | 86 | // wether each line has assigned a quantity of points proportional to its length or a fixed number instead 87 | useLengthSampling = false; 88 | 89 | // if $useLengthSampling is false, every line will by rendered by default with $pointsPerLine points, same for $pointsPerQuad 90 | pointsPerLine = 25; 91 | pointsPerQuad = 500; 92 | 93 | // if $useLengthSampling is true, every line will be drawn with an amount of points that 94 | // is proportional to the line's length, (or quad's area length for $quadPointsPerFrame) 95 | // use $pointsPerFrame/$quadPointsPerFrame to determine how many points will be drawn in 96 | // a single drawcall. Keep in mind that each line/quad is drawn with 97 | // at least one point 98 | pointsPerFrame = 100000; 99 | quadPointsPerFrame = 50000; 100 | 101 | // wether to use a bokeh texture or not, keep an eye on render times 102 | // since they will be a bit slower when using bokeh textures 103 | useBokehTexture = false; 104 | bokehTexturePath = "assets/bokeh/c1.png"; 105 | // static background color (is additive with the rest of the scene) 106 | backgroundColor = [21/255, 16/255, 16/255]; 107 | } 108 | 109 | 110 | ``` 111 | `setGlobals()` will be called once at startup 112 | 113 | 114 | The threejs source attached in the repo was modified to always disable frustum culling (check `libs/main.js` to see the exact changes) 115 | 116 | Credits 117 | ------ 118 | The DOF displacement algorithm was taken from [This blog post](https://inconvergent.net/2019/depth-of-field/) -------------------------------------------------------------------------------- /libs/shaders/line.js: -------------------------------------------------------------------------------- 1 | let linev = ` 2 | attribute vec3 position1; 3 | attribute vec3 color1; 4 | attribute vec3 color2; 5 | attribute vec4 aSeed; 6 | 7 | uniform float uRandom; 8 | uniform vec4 uRandomVec4; 9 | uniform float uFocalDepth; 10 | uniform float uBokehStrength; 11 | uniform float uMinimumLineSize; 12 | uniform float uFocalPowerFunction; 13 | uniform float uTime; 14 | uniform float uDistanceAttenuation; 15 | 16 | uniform sampler2D uBokehTexture; 17 | 18 | varying vec3 vColor; 19 | 20 | 21 | 22 | 23 | 24 | 25 | // the function below is hash12 from https://www.shadertoy.com/view/4djSRW - I just renamed it nrand() 26 | // sin based random functions wont work 27 | float nrand(vec2 p) 28 | { 29 | vec3 p3 = fract(vec3(p.xyx) * .1031); 30 | p3 += dot(p3, p3.yzx + 19.19); 31 | return fract((p3.x + p3.y) * p3.z); 32 | } 33 | 34 | float n1rand( vec2 n ) 35 | { 36 | float t = fract( uTime ); 37 | float nrnd0 = nrand( n + 0.7*t ); 38 | return nrnd0; 39 | } 40 | 41 | 42 | void main() { 43 | 44 | // Uncomment these and comment the ones below and have fun with the result you'll get 45 | // float o1 = n1rand( vec2(uRandom + aSeed.x, uRandomVec4.x) ); 46 | // float o2 = n1rand( vec2(uRandom + aSeed.x, uRandomVec4.x) ); 47 | // float o3 = n1rand( vec2(uRandom + aSeed.x, uRandomVec4.x) ); 48 | // float o4 = n1rand( vec2(uRandom + aSeed.x, uRandomVec4.x) ); 49 | // float o5 = n1rand( vec2(uRandom + aSeed.x, uRandomVec4.w) ); 50 | 51 | float o1 = n1rand( vec2(uRandom + aSeed.x, uRandomVec4.x) ); 52 | float o2 = n1rand( vec2(uRandom + aSeed.y, uRandomVec4.y) ); 53 | float o3 = n1rand( vec2(uRandom + aSeed.z, uRandomVec4.z) ); 54 | float o4 = n1rand( vec2(uRandom + aSeed.w, uRandomVec4.w) ); 55 | float o5 = n1rand( vec2(uRandom + aSeed.w, uRandomVec4.w) ); 56 | 57 | 58 | 59 | 60 | 61 | 62 | float t = o1; 63 | vec3 positiont = position * (1.0 - t) + position1 * t; 64 | vec3 viewSpacePositionT = (modelViewMatrix * vec4(positiont, 1.0)).xyz; 65 | vColor = color1 * (1.0 - t) + color2 * t; 66 | 67 | float distanceFromFocalPoint = abs(viewSpacePositionT.z - (-uFocalDepth)); 68 | if(uFocalPowerFunction > 0.5) { 69 | distanceFromFocalPoint = pow(distanceFromFocalPoint, 1.5); 70 | } 71 | 72 | 73 | float bokehStrength = distanceFromFocalPoint * uBokehStrength; 74 | bokehStrength = max(bokehStrength, uMinimumLineSize); 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | #if USE_BOKEH_TEXTURE 84 | vec4 randNumbers = vec4( o2, o3, o4, o5 ); 85 | float ux = randNumbers.x; 86 | float uy = randNumbers.y; 87 | 88 | float x = (ux * 2.0 - 1.0) * bokehStrength; 89 | float y = (uy * 2.0 - 1.0) * bokehStrength; 90 | 91 | vec3 bokehVal = texture2D(uBokehTexture, vec2(ux, uy)).xyz; 92 | viewSpacePositionT += vec3(x, y, 0.0); 93 | vColor *= bokehVal; 94 | #else 95 | // if we're not using a bokeh texture we'll randomly displace points 96 | // in a sphere 97 | 98 | vec4 randNumbers = vec4( o2, o3, o4, o5 ); 99 | 100 | float lambda = randNumbers.x; 101 | float u = randNumbers.y * 2.0 - 1.0; 102 | float phi = randNumbers.z * 6.28; 103 | float R = bokehStrength; 104 | 105 | float x = R * pow(lambda, 0.33333) * sqrt(1.0 - u * u) * cos(phi); 106 | float y = R * pow(lambda, 0.33333) * sqrt(1.0 - u * u) * sin(phi); 107 | float z = R * pow(lambda, 0.33333) * u; 108 | 109 | viewSpacePositionT += vec3(x, y, z); 110 | #endif 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | // two different functions for color attenuation if you need it 127 | 128 | vColor = vec3( 129 | vColor.r * exp(-distanceFromFocalPoint * uDistanceAttenuation), 130 | vColor.g * exp(-distanceFromFocalPoint * uDistanceAttenuation), 131 | vColor.b * exp(-distanceFromFocalPoint * uDistanceAttenuation) 132 | ); 133 | 134 | // vColor = vec3( 135 | // vColor.r / (1.0 + pow(distanceFromFocalPoint * 0.015, 2.71828)), 136 | // vColor.g / (1.0 + pow(distanceFromFocalPoint * 0.015, 2.71828)), 137 | // vColor.b / (1.0 + pow(distanceFromFocalPoint * 0.015, 2.71828)) 138 | // ); 139 | 140 | 141 | // vec4 projectedPosition = projectionMatrix * modelViewMatrix * vec4(positiont, 1.0); 142 | vec4 projectedPosition = projectionMatrix * vec4(viewSpacePositionT, 1.0); 143 | gl_Position = projectedPosition; 144 | 145 | gl_PointSize = 1.0; 146 | }`; 147 | 148 | let linef = ` 149 | varying vec3 vColor; 150 | 151 | void main() { 152 | gl_FragColor = vec4(vColor, 1.0); 153 | }`; -------------------------------------------------------------------------------- /libs/shaders/quad.js: -------------------------------------------------------------------------------- 1 | let quadv = ` 2 | attribute vec3 position1; 3 | attribute vec3 position2; 4 | attribute vec3 uv1; 5 | attribute vec3 uv2; 6 | attribute vec3 color; 7 | attribute vec4 aSeeds; 8 | 9 | uniform sampler2D uTexture; 10 | uniform sampler2D uNormalMap; 11 | uniform sampler2D uBokehTexture; 12 | 13 | uniform float uRandom; 14 | uniform vec4 uRandomVec4; 15 | uniform float uFocalDepth; 16 | uniform float uBokehStrength; 17 | uniform float uMinimumLineSize; 18 | uniform float uFocalPowerFunction; 19 | uniform float uTime; 20 | uniform float uDistanceAttenuation; 21 | 22 | varying vec3 vColor; 23 | 24 | 25 | // the function below is hash12 from https://www.shadertoy.com/view/4djSRW - I just renamed it nrand() 26 | // sin based random functions wont work 27 | float nrand(vec2 p) 28 | { 29 | vec3 p3 = fract(vec3(p.xyx) * .1031); 30 | p3 += dot(p3, p3.yzx + 19.19); 31 | return fract((p3.x + p3.y) * p3.z); 32 | } 33 | 34 | float n1rand( vec2 n ) 35 | { 36 | float t = fract( uTime ); 37 | float nrnd0 = nrand( n + 0.7*t ); 38 | return nrnd0; 39 | } 40 | 41 | 42 | void main() { 43 | 44 | // Uncomment these and comment the ones below and have fun with the result you'll get 45 | // float o1 = n1rand( vec2(uRandom + aSeeds.x, uRandomVec4.x) ); 46 | // float o2 = n1rand( vec2(uRandom + aSeeds.x, uRandomVec4.x) ); 47 | // float o3 = n1rand( vec2(uRandom + aSeeds.x, uRandomVec4.x) ); 48 | // float o4 = n1rand( vec2(uRandom + aSeeds.x, uRandomVec4.x) ); 49 | // float o5 = n1rand( vec2(uRandom + aSeeds.x, uRandomVec4.w) ); 50 | 51 | float o1 = n1rand( vec2(uRandom + aSeeds.x, uRandomVec4.x) ); 52 | float o2 = n1rand( vec2(uRandom + aSeeds.y, uRandomVec4.y) ); 53 | float o3 = n1rand( vec2(uRandom + aSeeds.z, uRandomVec4.z) ); 54 | float o4 = n1rand( vec2(uRandom + aSeeds.w, uRandomVec4.w) ); 55 | float o5 = n1rand( vec2(uRandom + aSeeds.w, uRandomVec4.w) ); 56 | 57 | 58 | 59 | 60 | 61 | 62 | float uu = fract(o1 + uRandomVec4.x + aSeeds.z); 63 | float vv = fract(o2 + uRandomVec4.y + aSeeds.w); 64 | vec3 positiont = position + uu * (position1 - position) + vv * (position2 - position); 65 | vec3 viewSpacePositionT = (modelViewMatrix * vec4(positiont, 1.0)).xyz; 66 | 67 | // ******** texture coordinates calculation 68 | float ud = uv2.x - uv1.x; 69 | float vd = uv2.y - uv1.y; 70 | float ut = uv1.x + fract(o1 + uRandomVec4.x + aSeeds.z) * ud; 71 | float vt = uv1.y + fract(o2 + uRandomVec4.y + aSeeds.w) * vd; 72 | // ******** texture coordinates calculation - END 73 | 74 | vColor = color * (texture2D(uTexture, vec2(ut, vt)).rgb); 75 | vColor = pow(vColor, vec3(2.0)); 76 | 77 | 78 | 79 | 80 | 81 | 82 | float distanceFromFocalPoint = abs(viewSpacePositionT.z - (-uFocalDepth)); 83 | if(uFocalPowerFunction > 0.5) { 84 | distanceFromFocalPoint = pow(distanceFromFocalPoint, 1.5); 85 | } 86 | 87 | 88 | float bokehStrength = distanceFromFocalPoint * uBokehStrength; 89 | bokehStrength = max(bokehStrength, 0.0); //uMinimumLineSize); 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | #if USE_BOKEH_TEXTURE 101 | vec4 randNumbers = vec4( o2, o3, o4, o5 ); 102 | float ux = randNumbers.x; 103 | float uy = randNumbers.y; 104 | 105 | float x = (ux * 2.0 - 1.0) * bokehStrength; 106 | float y = (uy * 2.0 - 1.0) * bokehStrength; 107 | 108 | float bokehVal = texture2D(uBokehTexture, vec2(ux, uy)).x; 109 | viewSpacePositionT += vec3(x, y, 0.0); 110 | vColor *= bokehVal; 111 | #else 112 | // if we're not using a bokeh texture we'll randomly displace points 113 | // in a sphere 114 | 115 | vec4 randNumbers = vec4( o2, o3, o4, o5 ); 116 | 117 | float lambda = randNumbers.x; 118 | float u = randNumbers.y * 2.0 - 1.0; 119 | float phi = randNumbers.z * 6.28; 120 | float R = bokehStrength; 121 | 122 | float x = R * pow(lambda, 0.33333) * sqrt(1.0 - u * u) * cos(phi); 123 | float y = R * pow(lambda, 0.33333) * sqrt(1.0 - u * u) * sin(phi); 124 | float z = R * pow(lambda, 0.33333) * u; 125 | 126 | viewSpacePositionT += vec3(x, y, z); 127 | #endif 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | // two different functions for color attenuation if you need it 142 | 143 | vColor = vec3( 144 | vColor.r * exp(-distanceFromFocalPoint * uDistanceAttenuation * 0.5), 145 | vColor.g * exp(-distanceFromFocalPoint * uDistanceAttenuation * 0.5), 146 | vColor.b * exp(-distanceFromFocalPoint * uDistanceAttenuation * 0.5) 147 | ); 148 | 149 | // vColor = vec3( 150 | // vColor.r / (1.0 + pow(distanceFromFocalPoint * 0.05, 2.71828)), 151 | // vColor.g / (1.0 + pow(distanceFromFocalPoint * 0.05, 2.71828)), 152 | // vColor.b / (1.0 + pow(distanceFromFocalPoint * 0.05, 2.71828)) 153 | // ); 154 | 155 | 156 | vec4 projectedPosition = projectionMatrix * vec4(viewSpacePositionT, 1.0); 157 | gl_Position = projectedPosition; 158 | 159 | gl_PointSize = 1.0; 160 | }`; 161 | 162 | let quadf = ` 163 | 164 | varying vec3 vColor; 165 | 166 | void main() { 167 | gl_FragColor = vec4(vColor, 1.0); 168 | }`; -------------------------------------------------------------------------------- /libs/scenes/codrops-article/v3.js: -------------------------------------------------------------------------------- 1 | function setGlobals() { 2 | pointsPerFrame = 50000; 3 | 4 | cameraPosition = new THREE.Vector3(0, 0, 115); 5 | cameraFocalDistance = 100; 6 | 7 | minimumLineSize = 0.005; 8 | 9 | bokehStrength = 0.02; 10 | focalPowerFunction = 1; 11 | exposure = 0.009; 12 | distanceAttenuation = 0.002; 13 | 14 | useBokehTexture = true; 15 | bokehTexturePath = "assets/bokeh/pentagon2.png"; 16 | 17 | backgroundColor[0] *= 0.8; 18 | backgroundColor[1] *= 0.8; 19 | backgroundColor[2] *= 0.8; 20 | } 21 | 22 | let rand, nrand; 23 | let vec3 = function(x,y,z) { return new THREE.Vector3(x,y,z) }; 24 | 25 | function createScene() { 26 | Utils.setRandomSeed("3926153465010"); 27 | 28 | rand = function() { return Utils.rand(); }; 29 | nrand = function() { return rand() * 2 - 1; }; 30 | 31 | computeWeb(); 32 | computeSparkles(); 33 | } 34 | 35 | function computeWeb() { 36 | let r1 = 35; 37 | let r2 = 17; 38 | for(let j = 0; j < r2; j++) { 39 | for(let i = 0; i < r1; i++) { 40 | let phi1 = (j + i * 0.075) / r2 * Math.PI * 2; 41 | let theta1 = i / r1 * Math.PI - Math.PI * 0.5; 42 | 43 | let phi2 = (j + (i+1) * 0.075) / r2 * Math.PI * 2; 44 | let theta2 = (i+1) / r1 * Math.PI - Math.PI * 0.5; 45 | 46 | 47 | let x1 = Math.sin(phi1) * Math.cos(theta1); 48 | let y1 = Math.sin(theta1); 49 | let z1 = Math.cos(phi1) * Math.cos(theta1); 50 | 51 | let x2 = Math.sin(phi2) * Math.cos(theta2); 52 | let y2 = Math.sin(theta2); 53 | let z2 = Math.cos(phi2) * Math.cos(theta2); 54 | 55 | 56 | lines.push( 57 | new Line({ 58 | v1: vec3(x1,z1,y1).multiplyScalar(15), 59 | v2: vec3(x2,z2,y2).multiplyScalar(15), 60 | c1: vec3(5,5,5), 61 | c2: vec3(5,5,5), 62 | }) 63 | ); 64 | } 65 | } 66 | 67 | // intersect many 3d planes against all the lines we made so far 68 | for(let i = 0; i < 4500; i++) { 69 | let x0 = nrand() * 15; 70 | let y0 = nrand() * 15; 71 | let z0 = nrand() * 15; 72 | 73 | let dir = vec3(nrand(), nrand(), nrand()).normalize(); 74 | findIntersectingEdges(vec3(x0, y0, z0), dir); 75 | } 76 | } 77 | function computeSparkles() { } 78 | 79 | 80 | function findIntersectingEdges(center, dir) { 81 | 82 | let contactPoints = []; 83 | for(line of lines) { 84 | let ires = intersectsPlane( 85 | center, dir, 86 | line.v1, line.v2 87 | ); 88 | 89 | if(ires === false) continue; 90 | 91 | contactPoints.push(ires); 92 | } 93 | 94 | if(contactPoints.length < 2) return; 95 | 96 | let randCpIndex = Math.floor(rand() * contactPoints.length); 97 | let randCp = contactPoints[randCpIndex]; 98 | 99 | // lets search the closest contact point from randCp 100 | let minl = Infinity; 101 | let minI = -1; 102 | for(let i = 0; i < contactPoints.length; i++) { 103 | 104 | if(i === randCpIndex) continue; 105 | 106 | let cp2 = contactPoints[i]; 107 | 108 | // 3d point in space of randCp 109 | let v1 = vec3(randCp.x, randCp.y, randCp.z); 110 | // 3d point in space of the contact point we're testing for proximity 111 | let v2 = vec3(cp2.x, cp2.y, cp2.z); 112 | 113 | let sv = vec3(v2.x - v1.x, v2.y - v1.y, v2.z - v1.z); 114 | // "l" holds the euclidean distance between the two contact points 115 | let l = sv.length(); 116 | 117 | // if "l" is smaller than the minimum distance we've registered so far, store this contact point's index as minI 118 | if(l < minl) { 119 | minl = l; 120 | minI = i; 121 | } 122 | } 123 | 124 | let cp1 = contactPoints[randCpIndex]; 125 | let cp2 = contactPoints[minI]; 126 | 127 | lines.push( 128 | new Line({ 129 | v1: vec3(cp1.x, cp1.y, cp1.z), 130 | v2: vec3(cp2.x, cp2.y, cp2.z), 131 | c1: vec3(2,2,2), 132 | c2: vec3(2,2,2), 133 | }) 134 | ); 135 | } 136 | 137 | function intersectsPlane(planePoint, planeNormal, linePoint, linePoint2) { 138 | 139 | let lineDirection = new THREE.Vector3(linePoint2.x - linePoint.x, linePoint2.y - linePoint.y, linePoint2.z - linePoint.z); 140 | let lineLength = lineDirection.length(); 141 | lineDirection.normalize(); 142 | 143 | if (planeNormal.dot(lineDirection) === 0) { 144 | return false; 145 | } 146 | 147 | let t = (planeNormal.dot(planePoint) - planeNormal.dot(linePoint)) / planeNormal.dot(lineDirection); 148 | if (t > lineLength) return false; 149 | if (t < 0) return false; 150 | 151 | let px = linePoint.x + lineDirection.x * t; 152 | let py = linePoint.y + lineDirection.y * t; 153 | let pz = linePoint.z + lineDirection.z * t; 154 | 155 | let planeSize = Infinity; 156 | if(vec3(planePoint.x - px, planePoint.y - py, planePoint.z - pz).length() > planeSize) return false; 157 | 158 | return vec3(px, py, pz); 159 | } -------------------------------------------------------------------------------- /libs/scenes/codrops-article/v4.js: -------------------------------------------------------------------------------- 1 | function setGlobals() { 2 | pointsPerFrame = 50000; 3 | 4 | cameraPosition = new THREE.Vector3(0, 0, 115); 5 | cameraFocalDistance = 100; 6 | 7 | minimumLineSize = 0.005; 8 | 9 | bokehStrength = 0.02; 10 | focalPowerFunction = 1; 11 | exposure = 0.009; 12 | distanceAttenuation = 0.002; 13 | 14 | useBokehTexture = true; 15 | bokehTexturePath = "assets/bokeh/pentagon2.png"; 16 | 17 | backgroundColor[0] *= 0.8; 18 | backgroundColor[1] *= 0.8; 19 | backgroundColor[2] *= 0.8; 20 | } 21 | 22 | let rand, nrand; 23 | let vec3 = function(x,y,z) { return new THREE.Vector3(x,y,z) }; 24 | 25 | function createScene() { 26 | Utils.setRandomSeed("3926153465010"); 27 | 28 | rand = function() { return Utils.rand(); }; 29 | nrand = function() { return rand() * 2 - 1; }; 30 | 31 | computeWeb(); 32 | computeSparkles(); 33 | } 34 | 35 | function computeWeb() { 36 | let r1 = 35; 37 | let r2 = 17; 38 | for(let j = 0; j < r2; j++) { 39 | for(let i = 0; i < r1; i++) { 40 | let phi1 = (j + i * 0.075) / r2 * Math.PI * 2; 41 | let theta1 = i / r1 * Math.PI - Math.PI * 0.5; 42 | 43 | let phi2 = (j + (i+1) * 0.075) / r2 * Math.PI * 2; 44 | let theta2 = (i+1) / r1 * Math.PI - Math.PI * 0.5; 45 | 46 | 47 | let x1 = Math.sin(phi1) * Math.cos(theta1); 48 | let y1 = Math.sin(theta1); 49 | let z1 = Math.cos(phi1) * Math.cos(theta1); 50 | 51 | let x2 = Math.sin(phi2) * Math.cos(theta2); 52 | let y2 = Math.sin(theta2); 53 | let z2 = Math.cos(phi2) * Math.cos(theta2); 54 | 55 | 56 | lines.push( 57 | new Line({ 58 | v1: vec3(x1,z1,y1).multiplyScalar(15), 59 | v2: vec3(x2,z2,y2).multiplyScalar(15), 60 | c1: vec3(5,5,5), 61 | c2: vec3(5,5,5), 62 | }) 63 | ); 64 | } 65 | } 66 | 67 | // intersect many 3d planes against all the lines we made so far 68 | for(let i = 0; i < 4500; i++) { 69 | let x0 = nrand() * 15; 70 | let y0 = nrand() * 15; 71 | let z0 = nrand() * 15; 72 | 73 | let dir = vec3(nrand(), nrand(), nrand()).normalize(); 74 | findIntersectingEdges(vec3(x0, y0, z0), dir); 75 | } 76 | } 77 | 78 | function computeSparkles() { 79 | for(let i = 0; i < 5500; i++) { 80 | let v0 = vec3(nrand(), nrand(), nrand()).normalize().multiplyScalar(18 + rand() * 65); 81 | 82 | let c = 1.325 * (0.3 + rand() * 0.7); 83 | let s = 0.125; 84 | 85 | if(rand() > 0.9) { 86 | c *= 4; 87 | } 88 | 89 | lines.push(new Line({ 90 | v1: vec3(v0.x - s, v0.y, v0.z), 91 | v2: vec3(v0.x + s, v0.y, v0.z), 92 | 93 | c1: vec3(c, c, c), 94 | c2: vec3(c, c, c), 95 | })); 96 | 97 | lines.push(new Line({ 98 | v1: vec3(v0.x, v0.y - s, v0.z), 99 | v2: vec3(v0.x, v0.y + s, v0.z), 100 | 101 | c1: vec3(c, c, c), 102 | c2: vec3(c, c, c), 103 | })); 104 | } 105 | } 106 | 107 | 108 | function findIntersectingEdges(center, dir) { 109 | 110 | let contactPoints = []; 111 | for(line of lines) { 112 | let ires = intersectsPlane( 113 | center, dir, 114 | line.v1, line.v2 115 | ); 116 | 117 | if(ires === false) continue; 118 | 119 | contactPoints.push(ires); 120 | } 121 | 122 | if(contactPoints.length < 2) return; 123 | 124 | let randCpIndex = Math.floor(rand() * contactPoints.length); 125 | let randCp = contactPoints[randCpIndex]; 126 | 127 | // lets search the closest contact point from randCp 128 | let minl = Infinity; 129 | let minI = -1; 130 | for(let i = 0; i < contactPoints.length; i++) { 131 | 132 | if(i === randCpIndex) continue; 133 | 134 | let cp2 = contactPoints[i]; 135 | 136 | // 3d point in space of randCp 137 | let v1 = vec3(randCp.x, randCp.y, randCp.z); 138 | // 3d point in space of the contact point we're testing for proximity 139 | let v2 = vec3(cp2.x, cp2.y, cp2.z); 140 | 141 | let sv = vec3(v2.x - v1.x, v2.y - v1.y, v2.z - v1.z); 142 | // "l" holds the euclidean distance between the two contact points 143 | let l = sv.length(); 144 | 145 | // if "l" is smaller than the minimum distance we've registered so far, store this contact point's index as minI 146 | if(l < minl) { 147 | minl = l; 148 | minI = i; 149 | } 150 | } 151 | 152 | let cp1 = contactPoints[randCpIndex]; 153 | let cp2 = contactPoints[minI]; 154 | 155 | lines.push( 156 | new Line({ 157 | v1: vec3(cp1.x, cp1.y, cp1.z), 158 | v2: vec3(cp2.x, cp2.y, cp2.z), 159 | c1: vec3(2,2,2), 160 | c2: vec3(2,2,2), 161 | }) 162 | ); 163 | } 164 | 165 | function intersectsPlane(planePoint, planeNormal, linePoint, linePoint2) { 166 | 167 | let lineDirection = new THREE.Vector3(linePoint2.x - linePoint.x, linePoint2.y - linePoint.y, linePoint2.z - linePoint.z); 168 | let lineLength = lineDirection.length(); 169 | lineDirection.normalize(); 170 | 171 | if (planeNormal.dot(lineDirection) === 0) { 172 | return false; 173 | } 174 | 175 | let t = (planeNormal.dot(planePoint) - planeNormal.dot(linePoint)) / planeNormal.dot(lineDirection); 176 | if (t > lineLength) return false; 177 | if (t < 0) return false; 178 | 179 | let px = linePoint.x + lineDirection.x * t; 180 | let py = linePoint.y + lineDirection.y * t; 181 | let pz = linePoint.z + lineDirection.z * t; 182 | 183 | let planeSize = Infinity; 184 | if(vec3(planePoint.x - px, planePoint.y - py, planePoint.z - pz).length() > planeSize) return false; 185 | 186 | return vec3(px, py, pz); 187 | } -------------------------------------------------------------------------------- /libs/scenes/trees.js: -------------------------------------------------------------------------------- 1 | function setGlobals() { 2 | cameraPosition = new THREE.Vector3(0, 20, 70); 3 | cameraTarget = new THREE.Vector3(0, 0, 0); 4 | cameraFocalDistance = 58.4; 5 | 6 | bokehStrength = 0.01; 7 | } 8 | 9 | // use this function to create the lines that will be rendered 10 | function createScene(frame) { 11 | 12 | Utils.setRandomSeed("cauliflower1"); 13 | 14 | 15 | function createTree(sv, relRot, level) { 16 | if(level >= 6) return; 17 | 18 | let v1 = sv.clone(); 19 | let v2 = new THREE.Vector3(0, (1 + ((6 - level) * 0.5)) * (Utils.rand() + 0.15), 0); 20 | v2.applyAxisAngle(new THREE.Vector3(0, 0, 1), relRot + (Utils.rand() * 2 - 1) * 0.25); 21 | v2.applyAxisAngle(new THREE.Vector3(1, 0, 0), (Utils.rand() * 2 - 1) * 0.25); 22 | v2.add(v1); 23 | 24 | let colorMult = 1; 25 | 26 | lines.push({ 27 | x1: v1.x, 28 | y1: v1.y, 29 | z1: v1.z, 30 | 31 | x2: v2.x, 32 | y2: v2.y, 33 | z2: v2.z, 34 | 35 | c1r: 1 * colorMult, 36 | c1g: 1 * colorMult, 37 | c1b: 1 * colorMult, 38 | 39 | c2r: 1 * colorMult, 40 | c2g: 1 * colorMult, 41 | c2b: 1 * colorMult, 42 | }); 43 | 44 | 45 | if(Utils.rand() > 0.3) 46 | createTree(v2, relRot - 0.5, level + 1); 47 | 48 | if(Utils.rand() > 0.85) 49 | createTree(v2, relRot + 0.0, level + 1); 50 | 51 | if(Utils.rand() > 0.3) 52 | createTree(v2, relRot + 0.5, level + 1); 53 | 54 | 55 | 56 | if(level === 5 && (Math.abs(relRot) > 0.5) && Utils.rand() > 0.5) { 57 | createFallingText(v2); 58 | } 59 | } 60 | 61 | 62 | 63 | for(let i = 0; i < 40; i++) { 64 | 65 | let x = (Utils.rand() * 2 - 1) * 30; 66 | let z = -(Utils.rand()) * 90 + 30; 67 | 68 | let pos = new THREE.Vector3(x, 0, z); 69 | pos.x += (Utils.rand() * 2 - 1) * 2; 70 | pos.z += (Utils.rand() * 2 - 1) * 2; 71 | 72 | createTree(pos, 0, 1); 73 | } 74 | 75 | 76 | 77 | function createFallingText(start) { 78 | 79 | let chars = Math.floor(4 + Utils.rand() * 5); 80 | 81 | let a1Start = Utils.rand() * Math.PI * 2; 82 | 83 | for(let i = 0; i < chars; i++) { 84 | 85 | let index = 0; 86 | 87 | if(i === 0) 88 | index = Math.floor(33 + Utils.rand() * 25); 89 | else 90 | index = Math.floor(65 + Utils.rand() * 22); 91 | 92 | // 16 * 16 because uvs starts from the bottom 93 | let t = indexToUvs(16 * 16 - index); 94 | 95 | 96 | let maxRot = 0.1 + Utils.rand() * 0.3; 97 | let maxRotSpeed = 0.5 + Utils.rand() * 0.9; 98 | let a1 = Math.sin(a1Start + i * maxRotSpeed) * maxRot; 99 | 100 | let color = { 101 | x: 100, 102 | y: 100, 103 | z: 100, 104 | }; 105 | if(Utils.rand() > 0.85) { 106 | color.x = 10; 107 | color.y = 30; 108 | color.z = 100; 109 | } 110 | 111 | let quad = new Quad(0,0,0, t.us, t.vs, t.ue, t.ve); 112 | 113 | quad = quad 114 | .scale(0.5) 115 | .translate(0, 1, 0) 116 | .rotate(0, 0, 1, a1) 117 | .translate(-(quad.v1.x + quad.v2.x) / 2, 0, 0) 118 | .color(color.x, color.y, color.z) 119 | .translate(start.x, start.y - 0.5 - i * 0.35, start.z) 120 | .translate(0, -1, 0) 121 | 122 | quads.push(quad); 123 | } 124 | } 125 | 126 | 127 | 128 | 129 | // *************** grass 130 | for(let i = -80; i < 80; i++) { 131 | let v1 = new THREE.Vector3( -100, 0, i * 0.375 ); 132 | let v2 = new THREE.Vector3( 100, 0, i * 0.375 ); 133 | 134 | let color = 1; 135 | 136 | lines.push({ 137 | x1: v1.x, 138 | y1: v1.y, 139 | z1: v1.z, 140 | 141 | x2: v2.x, 142 | y2: v2.y, 143 | z2: v2.z, 144 | 145 | c1r: color, 146 | c1g: color, 147 | c1b: color, 148 | 149 | c2r: color, 150 | c2g: color, 151 | c2b: color, 152 | 153 | weight: 0.25, 154 | }); 155 | } 156 | 157 | 158 | 159 | for(let i = 0; i < 85000; i++) { 160 | let v1 = new THREE.Vector3( 161 | (Utils.rand() * 2 - 1) * 45, 162 | 0, // (Utils.rand() * 2 - 1) * 30, 163 | Math.floor((Utils.rand() * 2 - 1) * 135) * 0.375, 164 | ); 165 | 166 | let v2 = new THREE.Vector3(0, 0.1 + Utils.rand() * 1.15, 0); 167 | let ax = 1; 168 | let az = 0; 169 | if(Utils.rand() > 0.5) { 170 | ax = 0; 171 | az = 1; 172 | } 173 | v2.applyAxisAngle(new THREE.Vector3(ax, 0, az), (Utils.rand() * 2 - 1) * 0.25); 174 | v2.add(v1); 175 | 176 | let colorMult = 0.05 * Math.pow(Utils.rand(), 1.25) * 0.95; 177 | 178 | lines.push({ 179 | x1: v1.x, 180 | y1: v1.y, 181 | z1: v1.z, 182 | 183 | x2: v2.x, 184 | y2: v2.y, 185 | z2: v2.z, 186 | 187 | c1r: 1 * colorMult, 188 | c1g: 1 * colorMult, 189 | c1b: 1 * colorMult, 190 | 191 | c2r: 1 * colorMult, 192 | c2g: 1 * colorMult, 193 | c2b: 1 * colorMult, 194 | }); 195 | } 196 | 197 | // *************** grass - END 198 | 199 | } 200 | 201 | 202 | function indexToUvs(index) { 203 | let charsPerRow = 16; 204 | 205 | let x = index % charsPerRow; 206 | let y = Math.floor(index / charsPerRow); 207 | 208 | let us = x / charsPerRow; 209 | let vs = y / charsPerRow; 210 | 211 | let ue = us + 1 / charsPerRow; 212 | let ve = vs + 1 / charsPerRow; 213 | 214 | return { 215 | us: us, 216 | vs: vs, 217 | 218 | ue: ue, 219 | ve: ve, 220 | } 221 | } -------------------------------------------------------------------------------- /libs/createScene.js: -------------------------------------------------------------------------------- 1 | function setGlobals() { 2 | pointsPerFrame = 50000; 3 | 4 | cameraPosition = new THREE.Vector3(0, 0, 115); 5 | cameraFocalDistance = 100; 6 | 7 | minimumLineSize = 0.005; 8 | 9 | bokehStrength = 0.02; 10 | focalPowerFunction = 1; 11 | exposure = 0.009; 12 | distanceAttenuation = 0.002; 13 | 14 | useBokehTexture = true; 15 | bokehTexturePath = "assets/bokeh/pentagon2.png"; 16 | 17 | backgroundColor[0] *= 0.8; 18 | backgroundColor[1] *= 0.8; 19 | backgroundColor[2] *= 0.8; 20 | } 21 | 22 | let rand, nrand; 23 | let vec3 = function(x,y,z) { return new THREE.Vector3(x,y,z) }; 24 | let lightDir0 = vec3(1, 1, 0.2).normalize(); 25 | let lightDir1 = vec3(-1, 1, 0.2).normalize(); 26 | 27 | function createScene() { 28 | Utils.setRandomSeed("3926153465010"); 29 | 30 | rand = function() { return Utils.rand(); }; 31 | nrand = function() { return rand() * 2 - 1; }; 32 | 33 | computeWeb(); 34 | computeSparkles(); 35 | } 36 | 37 | function computeWeb() { 38 | let r1 = 35; 39 | let r2 = 17; 40 | for(let j = 0; j < r2; j++) { 41 | for(let i = 0; i < r1; i++) { 42 | let phi1 = (j + i * 0.075) / r2 * Math.PI * 2; 43 | let theta1 = i / r1 * Math.PI - Math.PI * 0.5; 44 | 45 | let phi2 = (j + (i+1) * 0.075) / r2 * Math.PI * 2; 46 | let theta2 = (i+1) / r1 * Math.PI - Math.PI * 0.5; 47 | 48 | 49 | let x1 = Math.sin(phi1) * Math.cos(theta1); 50 | let y1 = Math.sin(theta1); 51 | let z1 = Math.cos(phi1) * Math.cos(theta1); 52 | 53 | let x2 = Math.sin(phi2) * Math.cos(theta2); 54 | let y2 = Math.sin(theta2); 55 | let z2 = Math.cos(phi2) * Math.cos(theta2); 56 | 57 | 58 | lines.push( 59 | new Line({ 60 | v1: vec3(x1,z1,y1).multiplyScalar(15), 61 | v2: vec3(x2,z2,y2).multiplyScalar(15), 62 | c1: vec3(5,5,5), 63 | c2: vec3(5,5,5), 64 | }) 65 | ); 66 | } 67 | } 68 | 69 | // intersect many 3d planes against all the lines we made so far 70 | for(let i = 0; i < 4500; i++) { 71 | let x0 = nrand() * 15; 72 | let y0 = nrand() * 15; 73 | let z0 = nrand() * 15; 74 | 75 | let dir = vec3(nrand(), nrand(), nrand()).normalize(); 76 | findIntersectingEdges(vec3(x0, y0, z0), dir); 77 | } 78 | 79 | // recolor edges 80 | for(line of lines) { 81 | let v1 = line.v1; 82 | 83 | // these will be used as the "red vectors" of the previous example 84 | let normal1 = v1.clone().normalize(); 85 | 86 | // lets calculate how much light the two endpoints of the line 87 | // will get from the "lightDir0" light source (the white light) 88 | // we need Math.max( ... , 0.1) to make sure the dot product doesn't get lower than 89 | // 0.1, this will ensure each point is at least partially lit by a light source and 90 | // doesn't end up being completely black 91 | let diffuse0 = Math.max(lightDir0.dot(normal1) * 3, 0.15); 92 | let diffuse1 = Math.max(lightDir1.dot(normal1) * 2, 0.2 ); 93 | 94 | let firstColor = [diffuse0, diffuse0, diffuse0]; 95 | let secondColor = [2 * diffuse1, 0.2 * diffuse1, 0]; 96 | 97 | let r1 = firstColor[0] + secondColor[0]; 98 | let g1 = firstColor[1] + secondColor[1]; 99 | let b1 = firstColor[2] + secondColor[2]; 100 | 101 | let r2 = firstColor[0] + secondColor[0]; 102 | let g2 = firstColor[1] + secondColor[1]; 103 | let b2 = firstColor[2] + secondColor[2]; 104 | 105 | line.c1 = vec3(r1, g1, b1); 106 | line.c2 = vec3(r2, g2, b2); 107 | } 108 | } 109 | 110 | function computeSparkles() { 111 | for(let i = 0; i < 5500; i++) { 112 | let v0 = vec3(nrand(), nrand(), nrand()).normalize().multiplyScalar(18 + rand() * 65); 113 | 114 | let c = 1.325 * (0.3 + rand() * 0.7); 115 | let s = 0.125; 116 | 117 | if(rand() > 0.9) { 118 | c *= 4; 119 | } 120 | 121 | let normal1 = v0.clone().normalize(); 122 | 123 | let diffuse0 = Math.max(lightDir0.dot(normal1) * 3, 0.15); 124 | let diffuse1 = Math.max(lightDir1.dot(normal1) * 2, 0.2 ); 125 | 126 | let r = diffuse0 + 2 * diffuse1; 127 | let g = diffuse0 + 0.2 * diffuse1; 128 | let b = diffuse0; 129 | 130 | lines.push(new Line({ 131 | v1: vec3(v0.x - s, v0.y, v0.z), 132 | v2: vec3(v0.x + s, v0.y, v0.z), 133 | 134 | c1: vec3(r * c, g * c, b * c), 135 | c2: vec3(r * c, g * c, b * c), 136 | })); 137 | 138 | lines.push(new Line({ 139 | v1: vec3(v0.x, v0.y - s, v0.z), 140 | v2: vec3(v0.x, v0.y + s, v0.z), 141 | 142 | c1: vec3(r * c, g * c, b * c), 143 | c2: vec3(r * c, g * c, b * c), 144 | })); 145 | } 146 | } 147 | 148 | 149 | function findIntersectingEdges(center, dir) { 150 | 151 | let contactPoints = []; 152 | for(line of lines) { 153 | let ires = intersectsPlane( 154 | center, dir, 155 | line.v1, line.v2 156 | ); 157 | 158 | if(ires === false) continue; 159 | 160 | contactPoints.push(ires); 161 | } 162 | 163 | if(contactPoints.length < 2) return; 164 | 165 | let randCpIndex = Math.floor(rand() * contactPoints.length); 166 | let randCp = contactPoints[randCpIndex]; 167 | 168 | // lets search the closest contact point from randCp 169 | let minl = Infinity; 170 | let minI = -1; 171 | for(let i = 0; i < contactPoints.length; i++) { 172 | 173 | if(i === randCpIndex) continue; 174 | 175 | let cp2 = contactPoints[i]; 176 | 177 | // 3d point in space of randCp 178 | let v1 = vec3(randCp.x, randCp.y, randCp.z); 179 | // 3d point in space of the contact point we're testing for proximity 180 | let v2 = vec3(cp2.x, cp2.y, cp2.z); 181 | 182 | let sv = vec3(v2.x - v1.x, v2.y - v1.y, v2.z - v1.z); 183 | // "l" holds the euclidean distance between the two contact points 184 | let l = sv.length(); 185 | 186 | // if "l" is smaller than the minimum distance we've registered so far, store this contact point's index as minI 187 | if(l < minl) { 188 | minl = l; 189 | minI = i; 190 | } 191 | } 192 | 193 | let cp1 = contactPoints[randCpIndex]; 194 | let cp2 = contactPoints[minI]; 195 | 196 | lines.push( 197 | new Line({ 198 | v1: vec3(cp1.x, cp1.y, cp1.z), 199 | v2: vec3(cp2.x, cp2.y, cp2.z), 200 | c1: vec3(2,2,2), 201 | c2: vec3(2,2,2), 202 | }) 203 | ); 204 | } 205 | 206 | function intersectsPlane(planePoint, planeNormal, linePoint, linePoint2) { 207 | 208 | let lineDirection = new THREE.Vector3(linePoint2.x - linePoint.x, linePoint2.y - linePoint.y, linePoint2.z - linePoint.z); 209 | let lineLength = lineDirection.length(); 210 | lineDirection.normalize(); 211 | 212 | if (planeNormal.dot(lineDirection) === 0) { 213 | return false; 214 | } 215 | 216 | let t = (planeNormal.dot(planePoint) - planeNormal.dot(linePoint)) / planeNormal.dot(lineDirection); 217 | if (t > lineLength) return false; 218 | if (t < 0) return false; 219 | 220 | let px = linePoint.x + lineDirection.x * t; 221 | let py = linePoint.y + lineDirection.y * t; 222 | let pz = linePoint.z + lineDirection.z * t; 223 | 224 | let planeSize = Infinity; 225 | if(vec3(planePoint.x - px, planePoint.y - py, planePoint.z - pz).length() > planeSize) return false; 226 | 227 | return vec3(px, py, pz); 228 | } -------------------------------------------------------------------------------- /libs/scenes/codrops-article/v5.js: -------------------------------------------------------------------------------- 1 | function setGlobals() { 2 | pointsPerFrame = 50000; 3 | 4 | cameraPosition = new THREE.Vector3(0, 0, 115); 5 | cameraFocalDistance = 100; 6 | 7 | minimumLineSize = 0.005; 8 | 9 | bokehStrength = 0.02; 10 | focalPowerFunction = 1; 11 | exposure = 0.009; 12 | distanceAttenuation = 0.002; 13 | 14 | useBokehTexture = true; 15 | bokehTexturePath = "assets/bokeh/pentagon2.png"; 16 | 17 | backgroundColor[0] *= 0.8; 18 | backgroundColor[1] *= 0.8; 19 | backgroundColor[2] *= 0.8; 20 | } 21 | 22 | let rand, nrand; 23 | let vec3 = function(x,y,z) { return new THREE.Vector3(x,y,z) }; 24 | let lightDir0 = vec3(1, 1, 0.2).normalize(); 25 | let lightDir1 = vec3(-1, 1, 0.2).normalize(); 26 | 27 | function createScene() { 28 | Utils.setRandomSeed("3926153465010"); 29 | 30 | rand = function() { return Utils.rand(); }; 31 | nrand = function() { return rand() * 2 - 1; }; 32 | 33 | computeWeb(); 34 | computeSparkles(); 35 | } 36 | 37 | function computeWeb() { 38 | let r1 = 35; 39 | let r2 = 17; 40 | for(let j = 0; j < r2; j++) { 41 | for(let i = 0; i < r1; i++) { 42 | let phi1 = (j + i * 0.075) / r2 * Math.PI * 2; 43 | let theta1 = i / r1 * Math.PI - Math.PI * 0.5; 44 | 45 | let phi2 = (j + (i+1) * 0.075) / r2 * Math.PI * 2; 46 | let theta2 = (i+1) / r1 * Math.PI - Math.PI * 0.5; 47 | 48 | 49 | let x1 = Math.sin(phi1) * Math.cos(theta1); 50 | let y1 = Math.sin(theta1); 51 | let z1 = Math.cos(phi1) * Math.cos(theta1); 52 | 53 | let x2 = Math.sin(phi2) * Math.cos(theta2); 54 | let y2 = Math.sin(theta2); 55 | let z2 = Math.cos(phi2) * Math.cos(theta2); 56 | 57 | 58 | lines.push( 59 | new Line({ 60 | v1: vec3(x1,z1,y1).multiplyScalar(15), 61 | v2: vec3(x2,z2,y2).multiplyScalar(15), 62 | c1: vec3(5,5,5), 63 | c2: vec3(5,5,5), 64 | }) 65 | ); 66 | } 67 | } 68 | 69 | // intersect many 3d planes against all the lines we made so far 70 | for(let i = 0; i < 4500; i++) { 71 | let x0 = nrand() * 15; 72 | let y0 = nrand() * 15; 73 | let z0 = nrand() * 15; 74 | 75 | let dir = vec3(nrand(), nrand(), nrand()).normalize(); 76 | findIntersectingEdges(vec3(x0, y0, z0), dir); 77 | } 78 | 79 | // recolor edges 80 | for(line of lines) { 81 | let v1 = line.v1; 82 | 83 | // these will be used as the "red vectors" of the previous example 84 | let normal1 = v1.clone().normalize(); 85 | 86 | // lets calculate how much light the two endpoints of the line 87 | // will get from the "lightDir0" light source (the white light) 88 | // we need Math.max( ... , 0.1) to make sure the dot product doesn't get lower than 89 | // 0.1, this will ensure each point is at least partially lit by a light source and 90 | // doesn't end up being completely black 91 | let diffuse0 = Math.max(lightDir0.dot(normal1) * 3, 0.15); 92 | let diffuse1 = Math.max(lightDir1.dot(normal1) * 2, 0.2 ); 93 | 94 | let firstColor = [diffuse0, diffuse0, diffuse0]; 95 | let secondColor = [2 * diffuse1, 0.2 * diffuse1, 0]; 96 | 97 | let r1 = firstColor[0] + secondColor[0]; 98 | let g1 = firstColor[1] + secondColor[1]; 99 | let b1 = firstColor[2] + secondColor[2]; 100 | 101 | let r2 = firstColor[0] + secondColor[0]; 102 | let g2 = firstColor[1] + secondColor[1]; 103 | let b2 = firstColor[2] + secondColor[2]; 104 | 105 | line.c1 = vec3(r1, g1, b1); 106 | line.c2 = vec3(r2, g2, b2); 107 | } 108 | } 109 | 110 | function computeSparkles() { 111 | for(let i = 0; i < 5500; i++) { 112 | let v0 = vec3(nrand(), nrand(), nrand()).normalize().multiplyScalar(18 + rand() * 65); 113 | 114 | let c = 1.325 * (0.3 + rand() * 0.7); 115 | let s = 0.125; 116 | 117 | if(rand() > 0.9) { 118 | c *= 4; 119 | } 120 | 121 | let normal1 = v0.clone().normalize(); 122 | 123 | let diffuse0 = Math.max(lightDir0.dot(normal1) * 3, 0.15); 124 | let diffuse1 = Math.max(lightDir1.dot(normal1) * 2, 0.2 ); 125 | 126 | let r = diffuse0 + 2 * diffuse1; 127 | let g = diffuse0 + 0.2 * diffuse1; 128 | let b = diffuse0; 129 | 130 | lines.push(new Line({ 131 | v1: vec3(v0.x - s, v0.y, v0.z), 132 | v2: vec3(v0.x + s, v0.y, v0.z), 133 | 134 | c1: vec3(r * c, g * c, b * c), 135 | c2: vec3(r * c, g * c, b * c), 136 | })); 137 | 138 | lines.push(new Line({ 139 | v1: vec3(v0.x, v0.y - s, v0.z), 140 | v2: vec3(v0.x, v0.y + s, v0.z), 141 | 142 | c1: vec3(r * c, g * c, b * c), 143 | c2: vec3(r * c, g * c, b * c), 144 | })); 145 | } 146 | } 147 | 148 | 149 | function findIntersectingEdges(center, dir) { 150 | 151 | let contactPoints = []; 152 | for(line of lines) { 153 | let ires = intersectsPlane( 154 | center, dir, 155 | line.v1, line.v2 156 | ); 157 | 158 | if(ires === false) continue; 159 | 160 | contactPoints.push(ires); 161 | } 162 | 163 | if(contactPoints.length < 2) return; 164 | 165 | let randCpIndex = Math.floor(rand() * contactPoints.length); 166 | let randCp = contactPoints[randCpIndex]; 167 | 168 | // lets search the closest contact point from randCp 169 | let minl = Infinity; 170 | let minI = -1; 171 | for(let i = 0; i < contactPoints.length; i++) { 172 | 173 | if(i === randCpIndex) continue; 174 | 175 | let cp2 = contactPoints[i]; 176 | 177 | // 3d point in space of randCp 178 | let v1 = vec3(randCp.x, randCp.y, randCp.z); 179 | // 3d point in space of the contact point we're testing for proximity 180 | let v2 = vec3(cp2.x, cp2.y, cp2.z); 181 | 182 | let sv = vec3(v2.x - v1.x, v2.y - v1.y, v2.z - v1.z); 183 | // "l" holds the euclidean distance between the two contact points 184 | let l = sv.length(); 185 | 186 | // if "l" is smaller than the minimum distance we've registered so far, store this contact point's index as minI 187 | if(l < minl) { 188 | minl = l; 189 | minI = i; 190 | } 191 | } 192 | 193 | let cp1 = contactPoints[randCpIndex]; 194 | let cp2 = contactPoints[minI]; 195 | 196 | lines.push( 197 | new Line({ 198 | v1: vec3(cp1.x, cp1.y, cp1.z), 199 | v2: vec3(cp2.x, cp2.y, cp2.z), 200 | c1: vec3(2,2,2), 201 | c2: vec3(2,2,2), 202 | }) 203 | ); 204 | } 205 | 206 | function intersectsPlane(planePoint, planeNormal, linePoint, linePoint2) { 207 | 208 | let lineDirection = new THREE.Vector3(linePoint2.x - linePoint.x, linePoint2.y - linePoint.y, linePoint2.z - linePoint.z); 209 | let lineLength = lineDirection.length(); 210 | lineDirection.normalize(); 211 | 212 | if (planeNormal.dot(lineDirection) === 0) { 213 | return false; 214 | } 215 | 216 | let t = (planeNormal.dot(planePoint) - planeNormal.dot(linePoint)) / planeNormal.dot(lineDirection); 217 | if (t > lineLength) return false; 218 | if (t < 0) return false; 219 | 220 | let px = linePoint.x + lineDirection.x * t; 221 | let py = linePoint.y + lineDirection.y * t; 222 | let pz = linePoint.z + lineDirection.z * t; 223 | 224 | let planeSize = Infinity; 225 | if(vec3(planePoint.x - px, planePoint.y - py, planePoint.z - pz).length() > planeSize) return false; 226 | 227 | return vec3(px, py, pz); 228 | } -------------------------------------------------------------------------------- /libs/perlinNoise.js: -------------------------------------------------------------------------------- 1 | /* 2 | * A speed-improved perlin and simplex noise algorithms for 2D. 3 | * 4 | * Based on example code by Stefan Gustavson (stegu@itn.liu.se). 5 | * Optimisations by Peter Eastman (peastman@drizzle.stanford.edu). 6 | * Better rank ordering method by Stefan Gustavson in 2012. 7 | * Converted to Javascript by Joseph Gentle. 8 | * 9 | * Version 2012-03-09 10 | * 11 | * This code was placed in the public domain by its original author, 12 | * Stefan Gustavson. You may use it as you see fit, but 13 | * attribution is appreciated. 14 | * 15 | */ 16 | 17 | (function(global){ 18 | var module = global.noise = {}; 19 | 20 | function Grad(x, y, z) { 21 | this.x = x; this.y = y; this.z = z; 22 | } 23 | 24 | Grad.prototype.dot2 = function(x, y) { 25 | return this.x*x + this.y*y; 26 | }; 27 | 28 | Grad.prototype.dot3 = function(x, y, z) { 29 | return this.x*x + this.y*y + this.z*z; 30 | }; 31 | 32 | var grad3 = [new Grad(1,1,0),new Grad(-1,1,0),new Grad(1,-1,0),new Grad(-1,-1,0), 33 | new Grad(1,0,1),new Grad(-1,0,1),new Grad(1,0,-1),new Grad(-1,0,-1), 34 | new Grad(0,1,1),new Grad(0,-1,1),new Grad(0,1,-1),new Grad(0,-1,-1)]; 35 | 36 | var p = [151,160,137,91,90,15, 37 | 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 38 | 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 39 | 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 40 | 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 41 | 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 42 | 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 43 | 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 44 | 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 45 | 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 46 | 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 47 | 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 48 | 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180]; 49 | // To remove the need for index wrapping, double the permutation table length 50 | var perm = new Array(512); 51 | var gradP = new Array(512); 52 | 53 | // This isn't a very good seeding function, but it works ok. It supports 2^16 54 | // different seed values. Write something better if you need more seeds. 55 | module.seed = function(seed) { 56 | if(seed > 0 && seed < 1) { 57 | // Scale the seed out 58 | seed *= 65536; 59 | } 60 | 61 | seed = Math.floor(seed); 62 | if(seed < 256) { 63 | seed |= seed << 8; 64 | } 65 | 66 | for(var i = 0; i < 256; i++) { 67 | var v; 68 | if (i & 1) { 69 | v = p[i] ^ (seed & 255); 70 | } else { 71 | v = p[i] ^ ((seed>>8) & 255); 72 | } 73 | 74 | perm[i] = perm[i + 256] = v; 75 | gradP[i] = gradP[i + 256] = grad3[v % 12]; 76 | } 77 | }; 78 | 79 | module.seed(0); 80 | 81 | /* 82 | for(var i=0; i<256; i++) { 83 | perm[i] = perm[i + 256] = p[i]; 84 | gradP[i] = gradP[i + 256] = grad3[perm[i] % 12]; 85 | }*/ 86 | 87 | // Skewing and unskewing factors for 2, 3, and 4 dimensions 88 | var F2 = 0.5*(Math.sqrt(3)-1); 89 | var G2 = (3-Math.sqrt(3))/6; 90 | 91 | var F3 = 1/3; 92 | var G3 = 1/6; 93 | 94 | // 2D simplex noise 95 | module.simplex2 = function(xin, yin) { 96 | var n0, n1, n2; // Noise contributions from the three corners 97 | // Skew the input space to determine which simplex cell we're in 98 | var s = (xin+yin)*F2; // Hairy factor for 2D 99 | var i = Math.floor(xin+s); 100 | var j = Math.floor(yin+s); 101 | var t = (i+j)*G2; 102 | var x0 = xin-i+t; // The x,y distances from the cell origin, unskewed. 103 | var y0 = yin-j+t; 104 | // For the 2D case, the simplex shape is an equilateral triangle. 105 | // Determine which simplex we are in. 106 | var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords 107 | if(x0>y0) { // lower triangle, XY order: (0,0)->(1,0)->(1,1) 108 | i1=1; j1=0; 109 | } else { // upper triangle, YX order: (0,0)->(0,1)->(1,1) 110 | i1=0; j1=1; 111 | } 112 | // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and 113 | // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where 114 | // c = (3-sqrt(3))/6 115 | var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords 116 | var y1 = y0 - j1 + G2; 117 | var x2 = x0 - 1 + 2 * G2; // Offsets for last corner in (x,y) unskewed coords 118 | var y2 = y0 - 1 + 2 * G2; 119 | // Work out the hashed gradient indices of the three simplex corners 120 | i &= 255; 121 | j &= 255; 122 | var gi0 = gradP[i+perm[j]]; 123 | var gi1 = gradP[i+i1+perm[j+j1]]; 124 | var gi2 = gradP[i+1+perm[j+1]]; 125 | // Calculate the contribution from the three corners 126 | var t0 = 0.5 - x0*x0-y0*y0; 127 | if(t0<0) { 128 | n0 = 0; 129 | } else { 130 | t0 *= t0; 131 | n0 = t0 * t0 * gi0.dot2(x0, y0); // (x,y) of grad3 used for 2D gradient 132 | } 133 | var t1 = 0.5 - x1*x1-y1*y1; 134 | if(t1<0) { 135 | n1 = 0; 136 | } else { 137 | t1 *= t1; 138 | n1 = t1 * t1 * gi1.dot2(x1, y1); 139 | } 140 | var t2 = 0.5 - x2*x2-y2*y2; 141 | if(t2<0) { 142 | n2 = 0; 143 | } else { 144 | t2 *= t2; 145 | n2 = t2 * t2 * gi2.dot2(x2, y2); 146 | } 147 | // Add contributions from each corner to get the final noise value. 148 | // The result is scaled to return values in the interval [-1,1]. 149 | return 70 * (n0 + n1 + n2); 150 | }; 151 | 152 | // 3D simplex noise 153 | module.simplex3 = function(xin, yin, zin) { 154 | var n0, n1, n2, n3; // Noise contributions from the four corners 155 | 156 | // Skew the input space to determine which simplex cell we're in 157 | var s = (xin+yin+zin)*F3; // Hairy factor for 2D 158 | var i = Math.floor(xin+s); 159 | var j = Math.floor(yin+s); 160 | var k = Math.floor(zin+s); 161 | 162 | var t = (i+j+k)*G3; 163 | var x0 = xin-i+t; // The x,y distances from the cell origin, unskewed. 164 | var y0 = yin-j+t; 165 | var z0 = zin-k+t; 166 | 167 | // For the 3D case, the simplex shape is a slightly irregular tetrahedron. 168 | // Determine which simplex we are in. 169 | var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords 170 | var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords 171 | if(x0 >= y0) { 172 | if(y0 >= z0) { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } 173 | else if(x0 >= z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } 174 | else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } 175 | } else { 176 | if(y0 < z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } 177 | else if(x0 < z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } 178 | else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } 179 | } 180 | // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z), 181 | // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and 182 | // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where 183 | // c = 1/6. 184 | var x1 = x0 - i1 + G3; // Offsets for second corner 185 | var y1 = y0 - j1 + G3; 186 | var z1 = z0 - k1 + G3; 187 | 188 | var x2 = x0 - i2 + 2 * G3; // Offsets for third corner 189 | var y2 = y0 - j2 + 2 * G3; 190 | var z2 = z0 - k2 + 2 * G3; 191 | 192 | var x3 = x0 - 1 + 3 * G3; // Offsets for fourth corner 193 | var y3 = y0 - 1 + 3 * G3; 194 | var z3 = z0 - 1 + 3 * G3; 195 | 196 | // Work out the hashed gradient indices of the four simplex corners 197 | i &= 255; 198 | j &= 255; 199 | k &= 255; 200 | var gi0 = gradP[i+ perm[j+ perm[k ]]]; 201 | var gi1 = gradP[i+i1+perm[j+j1+perm[k+k1]]]; 202 | var gi2 = gradP[i+i2+perm[j+j2+perm[k+k2]]]; 203 | var gi3 = gradP[i+ 1+perm[j+ 1+perm[k+ 1]]]; 204 | 205 | // Calculate the contribution from the four corners 206 | var t0 = 0.6 - x0*x0 - y0*y0 - z0*z0; 207 | if(t0<0) { 208 | n0 = 0; 209 | } else { 210 | t0 *= t0; 211 | n0 = t0 * t0 * gi0.dot3(x0, y0, z0); // (x,y) of grad3 used for 2D gradient 212 | } 213 | var t1 = 0.6 - x1*x1 - y1*y1 - z1*z1; 214 | if(t1<0) { 215 | n1 = 0; 216 | } else { 217 | t1 *= t1; 218 | n1 = t1 * t1 * gi1.dot3(x1, y1, z1); 219 | } 220 | var t2 = 0.6 - x2*x2 - y2*y2 - z2*z2; 221 | if(t2<0) { 222 | n2 = 0; 223 | } else { 224 | t2 *= t2; 225 | n2 = t2 * t2 * gi2.dot3(x2, y2, z2); 226 | } 227 | var t3 = 0.6 - x3*x3 - y3*y3 - z3*z3; 228 | if(t3<0) { 229 | n3 = 0; 230 | } else { 231 | t3 *= t3; 232 | n3 = t3 * t3 * gi3.dot3(x3, y3, z3); 233 | } 234 | // Add contributions from each corner to get the final noise value. 235 | // The result is scaled to return values in the interval [-1,1]. 236 | return 32 * (n0 + n1 + n2 + n3); 237 | 238 | }; 239 | 240 | // ##### Perlin noise stuff 241 | 242 | function fade(t) { 243 | return t*t*t*(t*(t*6-15)+10); 244 | } 245 | 246 | function lerp(a, b, t) { 247 | return (1-t)*a + t*b; 248 | } 249 | 250 | // 2D Perlin Noise 251 | module.perlin2 = function(x, y) { 252 | // Find unit grid cell containing point 253 | var X = Math.floor(x), Y = Math.floor(y); 254 | // Get relative xy coordinates of point within that cell 255 | x = x - X; y = y - Y; 256 | // Wrap the integer cells at 255 (smaller integer period can be introduced here) 257 | X = X & 255; Y = Y & 255; 258 | 259 | // Calculate noise contributions from each of the four corners 260 | var n00 = gradP[X+perm[Y]].dot2(x, y); 261 | var n01 = gradP[X+perm[Y+1]].dot2(x, y-1); 262 | var n10 = gradP[X+1+perm[Y]].dot2(x-1, y); 263 | var n11 = gradP[X+1+perm[Y+1]].dot2(x-1, y-1); 264 | 265 | // Compute the fade curve value for x 266 | var u = fade(x); 267 | 268 | // Interpolate the four results 269 | return lerp( 270 | lerp(n00, n10, u), 271 | lerp(n01, n11, u), 272 | fade(y)); 273 | }; 274 | 275 | // 3D Perlin Noise 276 | module.perlin3 = function(x, y, z) { 277 | // Find unit grid cell containing point 278 | var X = Math.floor(x), Y = Math.floor(y), Z = Math.floor(z); 279 | // Get relative xyz coordinates of point within that cell 280 | x = x - X; y = y - Y; z = z - Z; 281 | // Wrap the integer cells at 255 (smaller integer period can be introduced here) 282 | X = X & 255; Y = Y & 255; Z = Z & 255; 283 | 284 | // Calculate noise contributions from each of the eight corners 285 | var n000 = gradP[X+ perm[Y+ perm[Z ]]].dot3(x, y, z); 286 | var n001 = gradP[X+ perm[Y+ perm[Z+1]]].dot3(x, y, z-1); 287 | var n010 = gradP[X+ perm[Y+1+perm[Z ]]].dot3(x, y-1, z); 288 | var n011 = gradP[X+ perm[Y+1+perm[Z+1]]].dot3(x, y-1, z-1); 289 | var n100 = gradP[X+1+perm[Y+ perm[Z ]]].dot3(x-1, y, z); 290 | var n101 = gradP[X+1+perm[Y+ perm[Z+1]]].dot3(x-1, y, z-1); 291 | var n110 = gradP[X+1+perm[Y+1+perm[Z ]]].dot3(x-1, y-1, z); 292 | var n111 = gradP[X+1+perm[Y+1+perm[Z+1]]].dot3(x-1, y-1, z-1); 293 | 294 | // Compute the fade curve value for x, y, z 295 | var u = fade(x); 296 | var v = fade(y); 297 | var w = fade(z); 298 | 299 | // Interpolate 300 | return lerp( 301 | lerp( 302 | lerp(n000, n100, u), 303 | lerp(n001, n101, u), w), 304 | lerp( 305 | lerp(n010, n110, u), 306 | lerp(n011, n111, u), w), 307 | v); 308 | }; 309 | 310 | })(this); -------------------------------------------------------------------------------- /libs/curlNoise.js: -------------------------------------------------------------------------------- 1 | // Simplex 3D Noise 2 | // by Ian McEwan, Ashima Arts - ported from glsl 3 | // 4 | /* vec4 */ function permute(/* vec4 */ x) { 5 | // return mod(((x*34.0)+1.0)*x, 289.0); 6 | 7 | let retVec = new THREE.Vector4(); 8 | retVec.x = (((x.x * 34.0) + 1) * x.x) % 289.0; 9 | retVec.y = (((x.y * 34.0) + 1) * x.y) % 289.0; 10 | retVec.z = (((x.z * 34.0) + 1) * x.z) % 289.0; 11 | retVec.w = (((x.w * 34.0) + 1) * x.w) % 289.0; 12 | 13 | return retVec; 14 | } 15 | /* vec4 */ function taylorInvSqrt(/* vec4 */ r){ 16 | // return 1.79284291400159 - 0.85373472095314 * r; 17 | 18 | let retVec = new THREE.Vector4(); 19 | retVec.x = (1.79284291400159 - 0.85373472095314) * r.x; 20 | retVec.y = (1.79284291400159 - 0.85373472095314) * r.y; 21 | retVec.z = (1.79284291400159 - 0.85373472095314) * r.z; 22 | retVec.w = (1.79284291400159 - 0.85373472095314) * r.w; 23 | 24 | return retVec; 25 | } 26 | 27 | function vec3step(edge, x) { 28 | let rv = new THREE.Vector3(0,0,0); 29 | rv.x = x.x < edge.x ? 0 : 1; 30 | rv.y = x.y < edge.y ? 0 : 1; 31 | rv.z = x.z < edge.z ? 0 : 1; 32 | 33 | return rv; 34 | } 35 | 36 | function vec4step(edge, x) { 37 | let rv = new THREE.Vector4(0,0,0,0); 38 | rv.x = x.x < edge.x ? 0 : 1; 39 | rv.y = x.y < edge.y ? 0 : 1; 40 | rv.z = x.z < edge.z ? 0 : 1; 41 | rv.w = x.w < edge.w ? 0 : 1; 42 | 43 | return rv; 44 | } 45 | 46 | function vec3min(v1, v2) { 47 | let rv = new THREE.Vector3(0,0,0); 48 | rv.x = v1.x < v2.x ? v1.x : v2.x; 49 | rv.y = v1.y < v2.y ? v1.y : v2.y; 50 | rv.z = v1.z < v2.z ? v1.z : v2.z; 51 | 52 | return rv; 53 | } 54 | function vec3max(v1, v2) { 55 | let rv = new THREE.Vector3(0,0,0); 56 | rv.x = v1.x > v2.x ? v1.x : v2.x; 57 | rv.y = v1.y > v2.y ? v1.y : v2.y; 58 | rv.z = v1.z > v2.z ? v1.z : v2.z; 59 | 60 | return rv; 61 | } 62 | 63 | function vec3mod(v1, mod) { 64 | let rv = new THREE.Vector3(0,0,0); 65 | rv.x = v1.x % mod; 66 | rv.y = v1.y % mod; 67 | rv.z = v1.z % mod; 68 | 69 | return rv; 70 | } 71 | 72 | /* float */ function snoise(/* vec3 */ v) { 73 | // const vec2 C = vec2(1.0/6.0, 1.0/3.0) ; 74 | // const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); 75 | 76 | // // First corner 77 | // vec3 i = floor(v + dot(v, C.yyy) ); 78 | // vec3 x0 = v - i + dot(i, C.xxx) ; 79 | 80 | // // Other corners 81 | // vec3 g = step(x0.yzx, x0.xyz); 82 | // vec3 l = 1.0 - g; 83 | // vec3 i1 = min( g.xyz, l.zxy ); 84 | // vec3 i2 = max( g.xyz, l.zxy ); 85 | 86 | // // x0 = x0 - 0. + 0.0 * C 87 | // vec3 x1 = x0 - i1 + 1.0 * C.xxx; 88 | // vec3 x2 = x0 - i2 + 2.0 * C.xxx; 89 | // vec3 x3 = x0 - 1. + 3.0 * C.xxx; 90 | 91 | // // Permutations 92 | // i = mod(i, 289.0 ); 93 | // vec4 p = permute( permute( permute( 94 | // i.z + vec4(0.0, i1.z, i2.z, 1.0 )) 95 | // + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) 96 | // + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); 97 | 98 | // // Gradients 99 | // // ( N*N points uniformly over a square, mapped onto an octahedron.) 100 | // float n_ = 1.0/7.0; // N=7 101 | // vec3 ns = n_ * D.wyz - D.xzx; 102 | 103 | // vec4 j = p - 49.0 * floor(p * ns.z *ns.z); // mod(p,N*N) 104 | 105 | // vec4 x_ = floor(j * ns.z); 106 | // vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N) 107 | 108 | // vec4 x = x_ *ns.x + ns.yyyy; 109 | // vec4 y = y_ *ns.x + ns.yyyy; 110 | // vec4 h = 1.0 - abs(x) - abs(y); 111 | 112 | // vec4 b0 = vec4( x.xy, y.xy ); 113 | // vec4 b1 = vec4( x.zw, y.zw ); 114 | 115 | // vec4 s0 = floor(b0)*2.0 + 1.0; 116 | // vec4 s1 = floor(b1)*2.0 + 1.0; 117 | // vec4 sh = -step(h, vec4(0.0)); 118 | 119 | // vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; 120 | // vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; 121 | 122 | // vec3 p0 = vec3(a0.xy,h.x); 123 | // vec3 p1 = vec3(a0.zw,h.y); 124 | // vec3 p2 = vec3(a1.xy,h.z); 125 | // vec3 p3 = vec3(a1.zw,h.w); 126 | 127 | // //Normalise gradients 128 | // vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 129 | // p0 *= norm.x; 130 | // p1 *= norm.y; 131 | // p2 *= norm.z; 132 | // p3 *= norm.w; 133 | 134 | // // Mix final noise value 135 | // vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); 136 | // m = m * m; 137 | // return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), 138 | // dot(p2,x2), dot(p3,x3) ) ); 139 | 140 | 141 | 142 | let C = new THREE.Vector2(1.0/6.0, 1.0/3.0); 143 | let D = new THREE.Vector4(0.0, 0.5, 1.0, 2.0); 144 | 145 | // First corner 146 | 147 | let i = new THREE.Vector3(v.x, v.y, v.z); 148 | i.addScalar(i.dot(new THREE.Vector3(C.y, C.y, C.y))); 149 | i.x = Math.floor(i.x); 150 | i.y = Math.floor(i.y); 151 | i.z = Math.floor(i.z); 152 | i.w = Math.floor(i.w); 153 | 154 | 155 | let x0 = new THREE.Vector3(v.x, v.y, v.z); 156 | x0.sub(i); 157 | x0.addScalar(i.dot(new THREE.Vector3(C.x, C.x, C.x))); 158 | 159 | 160 | let g = new THREE.Vector3(0,0,0); 161 | g = vec3step(new THREE.Vector3(x0.y, x0.z, x0.x), new THREE.Vector3(x0.x, x0.y, x0.z)); 162 | let l = new THREE.Vector3(1,1,1); 163 | l.sub(g); 164 | let i1 = vec3min( g, new THREE.Vector3(l.z, l.x, l.y) ); 165 | let i2 = vec3max( g, new THREE.Vector3(l.z, l.x, l.y) ); 166 | 167 | 168 | let x1 = new THREE.Vector3(); 169 | x1.add(x0); x1.sub(i1); x1.addScalar(C.x); 170 | let x2 = new THREE.Vector3(); 171 | x2.add(x0); x2.sub(i2); x2.addScalar(2 * C.x); 172 | let x3 = new THREE.Vector3(); 173 | x3.add(x0); x3.subScalar(1); x3.addScalar(3 * C.x); 174 | 175 | i = vec3mod(i, 289.0 ); 176 | let p = new THREE.Vector4(); 177 | p = permute( permute( permute ( 178 | new THREE.Vector4( 179 | i.z + i.y + i.x, 180 | i.z + i.y + i.x + i1.z + i1.y + i1.x, 181 | i.z + i.y + i.x + i2.z + i2.y + i2.x, 182 | i.z + i.y + i.x + 1, 1, 1, 183 | ) 184 | ))); 185 | 186 | let n_ = 1.0 / 7.0; // N=7 187 | let ns = new THREE.Vector3(n_, n_, n_); 188 | ns.x = (ns.x * D.w) - D.x; 189 | ns.y = (ns.y * D.y) - D.z; 190 | ns.z = (ns.z * D.z) - D.x; 191 | 192 | 193 | let j = new THREE.Vector4(); 194 | j.x = p.x - 49 * Math.floor(p.x * ns.z * ns.z); 195 | j.y = p.y - 49 * Math.floor(p.y * ns.z * ns.z); 196 | j.z = p.z - 49 * Math.floor(p.z * ns.z * ns.z); 197 | j.w = p.w - 49 * Math.floor(p.w * ns.z * ns.z); 198 | 199 | let x_ = new THREE.Vector4(); 200 | x_.x = Math.floor(j.x * ns.z); 201 | x_.y = Math.floor(j.y * ns.z); 202 | x_.z = Math.floor(j.z * ns.z); 203 | x_.w = Math.floor(j.w * ns.z); 204 | 205 | let y_ = new THREE.Vector4(); 206 | y_.x = Math.floor(j.x - 7 * x_.x); 207 | y_.y = Math.floor(j.y - 7 * x_.y); 208 | y_.z = Math.floor(j.z - 7 * x_.z); 209 | y_.w = Math.floor(j.w - 7 * x_.w); 210 | 211 | 212 | 213 | let x = new THREE.Vector4(); 214 | x.x = x_.x * ns.x + ns.y; 215 | x.y = x_.y * ns.x + ns.y; 216 | x.z = x_.z * ns.x + ns.y; 217 | x.w = x_.w * ns.x + ns.y; 218 | 219 | let y = new THREE.Vector4(); 220 | y.x = y_.x * ns.x + ns.y; 221 | y.y = y_.y * ns.x + ns.y; 222 | y.z = y_.z * ns.x + ns.y; 223 | y.w = y_.w * ns.x + ns.y; 224 | 225 | let h = new THREE.Vector4(); 226 | h.x = 1 - Math.abs(x.x) - Math.abs(y.x); 227 | h.y = 1 - Math.abs(x.y) - Math.abs(y.y); 228 | h.z = 1 - Math.abs(x.z) - Math.abs(y.z); 229 | h.w = 1 - Math.abs(x.w) - Math.abs(y.w); 230 | 231 | 232 | // vec4 b0 = vec4( x.xy, y.xy ); 233 | let b0 = new THREE.Vector4(x.x, x.y, y.x, y.y); 234 | // vec4 b1 = vec4( x.zw, y.zw ); 235 | let b1 = new THREE.Vector4(x.z, x.w, y.z, y.w); 236 | 237 | // vec4 s0 = floor(b0)*2.0 + 1.0; 238 | let s0 = new THREE.Vector4(); 239 | s0.x = Math.floor(b0.x) * 2 + 1; 240 | s0.y = Math.floor(b0.y) * 2 + 1; 241 | s0.z = Math.floor(b0.z) * 2 + 1; 242 | s0.w = Math.floor(b0.w) * 2 + 1; 243 | 244 | // vec4 s1 = floor(b1)*2.0 + 1.0; 245 | let s1 = new THREE.Vector4(); 246 | s1.x = Math.floor(b1.x) * 2 + 1; 247 | s1.y = Math.floor(b1.y) * 2 + 1; 248 | s1.z = Math.floor(b1.z) * 2 + 1; 249 | s1.w = Math.floor(b1.w) * 2 + 1; 250 | 251 | // vec4 sh = -step(h, vec4(0.0)); 252 | let sh = new THREE.Vector4(); 253 | sh = vec4step(h, new THREE.Vector4(0,0,0,0)).multiplyScalar(-1); 254 | 255 | // vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; 256 | let a0 = new THREE.Vector4(); 257 | a0.x = b0.x + s0.x * sh.x; 258 | a0.y = b0.z + s0.z * sh.x; 259 | a0.z = b0.y + s0.y * sh.y; 260 | a0.w = b0.w + s0.w * sh.y; 261 | // vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; 262 | let a1 = new THREE.Vector4(); 263 | a1.x = b1.x + s1.x * sh.z; 264 | a1.y = b1.z + s1.z * sh.z; 265 | a1.z = b1.y + s1.y * sh.w; 266 | a1.w = b1.w + s1.w * sh.w; 267 | 268 | 269 | // vec3 p0 = vec3(a0.xy,h.x); 270 | let p0 = new THREE.Vector3(a0.x, a0.y, h.x); 271 | // vec3 p1 = vec3(a0.zw,h.y); 272 | let p1 = new THREE.Vector3(a0.z, a0.w, h.y); 273 | // vec3 p2 = vec3(a1.xy,h.z); 274 | let p2 = new THREE.Vector3(a1.x, a1.y, h.z); 275 | // vec3 p3 = vec3(a1.zw,h.w); 276 | let p3 = new THREE.Vector3(a1.z, a1.w, h.w); 277 | 278 | 279 | // vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 280 | let norm = taylorInvSqrt(new THREE.Vector4( p0.dot(p0), p1.dot(p1), p2.dot(p2), p3.dot(p3) )); 281 | p0.multiplyScalar(norm.x); 282 | p1.multiplyScalar(norm.y); 283 | p2.multiplyScalar(norm.z); 284 | p3.multiplyScalar(norm.w); 285 | 286 | 287 | 288 | 289 | 290 | 291 | // // Mix final noise value 292 | // vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); 293 | let t1 = new THREE.Vector4(x0.dot(x0), x1.dot(x1), x2.dot(x2), x3.dot(x3)); 294 | t1.multiplyScalar(-1); 295 | t1.addScalar(0.6); 296 | t1.x = Math.max(t1.x, 0.0); 297 | t1.y = Math.max(t1.y, 0.0); 298 | t1.z = Math.max(t1.z, 0.0); 299 | t1.w = Math.max(t1.w, 0.0); 300 | // m = m * m; 301 | let m = new THREE.Vector4(t1.x * t1.x, t1.y * t1.y, t1.z * t1.z, t1.w * t1.w); 302 | let m2 = new THREE.Vector4(m.x * m.x, m.y * m.y, m.z * m.z, m.w * m.w); 303 | 304 | // return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), 305 | // dot(p2,x2), dot(p3,x3) ) ); 306 | return 42 * m2.dot( new THREE.Vector4( p0.dot(x0), p1.dot(x1), p2.dot(x2), p3.dot(x3) )); 307 | } 308 | 309 | 310 | 311 | 312 | /*vec3*/ function snoiseVec3( /* vec3 */ x ) { 313 | // float s = snoise(vec3( x )); 314 | // float s1 = snoise(vec3( x.y - 19.1 , x.z + 33.4 , x.x + 47.2 )); 315 | // float s2 = snoise(vec3( x.z + 74.2 , x.x - 124.5 , x.y + 99.4 )); 316 | // vec3 c = vec3( s , s1 , s2 ); 317 | // return c; 318 | 319 | let s = snoise(new THREE.Vector3( x.x, x.y, x.z )); 320 | let s1 = snoise(new THREE.Vector3( x.y - 19.1 , x.z + 33.4 , x.x + 47.2 )); 321 | let s2 = snoise(new THREE.Vector3( x.z + 74.2 , x.x - 124.5 , x.y + 99.4 )); 322 | let c = new THREE.Vector3( s , s1 , s2 ); 323 | return c; 324 | }; 325 | 326 | 327 | function pnoisev3(px, py, pz) { 328 | let rv = new THREE.Vector3(); 329 | 330 | let x = noise.simplex3(px, py, pz); 331 | let y = noise.simplex3(px - 129825, py, pz + 1230951); 332 | let z = noise.simplex3(px, py - 321523, pz + 1523512); 333 | 334 | rv.x = x; 335 | rv.y = y; 336 | rv.z = z; 337 | 338 | return rv; 339 | } 340 | 341 | 342 | function initCurlNoise() { 343 | noise.seed(Utils.getSeed()); 344 | } 345 | 346 | /* vec3 */ function curlNoise( /* vec3 */ p ) { 347 | let e = 0.1; 348 | 349 | let dx = new THREE.Vector3(e, 0, 0); 350 | let dy = new THREE.Vector3(0, e, 0); 351 | let dz = new THREE.Vector3(0, 0, e); 352 | 353 | let p_x0 = pnoisev3(p.x - dx.x, p.y - dx.y, p.z - dx.z ); 354 | let p_x1 = pnoisev3(p.x + dx.x, p.y + dx.y, p.z + dx.z ); 355 | let p_y0 = pnoisev3(p.x - dy.x, p.y - dy.y, p.z - dy.z ); 356 | let p_y1 = pnoisev3(p.x + dy.x, p.y + dy.y, p.z + dy.z ); 357 | let p_z0 = pnoisev3(p.x - dz.x, p.y - dz.y, p.z - dz.z ); 358 | let p_z1 = pnoisev3(p.x + dz.x, p.y + dz.y, p.z + dz.z ); 359 | 360 | // float x = p_y1.z - p_y0.z - p_z1.y + p_z0.y; 361 | let x = p_y1.z - p_y0.z - p_z1.y + p_z0.y; 362 | // float y = p_z1.x - p_z0.x - p_x1.z + p_x0.z; 363 | let y = p_z1.x - p_z0.x - p_x1.z + p_x0.z; 364 | // float z = p_x1.y - p_x0.y - p_y1.x + p_y0.x; 365 | let z = p_x1.y - p_x0.y - p_y1.x + p_y0.x; 366 | 367 | let divisor = 1.0 / ( 2.0 * e ); 368 | let rv = new THREE.Vector3(x, y, z); 369 | rv.multiplyScalar(divisor); 370 | rv.normalize(); 371 | 372 | return rv; 373 | // return normalize( vec3( x , y , z ) * divisor ); 374 | } -------------------------------------------------------------------------------- /libs/scenes/cat-and-trees.js: -------------------------------------------------------------------------------- 1 | function setGlobals() { 2 | cameraPosition = new THREE.Vector3(14.5, 10.4, 28.9); 3 | cameraTarget = new THREE.Vector3(2.16, 0.51, -1.5); 4 | cameraFocalDistance = 33.8; 5 | 6 | // bokehStrength = 0.018; 7 | bokehStrength = 0.01; 8 | focalPowerFunction = 1; 9 | exposure = 0.0011; 10 | 11 | useBokehTexture = true; 12 | bokehTexturePath = "assets/bokeh/pentagon.png"; 13 | quadsTexturePath = "assets/textures/texture1.png"; 14 | 15 | // let seed = Date.now(); 16 | Utils.setRandomSeed("1562701299834"); 17 | // console.log(seed); 18 | } 19 | 20 | // use this function to create the lines that will be rendered 21 | function createScene(frame) { 22 | Utils.setRandomSeed("1562701299834"); 23 | 24 | let groundM1 = 0.03; 25 | let groundDispStrength = 1.15; 26 | 27 | let quadColorMult = 1.3; 28 | 29 | let at = frame / framesCount; 30 | let t = 0; 31 | let mr = 0.003; 32 | if(at > 0 && at < 0.25) { 33 | t = smoothstepAnim(0, mr, 0, 0.25, at); 34 | } else if(at >= 0.25 && at < 0.5) { 35 | t = smoothstepAnim(mr, 0, 0.25, 0.5, at); 36 | } else if(at > 0.5 && at < 0.75) { 37 | t = -smoothstepAnim(0, mr, 0.5, 0.75, at); 38 | } else if(at >= 0.75 && at < 1) { 39 | t = -smoothstepAnim(mr, 0, 0.75, 1, at); 40 | } 41 | 42 | controls.rotate(t);//Math.sin(frame / framesCount * Math.PI * 2) * 0.004); 43 | 44 | 45 | let goodTreeFallingTextPositions = []; 46 | 47 | function createTree(sv, relRot, level) { 48 | if(level >= 6) return; 49 | 50 | let v1 = sv.clone(); 51 | let v2 = new THREE.Vector3(0, (1 + ((6 - level) * 0.5)) * (Utils.rand() + 0.15), 0); 52 | v2.applyAxisAngle(new THREE.Vector3(0, 0, 1), relRot + (Utils.rand() * 2 - 1) * 0.25); 53 | v2.applyAxisAngle(new THREE.Vector3(1, 0, 0), (Utils.rand() * 2 - 1) * 0.25); 54 | v2.add(v1); 55 | 56 | let colorMult = 1; 57 | 58 | 59 | if(v1.z < 15) 60 | lines.push({ 61 | x1: v1.x, 62 | y1: v1.y, 63 | z1: v1.z, 64 | 65 | x2: v2.x, 66 | y2: v2.y, 67 | z2: v2.z, 68 | 69 | c1r: 1 * colorMult, 70 | c1g: 1 * colorMult, 71 | c1b: 1 * colorMult, 72 | 73 | c2r: 1 * colorMult, 74 | c2g: 1 * colorMult, 75 | c2b: 1 * colorMult, 76 | }); 77 | 78 | 79 | 80 | let relRotAnim = (Math.sin(frame / framesCount * Math.PI * 2 + (v1.x + v1.z) * 0.2) * 0.5 + 0.5) * 0.03; 81 | if(level < 2) relRotAnim = 0; 82 | 83 | 84 | 85 | if(Utils.rand() > 0.3) 86 | createTree(v2, relRot - 0.5 + relRotAnim, level + 1); 87 | 88 | if(Utils.rand() > 0.85) 89 | createTree(v2, relRot + 0.0 + relRotAnim, level + 1); 90 | 91 | if(Utils.rand() > 0.3) 92 | createTree(v2, relRot + 0.5 + relRotAnim, level + 1); 93 | 94 | 95 | if(level === 5 /*&& (Math.abs(relRot) > 0.25)*/ && Utils.rand() > 0.4) { 96 | let skip = goodtree; 97 | createFallingText(v2, skip); 98 | } 99 | 100 | if(level === 5 && goodtree) { 101 | goodTreeFallingTextPositions.push(v2); 102 | } 103 | } 104 | function createFirstTree(sv, relRot, level) { 105 | if(level >= 4) return; 106 | 107 | 108 | let v1 = sv.clone(); 109 | let v2 = new THREE.Vector3(0, (0.65 + ((6 - level) * 0.35)) * (Utils.rand() + 0.15), 0); 110 | v2.applyAxisAngle(new THREE.Vector3(0, 0, 1), relRot + (Utils.rand() * 2 - 1) * 0.25); 111 | v2.applyAxisAngle(new THREE.Vector3(1, 0, 0), (Utils.rand() * 2 - 1) * 0.25); 112 | v2.add(v1); 113 | 114 | let colorMult = 1; 115 | 116 | lines.push({ 117 | x1: v1.x, 118 | y1: v1.y, 119 | z1: v1.z, 120 | 121 | x2: v2.x, 122 | y2: v2.y, 123 | z2: v2.z, 124 | 125 | c1r: 1 * colorMult, 126 | c1g: 1 * colorMult, 127 | c1b: 1 * colorMult, 128 | 129 | c2r: 1 * colorMult, 130 | c2g: 1 * colorMult, 131 | c2b: 1 * colorMult, 132 | }); 133 | 134 | if(Utils.rand() > 0.25) 135 | createTree(v2, relRot - 0.5, level + 1); 136 | 137 | if(Utils.rand() > 0.95) 138 | createTree(v2, relRot + 0.0, level + 1); 139 | 140 | if(Utils.rand() > 0.25) 141 | createTree(v2, relRot + 0.5, level + 1); 142 | 143 | 144 | 145 | if(level === 5 /*&& (Math.abs(relRot) > 0.25)*/ && Utils.rand() > 0.4) { 146 | createFallingText(v2); 147 | } 148 | } 149 | 150 | var goodtree = false; 151 | for(let i = 0; i < 35; i++) { 152 | 153 | let x = (Utils.rand() * 2 - 1) * 30; 154 | let z = -(Utils.rand()) * 90 + 30; 155 | 156 | let pos = new THREE.Vector3(x, 0, z); 157 | pos.x += (Utils.rand() * 2 - 1) * 2; 158 | pos.z += (Utils.rand() * 2 - 1) * 2; 159 | pos.z = pos.z - pos.z % 0.375; 160 | 161 | if(i === 0) { 162 | pos.x = 0; 163 | pos.z = 0; 164 | } 165 | 166 | let cv = curlNoise(pos.clone().multiplyScalar(groundM1)).multiplyScalar(groundDispStrength); 167 | pos.add(cv); 168 | 169 | if(i === 5) { 170 | goodtree = true; 171 | } else { 172 | goodtree = false; 173 | } 174 | 175 | if(i === 0) 176 | createFirstTree(pos, 0, 1); 177 | else 178 | createTree(pos, 0, 1); 179 | } 180 | 181 | function createFallingText(start, skip) { 182 | 183 | let chars = Math.floor(4 + Utils.rand() * 5); 184 | 185 | let a1Start = Utils.rand() * Math.PI * 2; 186 | 187 | for(let i = 0; i < chars; i++) { 188 | 189 | let index = 0; 190 | 191 | if(i === 0) 192 | index = Math.floor(33 + Utils.rand() * 25); 193 | else 194 | index = Math.floor(65 + Utils.rand() * 22); 195 | 196 | // 16 * 16 because uvs starts from the bottom 197 | let t = indexToUvs(16 * 16 - index); 198 | 199 | 200 | let maxRot = 0.1 + Utils.rand() * 0.1; 201 | let maxRotSpeed = 0.5 + Utils.rand() * 0.9; 202 | let a1 = Math.sin(a1Start + i * maxRotSpeed + frame / framesCount * Math.PI * 2) * maxRot; 203 | 204 | let color = { 205 | x: 100, 206 | y: 100, 207 | z: 100, 208 | }; 209 | 210 | let cm = Utils.rand() * 0.4 + 0.6; 211 | if(Utils.rand() > 0.65) { 212 | color.x = 100 * cm + Utils.rand() * 50 * cm; 213 | color.y = 30 * cm + Utils.rand() * 50 * cm; 214 | color.z = 10 * cm + Utils.rand() * 50 * cm; 215 | } 216 | 217 | let quad = new Quad(0,0,0, t.us, t.vs, t.ue, t.ve); 218 | 219 | quad = quad 220 | .scale(0.5) 221 | .translate(0, 1, 0) 222 | .rotate(0, 0, 1, a1) 223 | .translate(-(quad.v1.x + quad.v2.x) / 2, 0, 0) 224 | .color(color.x * quadColorMult, color.y * quadColorMult, color.z * quadColorMult) 225 | .translate(start.x, start.y - 0.5 - i * 0.35, start.z) 226 | .translate(0, -1, 0) 227 | 228 | if(!skip && start.z < 15) 229 | quads.push(quad); 230 | } 231 | } 232 | 233 | 234 | 235 | // *************** grass 236 | for(let j = -30; j < 30; j++) { 237 | for(let i = -50; i < 50; i++) { 238 | let v1 = new THREE.Vector3( j * 0.7, 0, i * 0.375 ); 239 | let v2 = new THREE.Vector3( (j+1) * 0.7, 0, i * 0.375 ); 240 | 241 | let cv = curlNoise(v1.clone().multiplyScalar(groundM1)).multiplyScalar(groundDispStrength); 242 | v1.add(cv); 243 | 244 | cv = curlNoise(v2.clone().multiplyScalar(groundM1)).multiplyScalar(groundDispStrength); 245 | v2.add(cv); 246 | 247 | let color = 0.027; 248 | 249 | lines.push({ 250 | x1: v1.x, 251 | y1: v1.y, 252 | z1: v1.z, 253 | 254 | x2: v2.x, 255 | y2: v2.y, 256 | z2: v2.z, 257 | 258 | c1r: color, 259 | c1g: color, 260 | c1b: color, 261 | 262 | c2r: color, 263 | c2g: color, 264 | c2b: color, 265 | 266 | weight: 0.025, 267 | }); 268 | } 269 | } 270 | 271 | 272 | 273 | for(let i = 0; i < 85000; i++) { 274 | // for(let i = 0; i < 2000; i++) { 275 | let v1 = new THREE.Vector3( 276 | (Utils.rand() * 2 - 1) * 45, 277 | // (Utils.rand() * 2 - 1) * 10, 278 | 0, 279 | Math.floor((Utils.rand() * 2 - 1) * 135) * 0.375, 280 | // Math.floor((Utils.rand() * 2 - 1) * 45) * 0.375 - 5, 281 | ); 282 | 283 | let cv = curlNoise(v1.clone().multiplyScalar(groundM1)).multiplyScalar(groundDispStrength); 284 | v1.add(cv); 285 | 286 | let v2 = new THREE.Vector3(0, 0.1 + Utils.rand() * 1.15, 0); 287 | let windAngle = 0; 288 | let at = ((frame / framesCount) + Math.abs(cv.x + cv.z) * 0.2) % 1; 289 | if(at >= 0 && at < 0.25) { 290 | windAngle = -smoothstepAnim(0, 0.15, 0, 0.25, at); 291 | } 292 | if(at >= 0.25 && at <= 0.4) { 293 | windAngle = -smoothstepAnim(0.15, 0, 0.25, 0.4, at); 294 | } 295 | if(at >= 0.4 && at <= 0.54) { 296 | windAngle = -smoothstepAnim(0, 0.3, 0.4, 0.54, at); 297 | } 298 | if(at >= 0.54 && at <= 0.60) { 299 | windAngle = -smoothstepAnim(0.3, 0.2, 0.54, 0.60, at); 300 | } 301 | if(at >= 0.60 && at <= 0.73) { 302 | windAngle = -smoothstepAnim(0.2, 0.55, 0.60, 0.73, at); 303 | } 304 | if(at >= 0.73 && at <= 1) { 305 | windAngle = -smoothstepAnim(0.55, 0, 0.73, 1, at); 306 | } 307 | 308 | v2.applyAxisAngle(new THREE.Vector3(0,0,1), windAngle); 309 | 310 | 311 | let ax = 1; 312 | let az = 0; 313 | if(Utils.rand() > 0.5) { 314 | ax = 0; 315 | az = 1; 316 | } 317 | v2.applyAxisAngle(new THREE.Vector3(ax, 0, az), (Utils.rand() * 2 - 1) * 0.25); 318 | v2.add(v1); 319 | 320 | let colorMult = 0.05 * Math.pow(Utils.rand(), 1.25) * 0.95; 321 | 322 | lines.push({ 323 | x1: v1.x, 324 | y1: v1.y, 325 | z1: v1.z, 326 | 327 | x2: v2.x, 328 | y2: v2.y, 329 | z2: v2.z, 330 | 331 | c1r: 1 * colorMult, 332 | c1g: 1 * colorMult, 333 | c1b: 1 * colorMult, 334 | 335 | c2r: 1 * colorMult, 336 | c2g: 1 * colorMult, 337 | c2b: 1 * colorMult, 338 | }); 339 | } 340 | 341 | // *************** grass - END 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | // ************** grounded letters 357 | for(let i = 0; i < 500; i++) { 358 | let x = (Utils.rand() * 2 - 1) * 15; 359 | let z = (Utils.rand() * 2 - 1) * 15 + 0; 360 | let y = (Utils.rand()) * 1; 361 | 362 | let pos = new THREE.Vector3(x, y, z); 363 | pos.x += (Utils.rand() * 2 - 1) * 2; 364 | pos.z += (Utils.rand() * 2 - 1) * 2; 365 | pos.z = pos.z - pos.z % 0.375; 366 | 367 | let cv = curlNoise(pos.clone().multiplyScalar(groundM1)).multiplyScalar(groundDispStrength); 368 | pos.add(cv); 369 | 370 | 371 | 372 | 373 | // ************* animation params 374 | let r1 = ((x + y) % 0.015) / 0.015; 375 | r1 += frames / framesCount; 376 | r1 *= Math.PI * 2; 377 | pos.y += (Math.sin(r1)) * 0.2 + 0.2; 378 | // ************* animation params - END 379 | 380 | 381 | 382 | 383 | let index = 0; 384 | index = Math.floor(33 + Utils.rand() * 52); 385 | // 16 * 16 because uvs starts from the bottom 386 | let t = indexToUvs(16 * 16 - index); 387 | 388 | let cm = Utils.rand(); 389 | let color = { 390 | x: 100 * cm, 391 | y: 100 * cm, 392 | z: 100 * cm, 393 | }; 394 | if(Utils.rand() > 0.15) { 395 | color.x = 100 * cm + Utils.rand() * 50 * cm; 396 | color.y = 30 * cm + Utils.rand() * 50 * cm; 397 | color.z = 10 * cm + Utils.rand() * 50 * cm; 398 | } 399 | 400 | let quad = new Quad(0,0,0, t.us, t.vs, t.ue, t.ve); 401 | 402 | quad = quad 403 | .scale(0.35) 404 | .rotate(1, 0, 0, Utils.rand() * Math.PI) 405 | .color(color.x * quadColorMult, color.y * quadColorMult, color.z * quadColorMult) 406 | .translate(pos.x, pos.y, pos.z) 407 | 408 | quads.push(quad); 409 | } 410 | // ************** grounded letters - END 411 | 412 | 413 | 414 | 415 | 416 | // ************** cat 417 | let catquad = new Quad(0,0,0, 0.5, 0, 1, 0.5) 418 | .scale(-2, 2) 419 | .rotate(0, 1, 0, Math.PI) 420 | .color(500 * quadColorMult, 500 * quadColorMult, 500 * quadColorMult) 421 | .translate(0, 0, 0) 422 | 423 | let cv = curlNoise(new THREE.Vector3(0, 0, 0).multiplyScalar(groundM1)).multiplyScalar(groundDispStrength); 424 | catquad.translate(cv.x + 5, cv.y + 0.98, cv.z - 2); 425 | catquad.weight = 0.1; 426 | 427 | quads.push(catquad); 428 | // ************** cat - END 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | // *************** good tree letters 441 | for(let i = 0; i < goodTreeFallingTextPositions.length; i++) { 442 | 443 | goodTreeFallingTextPositions[i].add(new THREE.Vector3(0.15, 0.2, 0)); 444 | 445 | if(i === 0) createFallingText(goodTreeFallingTextPositions[i]); 446 | if(i === 1) createFallingText(goodTreeFallingTextPositions[i]); 447 | if(i === 2) createFallingText(goodTreeFallingTextPositions[i]); 448 | if(i === 3) createFallingText(goodTreeFallingTextPositions[i]); 449 | if(i === 4) createFallingText(goodTreeFallingTextPositions[i]); 450 | // if(i === 5) createFallingText(goodTreeFallingTextPositions[i]); 451 | if(i === 6) createFallingText(goodTreeFallingTextPositions[i]); 452 | if(i === 7) createFallingText(goodTreeFallingTextPositions[i]); 453 | if(i === 8) createFallingText(goodTreeFallingTextPositions[i]); 454 | // if(i === 9) createFallingText(goodTreeFallingTextPositions[i]); 455 | if(i === 10) createFallingText(goodTreeFallingTextPositions[i]); 456 | if(i === 11) createFallingText(goodTreeFallingTextPositions[i]); 457 | if(i === 12) createFallingText(goodTreeFallingTextPositions[i]); 458 | } 459 | // *************** good tree letters - END 460 | 461 | 462 | 463 | 464 | 465 | } 466 | 467 | 468 | 469 | function smoothstepAnim(from, to, animStart, animEnd, animCurrent) { 470 | let t = (animCurrent - animStart) / (animEnd - animStart); 471 | t = t * t * (3.0 - 2.0 * t); 472 | let delta = to - from; 473 | 474 | 475 | return from + delta * t; 476 | } 477 | 478 | 479 | function indexToUvs(index) { 480 | let charsPerRow = 16; 481 | 482 | let x = index % charsPerRow; 483 | let y = Math.floor(index / charsPerRow); 484 | 485 | let us = x / charsPerRow; 486 | let vs = y / charsPerRow; 487 | 488 | let ue = us + 1 / charsPerRow; 489 | let ve = vs + 1 / charsPerRow; 490 | 491 | return { 492 | us: us * 0.5, 493 | vs: vs * 0.5, 494 | 495 | ue: ue * 0.5, 496 | ve: ve * 0.5, 497 | } 498 | } -------------------------------------------------------------------------------- /libs/main.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", init); 2 | 3 | var scene; 4 | var postProcScene; 5 | var shaderPassScene; 6 | var camera; 7 | var postProcCamera; 8 | var controls; 9 | var renderer; 10 | var canvas; 11 | 12 | var preventOnControlsChangeReset = false; 13 | 14 | var postProcQuadMaterial; 15 | 16 | var capturerStarted = false; 17 | 18 | let lines = [ ]; 19 | let linesGeometry; 20 | let linesMaterial; 21 | 22 | let quads = [ ]; 23 | let quadsGeometry; 24 | let quadsMaterial; 25 | 26 | let shaderPassMaterial; 27 | 28 | let samples = 0; 29 | 30 | var offscreenRT; 31 | 32 | // The threejs version used in this repo was modified at line: 23060 to disable frustum culling 33 | let frames = 0; 34 | 35 | var controls = { }; 36 | 37 | function init() { 38 | if(setGlobals) setGlobals(); 39 | 40 | initCurlNoise(); 41 | 42 | renderer = new THREE.WebGLRenderer( { } ); 43 | renderer.setPixelRatio( window.devicePixelRatio ); 44 | renderer.setSize( innerWidth, innerHeight ); 45 | renderer.autoClear = false; 46 | document.body.appendChild(renderer.domElement); 47 | canvas = renderer.domElement; 48 | 49 | 50 | scene = new THREE.Scene(); 51 | postProcScene = new THREE.Scene(); 52 | shaderPassScene = new THREE.Scene(); 53 | 54 | camera = new THREE.PerspectiveCamera( 20, innerWidth / innerHeight, 2, 2000 ); 55 | // let dirVec = new THREE.Vector3(-5, -5, 10).normalize().multiplyScalar(49); 56 | // camera.position.set( dirVec.x, dirVec.y, dirVec.z ); 57 | // camera.position.set( 0, 0, 100 ); 58 | camera.position.set(cameraPosition.x, cameraPosition.y, cameraPosition.z); 59 | 60 | 61 | postProcCamera = new THREE.PerspectiveCamera( 20, innerWidth / innerHeight, 2, 2000 ); 62 | postProcCamera.position.set(0, 0, 10); 63 | 64 | controls = new THREE.OrbitControls(camera, renderer.domElement); 65 | controls.target.set(cameraTarget.x, cameraTarget.y, cameraTarget.z); 66 | controls.rotateSpeed = 1; 67 | controls.minAzimuthAngle = -Infinity; 68 | controls.maxAzimuthAngle = +Infinity; 69 | controls.minPolarAngle = 0; 70 | controls.maxPolarAngle = Math.PI - 0; 71 | 72 | controls.addEventListener("change", function() { 73 | if(!preventOnControlsChangeReset) 74 | resetCanvas(); 75 | }); 76 | 77 | 78 | 79 | offscreenRT = new THREE.WebGLRenderTarget(innerWidth, innerHeight, { 80 | stencilBuffer: false, 81 | depthBuffer: false, 82 | type: THREE.FloatType, 83 | }); 84 | 85 | var postProcQuadGeo = new THREE.PlaneBufferGeometry(2,2); 86 | postProcQuadMaterial = new THREE.ShaderMaterial({ 87 | vertexShader: postprocv, 88 | fragmentShader: postprocf, 89 | uniforms: { 90 | texture: { type: "t", value: offscreenRT.texture }, 91 | uSamples: { value: samples }, 92 | uExposure: { value: exposure }, 93 | uBackgroundColor: new THREE.Uniform(new THREE.Vector3(backgroundColor[0], backgroundColor[1], backgroundColor[2])), 94 | uResolution: new THREE.Uniform(new THREE.Vector2(innerWidth, innerHeight)), 95 | uCameraPosition: new THREE.Uniform(new THREE.Vector3(0,0,0)), 96 | }, 97 | side: THREE.DoubleSide, 98 | }); 99 | postProcScene.add(new THREE.Mesh(postProcQuadGeo, postProcQuadMaterial)); 100 | 101 | 102 | 103 | 104 | var shaderPassQuadGeo = new THREE.PlaneBufferGeometry(2,2); 105 | shaderPassMaterial = new THREE.ShaderMaterial({ 106 | vertexShader: shaderpassv, 107 | fragmentShader: shaderpassf, 108 | uniforms: { 109 | uTime: { value: 0 }, 110 | uResolution: new THREE.Uniform(new THREE.Vector2(innerWidth, innerHeight)), 111 | uCameraPosition: new THREE.Uniform(new THREE.Vector3(0,0,0)), 112 | uRandoms: new THREE.Uniform(new THREE.Vector4(0,0,0,0)), 113 | uBokehStrength: { value: 0 }, 114 | }, 115 | side: THREE.DoubleSide, 116 | depthTest: false, 117 | 118 | blending: THREE.CustomBlending, 119 | blendEquation: THREE.AddEquation, 120 | blendSrc: THREE.OneFactor, 121 | blendSrcAlpha: THREE.OneFactor, 122 | blendDst: THREE.OneFactor, 123 | blendDstAlpha: THREE.OneFactor, 124 | }); 125 | shaderPassScene.add(new THREE.Mesh(shaderPassQuadGeo, shaderPassMaterial)); 126 | 127 | 128 | linesMaterial = new THREE.ShaderMaterial({ 129 | vertexShader: linev, 130 | fragmentShader: linef, 131 | uniforms: { 132 | uTime: { value: 0 }, 133 | uRandom: { value: 0 }, 134 | uRandomVec4: new THREE.Uniform(new THREE.Vector4(0, 0, 0, 0)), 135 | uFocalDepth: { value: cameraFocalDistance }, 136 | uBokehStrength: { value: bokehStrength }, 137 | uMinimumLineSize: { value: minimumLineSize }, 138 | uFocalPowerFunction: { value: focalPowerFunction }, 139 | uBokehTexture: { type: "t", value: new THREE.TextureLoader().load(bokehTexturePath) }, 140 | uDistanceAttenuation: { value: distanceAttenuation }, 141 | }, 142 | 143 | defines: { 144 | USE_BOKEH_TEXTURE: (useBokehTexture ? 1 : 0) 145 | }, 146 | 147 | side: THREE.DoubleSide, 148 | depthTest: false, 149 | 150 | blending: THREE.CustomBlending, 151 | blendEquation: THREE.AddEquation, 152 | blendSrc: THREE.OneFactor, 153 | blendSrcAlpha: THREE.OneFactor, 154 | blendDst: THREE.OneFactor, 155 | blendDstAlpha: THREE.OneFactor, 156 | }); 157 | 158 | quadsMaterial = new THREE.ShaderMaterial({ 159 | vertexShader: quadv, 160 | fragmentShader: quadf, 161 | uniforms: { 162 | uTexture: { type: "t", value: new THREE.TextureLoader().load(quadsTexturePath) }, 163 | uTime: { value: 0 }, 164 | uRandom: { value: 0 }, 165 | uRandomVec4: new THREE.Uniform(new THREE.Vector4(0, 0, 0, 0)), 166 | uFocalDepth: { value: cameraFocalDistance }, 167 | uBokehStrength: { value: bokehStrength }, 168 | uMinimumLineSize: { value: minimumLineSize }, 169 | uFocalPowerFunction: { value: focalPowerFunction }, 170 | uBokehTexture: { type: "t", value: new THREE.TextureLoader().load(bokehTexturePath) }, 171 | uDistanceAttenuation: { value: distanceAttenuation }, 172 | }, 173 | 174 | defines: { 175 | USE_BOKEH_TEXTURE: (useBokehTexture ? 1 : 0) 176 | }, 177 | 178 | side: THREE.DoubleSide, 179 | depthTest: false, 180 | 181 | blending: THREE.CustomBlending, 182 | blendEquation: THREE.AddEquation, 183 | blendSrc: THREE.OneFactor, 184 | blendSrcAlpha: THREE.OneFactor, 185 | blendDst: THREE.OneFactor, 186 | blendDstAlpha: THREE.OneFactor, 187 | }); 188 | 189 | 190 | createLinesWrapper(frames / motionBlurFrames); 191 | 192 | 193 | buildControls(); 194 | render(); 195 | } 196 | 197 | 198 | let lastFrameDate = 0; 199 | function render(now) { 200 | requestAnimationFrame(render); 201 | 202 | checkControls(); 203 | 204 | 205 | 206 | if(!capturerStarted) { 207 | capturerStarted = true; 208 | } 209 | 210 | controls.update(); 211 | 212 | 213 | for(let i = 0; i < drawCallsPerFrame; i++) { 214 | samples++; 215 | linesMaterial.uniforms.uBokehStrength.value = bokehStrength; 216 | linesMaterial.uniforms.uFocalDepth.value = cameraFocalDistance; 217 | linesMaterial.uniforms.uFocalPowerFunction.value = focalPowerFunction; 218 | linesMaterial.uniforms.uMinimumLineSize.value = minimumLineSize; 219 | linesMaterial.uniforms.uRandom.value = Math.random() * 1000; 220 | linesMaterial.uniforms.uTime.value = (now * 0.001) % 100; // modulating time by 100 since it appears hash12 suffers with higher time values 221 | linesMaterial.uniforms.uRandomVec4.value = new THREE.Vector4(Math.random() * 100, Math.random() * 100, Math.random() * 100, Math.random() * 100); 222 | linesMaterial.uniforms.uDistanceAttenuation.value = distanceAttenuation; 223 | 224 | quadsMaterial.uniforms.uBokehStrength.value = bokehStrength; 225 | quadsMaterial.uniforms.uFocalDepth.value = cameraFocalDistance; 226 | quadsMaterial.uniforms.uFocalPowerFunction.value = focalPowerFunction; 227 | quadsMaterial.uniforms.uMinimumLineSize.value = minimumLineSize; 228 | quadsMaterial.uniforms.uRandom.value = Math.random() * 1000; 229 | quadsMaterial.uniforms.uTime.value = (now * 0.001) % 100; // modulating time by 100 since it appears hash12 suffers with higher time values 230 | quadsMaterial.uniforms.uRandomVec4.value = new THREE.Vector4(Math.random() * 100, Math.random() * 100, Math.random() * 100, Math.random() * 100); 231 | quadsMaterial.uniforms.uDistanceAttenuation.value = distanceAttenuation; 232 | 233 | renderer.render(scene, camera, offscreenRT); 234 | } 235 | 236 | if(shaderpassf !== "") { 237 | shaderPassMaterial.uniforms.uTime.value = (now * 0.001) % 1000; 238 | shaderPassMaterial.uniforms.uRandoms.value = new THREE.Vector4(Math.random(), Math.random(), Math.random(), Math.random()); 239 | shaderPassMaterial.uniforms.uCameraPosition.value = new THREE.Vector3(camera.position.x, camera.position.y, camera.position.z); 240 | shaderPassMaterial.uniforms.uBokehStrength.value = bokehStrength; 241 | renderer.render(shaderPassScene, postProcCamera, offscreenRT); 242 | } 243 | 244 | postProcQuadMaterial.uniforms.uSamples.value = samples; 245 | postProcQuadMaterial.uniforms.uExposure.value = exposure; 246 | postProcQuadMaterial.uniforms.uCameraPosition.value = new THREE.Vector3(camera.position.x, camera.position.y, camera.position.z); 247 | renderer.render(postProcScene, postProcCamera); 248 | 249 | 250 | // used to make GIF animations 251 | if(lastFrameDate + millisecondsPerFrame < Date.now()) { 252 | frames++; 253 | createLinesWrapper(frames / motionBlurFrames); 254 | 255 | if(frames % motionBlurFrames === 0) { 256 | resetCanvas(); 257 | 258 | if(captureFrames) { 259 | var photo = canvas.toDataURL('image/jpeg'); 260 | $.ajax({ 261 | method: 'POST', 262 | url: 'photo_upload.php', 263 | data: { 264 | photo: photo 265 | } 266 | }); 267 | } 268 | } 269 | 270 | lastFrameDate = Date.now(); 271 | 272 | if(frames === (framesCount * motionBlurFrames)) { 273 | lastFrameDate = Infinity; 274 | frames = 0; 275 | } 276 | } 277 | } 278 | 279 | 280 | function resetCanvas() { 281 | scene.background = new THREE.Color(0x000000); 282 | renderer.render(scene, camera, offscreenRT); 283 | samples = 0; 284 | scene.background = null; 285 | } 286 | 287 | function createLinesWrapper(frames) { 288 | // ***************** lines creation 289 | lines = []; 290 | scene.remove(scene.getObjectByName("points")); 291 | 292 | quads = []; 293 | scene.remove(scene.getObjectByName("quad-points")); 294 | 295 | 296 | 297 | 298 | createScene(frames); 299 | 300 | 301 | 302 | // ***************** lines creation 303 | createLinesGeometry(); 304 | let mesh = new THREE.Points(linesGeometry, linesMaterial); 305 | mesh.name = "points"; 306 | 307 | scene.add(mesh); 308 | // ***************** lines creation - END 309 | 310 | 311 | 312 | // ***************** quads creation 313 | createQuadsGeometry(); 314 | let quadmesh = new THREE.Points(quadsGeometry, quadsMaterial); 315 | quadmesh.name = "quad-points"; 316 | 317 | scene.add(quadmesh); 318 | // ***************** quads creation - END 319 | 320 | } 321 | 322 | function createLinesGeometry() { 323 | 324 | var geometry = new THREE.BufferGeometry(); 325 | var position1 = []; 326 | var position2 = []; 327 | var color1 = []; 328 | var color2 = []; 329 | var seed = []; 330 | 331 | 332 | 333 | let accumulatedLinesLength = 0; 334 | for(let i = 0; i < lines.length; i++) { 335 | let line = lines[i]; 336 | 337 | let lx1 = line.x1; 338 | let ly1 = line.y1; 339 | let lz1 = line.z1; 340 | 341 | let lx2 = line.x2; 342 | let ly2 = line.y2; 343 | let lz2 = line.z2; 344 | 345 | let weight = line.weight || 1; 346 | 347 | let dx = lx1 - lx2; 348 | let dy = ly1 - ly2; 349 | let dz = lz1 - lz2; 350 | let lineLength = Math.sqrt(dx*dx + dy*dy + dz*dz) * weight; 351 | 352 | accumulatedLinesLength += lineLength; 353 | } 354 | let pointsPerUnit = pointsPerFrame / accumulatedLinesLength; 355 | 356 | 357 | 358 | 359 | for(let j = 0; j < lines.length; j++) { 360 | 361 | let line = lines[j]; 362 | 363 | let lx1 = line.x1; 364 | let ly1 = line.y1; 365 | let lz1 = line.z1; 366 | 367 | let lx2 = line.x2; 368 | let ly2 = line.y2; 369 | let lz2 = line.z2; 370 | 371 | let weight = line.weight || 1; 372 | 373 | 374 | // how many points per line? 375 | let points = pointsPerLine; 376 | let invPointsPerLine = 1 / points; 377 | 378 | if(useLengthSampling) { 379 | let dx = lx1 - lx2; 380 | let dy = ly1 - ly2; 381 | let dz = lz1 - lz2; 382 | let lineLength = Math.sqrt(dx*dx + dy*dy + dz*dz); 383 | 384 | points = Math.max( Math.floor(pointsPerUnit * lineLength * weight), 1 ); 385 | invPointsPerLine = 1 / points; 386 | } 387 | 388 | for(let ppr = 0; ppr < points; ppr++) { 389 | position1.push(lx1, ly1, lz1); 390 | position2.push(lx2, ly2, lz2); 391 | color1.push(line.c1r * invPointsPerLine, line.c1g * invPointsPerLine, line.c1b * invPointsPerLine); 392 | color2.push(line.c2r * invPointsPerLine, line.c2g * invPointsPerLine, line.c2b * invPointsPerLine) 393 | 394 | seed.push(Math.random() * 100, Math.random() * 100, Math.random() * 100, Math.random() * 100); 395 | } 396 | } 397 | 398 | 399 | geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array(position1), 3 ) ); 400 | geometry.addAttribute( 'position1', new THREE.BufferAttribute( new Float32Array(position2), 3 ) ); 401 | geometry.addAttribute( 'color1', new THREE.BufferAttribute( new Float32Array(color1), 3 ) ); 402 | geometry.addAttribute( 'color2', new THREE.BufferAttribute( new Float32Array(color2), 3 ) ); 403 | geometry.addAttribute( 'aSeed', new THREE.BufferAttribute( new Float32Array(seed), 4 ) ); 404 | 405 | linesGeometry = geometry; 406 | } 407 | 408 | function createQuadsGeometry() { 409 | 410 | var geometry = new THREE.BufferGeometry(); 411 | var position1 = []; 412 | var position2 = []; 413 | var position3 = []; 414 | var uv1 = []; 415 | var uv2 = []; 416 | var color = []; 417 | var seeds = []; 418 | 419 | let accumulatedQuadsArea = 0; 420 | for(let i = 0; i < quads.length; i++) { 421 | let quad = quads[i]; 422 | 423 | let lx1 = quad.v1.x; 424 | let ly1 = quad.v1.y; 425 | let lz1 = quad.v1.z; 426 | 427 | let lx2 = quad.v2.x; 428 | let ly2 = quad.v2.y; 429 | let lz2 = quad.v2.z; 430 | 431 | let weight = quad.weight || 1; 432 | 433 | let dx = lx1 - lx2; 434 | let dy = ly1 - ly2; 435 | let dz = lz1 - lz2; 436 | let sideLength = Math.sqrt(dx*dx + dy*dy + dz*dz); 437 | let areaLength = (sideLength * sideLength) * weight; 438 | 439 | accumulatedQuadsArea += areaLength; 440 | } 441 | let pointsPerUnitArea = quadPointsPerFrame / accumulatedQuadsArea; 442 | 443 | for(let j = 0; j < quads.length; j++) { 444 | 445 | let quad = quads[j]; 446 | 447 | let lx1 = quad.v1.x; 448 | let ly1 = quad.v1.y; 449 | let lz1 = quad.v1.z; 450 | 451 | let lx2 = quad.v2.x; 452 | let ly2 = quad.v2.y; 453 | let lz2 = quad.v2.z; 454 | 455 | let lx3 = quad.v3.x; 456 | let ly3 = quad.v3.y; 457 | let lz3 = quad.v3.z; 458 | 459 | let weight = quad.weight || 1; 460 | 461 | if(j === 829) { 462 | let debug = 0; 463 | } 464 | 465 | let u1 = quad.uv1.x; 466 | let v1 = quad.uv1.y; 467 | 468 | let u2 = quad.uv2.x; 469 | let v2 = quad.uv2.y; 470 | 471 | 472 | let points = pointsPerQuad; 473 | let invPointsPerQuad = 1 / points; 474 | 475 | if(useLengthSampling) { 476 | let dx = lx1 - lx2; 477 | let dy = ly1 - ly2; 478 | let dz = lz1 - lz2; 479 | let sideLength = Math.sqrt(dx*dx + dy*dy + dz*dz); 480 | let areaLength = (sideLength * sideLength); 481 | 482 | points = Math.max( Math.floor(pointsPerUnitArea * areaLength * weight), 1 ); 483 | invPointsPerQuad = 1 / points; 484 | } 485 | 486 | 487 | for(let ppr = 0; ppr < points; ppr++) { 488 | position1.push(lx1, ly1, lz1); 489 | position2.push(lx2, ly2, lz2); 490 | position3.push(lx3, ly3, lz3); 491 | uv1.push(u1, v1); 492 | uv2.push(u2, v2); 493 | color.push(quad.col.x * invPointsPerQuad, quad.col.y * invPointsPerQuad, quad.col.z * invPointsPerQuad); 494 | 495 | seeds.push(Math.random() * 100, Math.random() * 100, Math.random() * 100, Math.random() * 100); 496 | } 497 | } 498 | 499 | geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array(position1), 3 ) ); 500 | geometry.addAttribute( 'position1', new THREE.BufferAttribute( new Float32Array(position2), 3 ) ); 501 | geometry.addAttribute( 'position2', new THREE.BufferAttribute( new Float32Array(position3), 3 ) ); 502 | geometry.addAttribute( 'uv1', new THREE.BufferAttribute( new Float32Array(uv1), 2 ) ); 503 | geometry.addAttribute( 'uv2', new THREE.BufferAttribute( new Float32Array(uv2), 2 ) ); 504 | geometry.addAttribute( 'color', new THREE.BufferAttribute( new Float32Array(color), 3 ) ); 505 | geometry.addAttribute( 'aSeeds', new THREE.BufferAttribute( new Float32Array(seeds), 4 ) ); 506 | 507 | quadsGeometry = geometry; 508 | } 509 | 510 | 511 | function buildControls() { 512 | window.addEventListener("keydown", function(e) { 513 | controls[e.key] = true; 514 | }); 515 | 516 | window.addEventListener("keyup", function(e) { 517 | controls[e.key] = false; 518 | }); 519 | 520 | 521 | window.addEventListener("keypress", function(e) { 522 | if(e.key == "h" || e.key == "H") { 523 | document.querySelector(".controls").classList.toggle("active"); 524 | } 525 | if(e.key == "m" || e.key == "M") { 526 | if(focalPowerFunction === 0) focalPowerFunction = 1; 527 | else focalPowerFunction = 0; 528 | 529 | resetCanvas(); 530 | } 531 | 532 | if(e.key == "5") { 533 | // if(layout) { 534 | // cameraFocalDistance = 99; //88; // dv.length(); 535 | // bokehStrength = 0.1; //0.01; 536 | // } else { 537 | // cameraFocalDistance = 88.2; //88; // dv.length(); 538 | // bokehStrength = 0.012; //0.01; 539 | // } 540 | 541 | // layout = !layout; 542 | 543 | // resetCanvas(); 544 | } 545 | }); 546 | } 547 | 548 | function checkControls() { 549 | if(controls["o"]) { 550 | cameraFocalDistance -= 0.6; 551 | console.log("cfd: " + cameraFocalDistance); 552 | resetCanvas(); 553 | } 554 | if(controls["p"]) { 555 | cameraFocalDistance += 0.6; 556 | console.log("cfd: " + cameraFocalDistance); 557 | resetCanvas(); 558 | } 559 | 560 | if(controls["k"]) { 561 | bokehStrength += 0.001; 562 | console.log("bs: " + bokehStrength); 563 | resetCanvas(); 564 | } 565 | if(controls["l"]) { 566 | bokehStrength -= 0.001; 567 | bokehStrength = Math.max(bokehStrength, 0); 568 | console.log("bs: " + bokehStrength); 569 | resetCanvas(); 570 | } 571 | 572 | if(controls["n"]) { 573 | bokehFalloff += 3.5; 574 | console.log("bf: " + bokehFalloff); 575 | } 576 | if(controls["m"]) { 577 | bokehFalloff -= 3.5; 578 | console.log("bf: " + bokehFalloff); 579 | } 580 | 581 | if(controls["v"]) { 582 | exposure += 0.0001; 583 | console.log("exp: " + exposure); 584 | } 585 | if(controls["b"]) { 586 | exposure -= 0.0001; 587 | exposure = Math.max(exposure, 0.0001); 588 | console.log("exp: " + exposure); 589 | } 590 | 591 | if(controls["u"]) { 592 | distanceAttenuation += 0.003; 593 | console.log("da: " + distanceAttenuation); 594 | resetCanvas(); 595 | } 596 | if(controls["i"]) { 597 | distanceAttenuation -= 0.003; 598 | distanceAttenuation = Math.max(distanceAttenuation, 0); 599 | console.log("da: " + distanceAttenuation); 600 | resetCanvas(); 601 | } 602 | } -------------------------------------------------------------------------------- /libs/dependencies/OrbitControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author qiao / https://github.com/qiao 3 | * @author mrdoob / http://mrdoob.com 4 | * @author alteredq / http://alteredqualia.com/ 5 | * @author WestLangley / http://github.com/WestLangley 6 | * @author erich666 / http://erichaines.com 7 | */ 8 | 9 | // This set of controls performs orbiting, dollying (zooming), and panning. 10 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). 11 | // 12 | // Orbit - left mouse / touch: one finger move 13 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish 14 | // Pan - right mouse, or arrow keys / touch: three finger swipe 15 | 16 | THREE.OrbitControls = function ( object, domElement ) { 17 | 18 | this.object = object; 19 | 20 | this.domElement = ( domElement !== undefined ) ? domElement : document; 21 | 22 | // Set to false to disable this control 23 | this.enabled = true; 24 | 25 | // "target" sets the location of focus, where the object orbits around 26 | this.target = new THREE.Vector3(); 27 | 28 | // How far you can dolly in and out ( PerspectiveCamera only ) 29 | this.minDistance = 0; 30 | this.maxDistance = Infinity; 31 | 32 | // How far you can zoom in and out ( OrthographicCamera only ) 33 | this.minZoom = 0; 34 | this.maxZoom = Infinity; 35 | 36 | // How far you can orbit vertically, upper and lower limits. 37 | // Range is 0 to Math.PI radians. 38 | this.minPolarAngle = 1; // radians 39 | this.maxPolarAngle = 2; // radians 40 | 41 | // How far you can orbit horizontally, upper and lower limits. 42 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. 43 | this.minAzimuthAngle = -Infinity; // radians 44 | this.maxAzimuthAngle = +Infinity; // radians 45 | 46 | // Set to true to enable damping (inertia) 47 | // If damping is enabled, you must call controls.update() in your animation loop 48 | this.enableDamping = false; 49 | this.dampingFactor = 0.25; 50 | 51 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. 52 | // Set to false to disable zooming 53 | this.enableZoom = true; 54 | this.zoomSpeed = 1.0; 55 | 56 | // Set to false to disable rotating 57 | this.enableRotate = true; 58 | this.rotateSpeed = 1.0; 59 | 60 | // Set to false to disable panning 61 | this.enablePan = true; 62 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 63 | 64 | // Set to true to automatically rotate around the target 65 | // If auto-rotate is enabled, you must call controls.update() in your animation loop 66 | this.autoRotate = false; 67 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 68 | 69 | // Set to false to disable use of the keys 70 | this.enableKeys = true; 71 | 72 | // The four arrow keys 73 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 74 | 75 | // Mouse buttons 76 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT }; 77 | 78 | // for reset 79 | this.target0 = this.target.clone(); 80 | this.position0 = this.object.position.clone(); 81 | this.zoom0 = this.object.zoom; 82 | 83 | // 84 | // public methods 85 | // 86 | 87 | this.getPolarAngle = function () { 88 | 89 | return spherical.phi; 90 | 91 | }; 92 | 93 | this.getAzimuthalAngle = function () { 94 | 95 | return spherical.theta; 96 | 97 | }; 98 | 99 | this.saveState = function () { 100 | 101 | scope.target0.copy( scope.target ); 102 | scope.position0.copy( scope.object.position ); 103 | scope.zoom0 = scope.object.zoom; 104 | 105 | }; 106 | 107 | this.reset = function () { 108 | 109 | scope.target.copy( scope.target0 ); 110 | scope.object.position.copy( scope.position0 ); 111 | scope.object.zoom = scope.zoom0; 112 | 113 | scope.object.updateProjectionMatrix(); 114 | scope.dispatchEvent( changeEvent ); 115 | 116 | scope.update(); 117 | 118 | state = STATE.NONE; 119 | 120 | }; 121 | 122 | // this method is exposed, but perhaps it would be better if we can make it private... 123 | this.update = function () { 124 | 125 | var offset = new THREE.Vector3(); 126 | 127 | // so camera.up is the orbit axis 128 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); 129 | var quatInverse = quat.clone().inverse(); 130 | 131 | var lastPosition = new THREE.Vector3(); 132 | var lastQuaternion = new THREE.Quaternion(); 133 | 134 | return function update() { 135 | 136 | var position = scope.object.position; 137 | 138 | offset.copy( position ).sub( scope.target ); 139 | 140 | // rotate offset to "y-axis-is-up" space 141 | offset.applyQuaternion( quat ); 142 | 143 | // angle from z-axis around y-axis 144 | spherical.setFromVector3( offset ); 145 | 146 | if ( scope.autoRotate && state === STATE.NONE ) { 147 | 148 | rotateLeft( getAutoRotationAngle() ); 149 | 150 | } 151 | 152 | spherical.theta += sphericalDelta.theta; 153 | spherical.phi += sphericalDelta.phi; 154 | 155 | // restrict theta to be between desired limits 156 | spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) ); 157 | 158 | // restrict phi to be between desired limits 159 | spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) ); 160 | 161 | spherical.makeSafe(); 162 | 163 | 164 | spherical.radius *= scale; 165 | 166 | // restrict radius to be between desired limits 167 | spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) ); 168 | 169 | // move target to panned location 170 | scope.target.add( panOffset ); 171 | 172 | offset.setFromSpherical( spherical ); 173 | 174 | // rotate offset back to "camera-up-vector-is-up" space 175 | offset.applyQuaternion( quatInverse ); 176 | 177 | position.copy( scope.target ).add( offset ); 178 | 179 | scope.object.lookAt( scope.target ); 180 | 181 | if ( scope.enableDamping === true ) { 182 | 183 | sphericalDelta.theta *= ( 1 - scope.dampingFactor ); 184 | sphericalDelta.phi *= ( 1 - scope.dampingFactor ); 185 | 186 | } else { 187 | 188 | sphericalDelta.set( 0, 0, 0 ); 189 | 190 | } 191 | 192 | scale = 1; 193 | panOffset.set( 0, 0, 0 ); 194 | 195 | // update condition is: 196 | // min(camera displacement, camera rotation in radians)^2 > EPS 197 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8 198 | 199 | if ( zoomChanged || 200 | lastPosition.distanceToSquared( scope.object.position ) > EPS || 201 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) { 202 | 203 | scope.dispatchEvent( changeEvent ); 204 | 205 | lastPosition.copy( scope.object.position ); 206 | lastQuaternion.copy( scope.object.quaternion ); 207 | zoomChanged = false; 208 | 209 | return true; 210 | 211 | } 212 | 213 | return false; 214 | 215 | }; 216 | 217 | }(); 218 | 219 | this.dispose = function () { 220 | 221 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false ); 222 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false ); 223 | scope.domElement.removeEventListener( 'wheel', onMouseWheel, false ); 224 | 225 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false ); 226 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false ); 227 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false ); 228 | 229 | document.removeEventListener( 'mousemove', onMouseMove, false ); 230 | document.removeEventListener( 'mouseup', onMouseUp, false ); 231 | 232 | window.removeEventListener( 'keydown', onKeyDown, false ); 233 | 234 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? 235 | 236 | }; 237 | 238 | // 239 | // internals 240 | // 241 | 242 | var scope = this; 243 | 244 | var changeEvent = { type: 'change' }; 245 | var startEvent = { type: 'start' }; 246 | var endEvent = { type: 'end' }; 247 | 248 | var STATE = { NONE: - 1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY: 4, TOUCH_PAN: 5 }; 249 | 250 | var state = STATE.NONE; 251 | 252 | var EPS = 0.000001; 253 | 254 | // current position in spherical coordinates 255 | var spherical = new THREE.Spherical(); 256 | var sphericalDelta = new THREE.Spherical(); 257 | 258 | var scale = 1; 259 | var panOffset = new THREE.Vector3(); 260 | var zoomChanged = false; 261 | 262 | var rotateStart = new THREE.Vector2(); 263 | var rotateEnd = new THREE.Vector2(); 264 | var rotateDelta = new THREE.Vector2(); 265 | 266 | var panStart = new THREE.Vector2(); 267 | var panEnd = new THREE.Vector2(); 268 | var panDelta = new THREE.Vector2(); 269 | 270 | var dollyStart = new THREE.Vector2(); 271 | var dollyEnd = new THREE.Vector2(); 272 | var dollyDelta = new THREE.Vector2(); 273 | 274 | function getAutoRotationAngle() { 275 | 276 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 277 | 278 | } 279 | 280 | function getZoomScale() { 281 | 282 | return Math.pow( 0.95, scope.zoomSpeed ); 283 | 284 | } 285 | 286 | function rotateLeft( angle ) { 287 | 288 | sphericalDelta.theta -= angle; 289 | 290 | } 291 | 292 | this.rotate = function(angle) { 293 | rotateLeft(angle); 294 | scope.dispatchEvent( changeEvent ); 295 | } 296 | 297 | function rotateUp( angle ) { 298 | 299 | sphericalDelta.phi -= angle; 300 | 301 | } 302 | 303 | var panLeft = function () { 304 | 305 | var v = new THREE.Vector3(); 306 | 307 | return function panLeft( distance, objectMatrix ) { 308 | 309 | v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix 310 | v.multiplyScalar( - distance ); 311 | 312 | panOffset.add( v ); 313 | 314 | }; 315 | 316 | }(); 317 | 318 | var panUp = function () { 319 | 320 | var v = new THREE.Vector3(); 321 | 322 | return function panUp( distance, objectMatrix ) { 323 | 324 | v.setFromMatrixColumn( objectMatrix, 1 ); // get Y column of objectMatrix 325 | v.multiplyScalar( distance ); 326 | 327 | panOffset.add( v ); 328 | 329 | }; 330 | 331 | }(); 332 | 333 | // deltaX and deltaY are in pixels; right and down are positive 334 | var pan = function () { 335 | 336 | var offset = new THREE.Vector3(); 337 | 338 | return function pan( deltaX, deltaY ) { 339 | 340 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 341 | 342 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 343 | 344 | // perspective 345 | var position = scope.object.position; 346 | offset.copy( position ).sub( scope.target ); 347 | var targetDistance = offset.length(); 348 | 349 | // half of the fov is center to top of screen 350 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); 351 | 352 | // we actually don't use screenWidth, since perspective camera is fixed to screen height 353 | panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); 354 | panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); 355 | 356 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 357 | 358 | // orthographic 359 | panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); 360 | panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); 361 | 362 | } else { 363 | 364 | // camera neither orthographic nor perspective 365 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 366 | scope.enablePan = false; 367 | 368 | } 369 | 370 | }; 371 | 372 | }(); 373 | 374 | function dollyIn( dollyScale ) { 375 | 376 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 377 | 378 | scale /= dollyScale; 379 | 380 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 381 | 382 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) ); 383 | scope.object.updateProjectionMatrix(); 384 | zoomChanged = true; 385 | 386 | } else { 387 | 388 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 389 | scope.enableZoom = false; 390 | 391 | } 392 | 393 | } 394 | 395 | function dollyOut( dollyScale ) { 396 | 397 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 398 | 399 | scale *= dollyScale; 400 | 401 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 402 | 403 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) ); 404 | scope.object.updateProjectionMatrix(); 405 | zoomChanged = true; 406 | 407 | } else { 408 | 409 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 410 | scope.enableZoom = false; 411 | 412 | } 413 | 414 | } 415 | 416 | // 417 | // event callbacks - update the object state 418 | // 419 | 420 | function handleMouseDownRotate( event ) { 421 | 422 | //console.log( 'handleMouseDownRotate' ); 423 | 424 | rotateStart.set( event.clientX, event.clientY ); 425 | 426 | } 427 | 428 | function handleMouseDownDolly( event ) { 429 | 430 | //console.log( 'handleMouseDownDolly' ); 431 | 432 | dollyStart.set( event.clientX, event.clientY ); 433 | 434 | } 435 | 436 | function handleMouseDownPan( event ) { 437 | 438 | //console.log( 'handleMouseDownPan' ); 439 | 440 | panStart.set( event.clientX, event.clientY ); 441 | 442 | } 443 | 444 | function handleMouseMoveRotate( event ) { 445 | 446 | //console.log( 'handleMouseMoveRotate' ); 447 | 448 | rotateEnd.set( event.clientX, event.clientY ); 449 | rotateDelta.subVectors( rotateEnd, rotateStart ); 450 | 451 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 452 | 453 | // rotating across whole screen goes 360 degrees around 454 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 455 | 456 | // rotating up and down along whole screen attempts to go 360, but limited to 180 457 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 458 | 459 | rotateStart.copy( rotateEnd ); 460 | 461 | scope.update(); 462 | 463 | } 464 | 465 | function handleMouseMoveDolly( event ) { 466 | 467 | //console.log( 'handleMouseMoveDolly' ); 468 | 469 | dollyEnd.set( event.clientX, event.clientY ); 470 | 471 | dollyDelta.subVectors( dollyEnd, dollyStart ); 472 | 473 | if ( dollyDelta.y > 0 ) { 474 | 475 | dollyIn( getZoomScale() ); 476 | 477 | } else if ( dollyDelta.y < 0 ) { 478 | 479 | dollyOut( getZoomScale() ); 480 | 481 | } 482 | 483 | dollyStart.copy( dollyEnd ); 484 | 485 | scope.update(); 486 | 487 | } 488 | 489 | function handleMouseMovePan( event ) { 490 | 491 | //console.log( 'handleMouseMovePan' ); 492 | 493 | panEnd.set( event.clientX, event.clientY ); 494 | 495 | panDelta.subVectors( panEnd, panStart ); 496 | 497 | pan( panDelta.x, panDelta.y ); 498 | 499 | panStart.copy( panEnd ); 500 | 501 | scope.update(); 502 | 503 | } 504 | 505 | function handleMouseUp( event ) { 506 | 507 | // console.log( 'handleMouseUp' ); 508 | 509 | } 510 | 511 | function handleMouseWheel( event ) { 512 | 513 | // console.log( 'handleMouseWheel' ); 514 | 515 | if ( event.deltaY < 0 ) { 516 | 517 | dollyOut( getZoomScale() ); 518 | 519 | } else if ( event.deltaY > 0 ) { 520 | 521 | dollyIn( getZoomScale() ); 522 | 523 | } 524 | 525 | scope.update(); 526 | 527 | } 528 | 529 | function handleKeyDown( event ) { 530 | 531 | //console.log( 'handleKeyDown' ); 532 | 533 | switch ( event.keyCode ) { 534 | 535 | case scope.keys.UP: 536 | pan( 0, scope.keyPanSpeed ); 537 | scope.update(); 538 | break; 539 | 540 | case scope.keys.BOTTOM: 541 | pan( 0, - scope.keyPanSpeed ); 542 | scope.update(); 543 | break; 544 | 545 | case scope.keys.LEFT: 546 | pan( scope.keyPanSpeed, 0 ); 547 | scope.update(); 548 | break; 549 | 550 | case scope.keys.RIGHT: 551 | pan( - scope.keyPanSpeed, 0 ); 552 | scope.update(); 553 | break; 554 | 555 | } 556 | 557 | } 558 | 559 | function handleTouchStartRotate( event ) { 560 | 561 | //console.log( 'handleTouchStartRotate' ); 562 | 563 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 564 | 565 | } 566 | 567 | function handleTouchStartDolly( event ) { 568 | 569 | //console.log( 'handleTouchStartDolly' ); 570 | 571 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 572 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 573 | 574 | var distance = Math.sqrt( dx * dx + dy * dy ); 575 | 576 | dollyStart.set( 0, distance ); 577 | 578 | } 579 | 580 | function handleTouchStartPan( event ) { 581 | 582 | //console.log( 'handleTouchStartPan' ); 583 | 584 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 585 | 586 | } 587 | 588 | function handleTouchMoveRotate( event ) { 589 | 590 | //console.log( 'handleTouchMoveRotate' ); 591 | 592 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 593 | rotateDelta.subVectors( rotateEnd, rotateStart ); 594 | 595 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 596 | 597 | // rotating across whole screen goes 360 degrees around 598 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 599 | 600 | // rotating up and down along whole screen attempts to go 360, but limited to 180 601 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 602 | 603 | rotateStart.copy( rotateEnd ); 604 | 605 | scope.update(); 606 | 607 | } 608 | 609 | function handleTouchMoveDolly( event ) { 610 | 611 | //console.log( 'handleTouchMoveDolly' ); 612 | 613 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 614 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 615 | 616 | var distance = Math.sqrt( dx * dx + dy * dy ); 617 | 618 | dollyEnd.set( 0, distance ); 619 | 620 | dollyDelta.subVectors( dollyEnd, dollyStart ); 621 | 622 | if ( dollyDelta.y > 0 ) { 623 | 624 | dollyOut( getZoomScale() ); 625 | 626 | } else if ( dollyDelta.y < 0 ) { 627 | 628 | dollyIn( getZoomScale() ); 629 | 630 | } 631 | 632 | dollyStart.copy( dollyEnd ); 633 | 634 | scope.update(); 635 | 636 | } 637 | 638 | function handleTouchMovePan( event ) { 639 | 640 | //console.log( 'handleTouchMovePan' ); 641 | 642 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 643 | 644 | panDelta.subVectors( panEnd, panStart ); 645 | 646 | pan( panDelta.x, panDelta.y ); 647 | 648 | panStart.copy( panEnd ); 649 | 650 | scope.update(); 651 | 652 | } 653 | 654 | function handleTouchEnd( event ) { 655 | 656 | //console.log( 'handleTouchEnd' ); 657 | 658 | } 659 | 660 | // 661 | // event handlers - FSM: listen for events and reset state 662 | // 663 | 664 | function onMouseDown( event ) { 665 | 666 | if ( scope.enabled === false ) return; 667 | 668 | event.preventDefault(); 669 | 670 | if ( event.button === scope.mouseButtons.ORBIT ) { 671 | 672 | if ( scope.enableRotate === false ) return; 673 | 674 | handleMouseDownRotate( event ); 675 | 676 | state = STATE.ROTATE; 677 | 678 | } else if ( event.button === scope.mouseButtons.ZOOM ) { 679 | 680 | if ( scope.enableZoom === false ) return; 681 | 682 | handleMouseDownDolly( event ); 683 | 684 | state = STATE.DOLLY; 685 | 686 | } else if ( event.button === scope.mouseButtons.PAN ) { 687 | 688 | if ( scope.enablePan === false ) return; 689 | 690 | handleMouseDownPan( event ); 691 | 692 | state = STATE.PAN; 693 | 694 | } 695 | 696 | if ( state !== STATE.NONE ) { 697 | 698 | document.addEventListener( 'mousemove', onMouseMove, false ); 699 | document.addEventListener( 'mouseup', onMouseUp, false ); 700 | 701 | scope.dispatchEvent( startEvent ); 702 | 703 | } 704 | 705 | } 706 | 707 | function onMouseMove( event ) { 708 | 709 | if ( scope.enabled === false ) return; 710 | 711 | event.preventDefault(); 712 | 713 | if ( state === STATE.ROTATE ) { 714 | 715 | if ( scope.enableRotate === false ) return; 716 | 717 | handleMouseMoveRotate( event ); 718 | 719 | } else if ( state === STATE.DOLLY ) { 720 | 721 | if ( scope.enableZoom === false ) return; 722 | 723 | handleMouseMoveDolly( event ); 724 | 725 | } else if ( state === STATE.PAN ) { 726 | 727 | if ( scope.enablePan === false ) return; 728 | 729 | handleMouseMovePan( event ); 730 | 731 | } 732 | 733 | } 734 | 735 | function onMouseUp( event ) { 736 | 737 | if ( scope.enabled === false ) return; 738 | 739 | handleMouseUp( event ); 740 | 741 | document.removeEventListener( 'mousemove', onMouseMove, false ); 742 | document.removeEventListener( 'mouseup', onMouseUp, false ); 743 | 744 | scope.dispatchEvent( endEvent ); 745 | 746 | state = STATE.NONE; 747 | 748 | } 749 | 750 | function onMouseWheel( event ) { 751 | 752 | if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return; 753 | 754 | event.preventDefault(); 755 | event.stopPropagation(); 756 | 757 | handleMouseWheel( event ); 758 | 759 | scope.dispatchEvent( startEvent ); // not sure why these are here... 760 | scope.dispatchEvent( endEvent ); 761 | 762 | } 763 | 764 | function onKeyDown( event ) { 765 | 766 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return; 767 | 768 | handleKeyDown( event ); 769 | 770 | } 771 | 772 | function onTouchStart( event ) { 773 | 774 | if ( scope.enabled === false ) return; 775 | 776 | switch ( event.touches.length ) { 777 | 778 | case 1: // one-fingered touch: rotate 779 | 780 | if ( scope.enableRotate === false ) return; 781 | 782 | handleTouchStartRotate( event ); 783 | 784 | state = STATE.TOUCH_ROTATE; 785 | 786 | break; 787 | 788 | case 2: // two-fingered touch: dolly 789 | 790 | if ( scope.enableZoom === false ) return; 791 | 792 | handleTouchStartDolly( event ); 793 | 794 | state = STATE.TOUCH_DOLLY; 795 | 796 | break; 797 | 798 | case 3: // three-fingered touch: pan 799 | 800 | if ( scope.enablePan === false ) return; 801 | 802 | handleTouchStartPan( event ); 803 | 804 | state = STATE.TOUCH_PAN; 805 | 806 | break; 807 | 808 | default: 809 | 810 | state = STATE.NONE; 811 | 812 | } 813 | 814 | if ( state !== STATE.NONE ) { 815 | 816 | scope.dispatchEvent( startEvent ); 817 | 818 | } 819 | 820 | } 821 | 822 | function onTouchMove( event ) { 823 | 824 | if ( scope.enabled === false ) return; 825 | 826 | event.preventDefault(); 827 | event.stopPropagation(); 828 | 829 | switch ( event.touches.length ) { 830 | 831 | case 1: // one-fingered touch: rotate 832 | 833 | if ( scope.enableRotate === false ) return; 834 | if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?... 835 | 836 | handleTouchMoveRotate( event ); 837 | 838 | break; 839 | 840 | case 2: // two-fingered touch: dolly 841 | 842 | if ( scope.enableZoom === false ) return; 843 | if ( state !== STATE.TOUCH_DOLLY ) return; // is this needed?... 844 | 845 | handleTouchMoveDolly( event ); 846 | 847 | break; 848 | 849 | case 3: // three-fingered touch: pan 850 | 851 | if ( scope.enablePan === false ) return; 852 | if ( state !== STATE.TOUCH_PAN ) return; // is this needed?... 853 | 854 | handleTouchMovePan( event ); 855 | 856 | break; 857 | 858 | default: 859 | 860 | state = STATE.NONE; 861 | 862 | } 863 | 864 | } 865 | 866 | function onTouchEnd( event ) { 867 | 868 | if ( scope.enabled === false ) return; 869 | 870 | handleTouchEnd( event ); 871 | 872 | scope.dispatchEvent( endEvent ); 873 | 874 | state = STATE.NONE; 875 | 876 | } 877 | 878 | function onContextMenu( event ) { 879 | 880 | event.preventDefault(); 881 | 882 | } 883 | 884 | // 885 | 886 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false ); 887 | 888 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false ); 889 | scope.domElement.addEventListener( 'wheel', onMouseWheel, false ); 890 | 891 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false ); 892 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false ); 893 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false ); 894 | 895 | window.addEventListener( 'keydown', onKeyDown, false ); 896 | 897 | // force an update at start 898 | 899 | this.update(); 900 | 901 | }; 902 | 903 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 904 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls; 905 | 906 | Object.defineProperties( THREE.OrbitControls.prototype, { 907 | 908 | center: { 909 | 910 | get: function () { 911 | 912 | console.warn( 'THREE.OrbitControls: .center has been renamed to .target' ); 913 | return this.target; 914 | 915 | } 916 | 917 | }, 918 | 919 | // backward compatibility 920 | 921 | noZoom: { 922 | 923 | get: function () { 924 | 925 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); 926 | return ! this.enableZoom; 927 | 928 | }, 929 | 930 | set: function ( value ) { 931 | 932 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); 933 | this.enableZoom = ! value; 934 | 935 | } 936 | 937 | }, 938 | 939 | noRotate: { 940 | 941 | get: function () { 942 | 943 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); 944 | return ! this.enableRotate; 945 | 946 | }, 947 | 948 | set: function ( value ) { 949 | 950 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); 951 | this.enableRotate = ! value; 952 | 953 | } 954 | 955 | }, 956 | 957 | noPan: { 958 | 959 | get: function () { 960 | 961 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); 962 | return ! this.enablePan; 963 | 964 | }, 965 | 966 | set: function ( value ) { 967 | 968 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); 969 | this.enablePan = ! value; 970 | 971 | } 972 | 973 | }, 974 | 975 | noKeys: { 976 | 977 | get: function () { 978 | 979 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); 980 | return ! this.enableKeys; 981 | 982 | }, 983 | 984 | set: function ( value ) { 985 | 986 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); 987 | this.enableKeys = ! value; 988 | 989 | } 990 | 991 | }, 992 | 993 | staticMoving: { 994 | 995 | get: function () { 996 | 997 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); 998 | return ! this.enableDamping; 999 | 1000 | }, 1001 | 1002 | set: function ( value ) { 1003 | 1004 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); 1005 | this.enableDamping = ! value; 1006 | 1007 | } 1008 | 1009 | }, 1010 | 1011 | dynamicDampingFactor: { 1012 | 1013 | get: function () { 1014 | 1015 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); 1016 | return this.dampingFactor; 1017 | 1018 | }, 1019 | 1020 | set: function ( value ) { 1021 | 1022 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); 1023 | this.dampingFactor = value; 1024 | 1025 | } 1026 | 1027 | } 1028 | 1029 | } ); 1030 | -------------------------------------------------------------------------------- /libs/scenes/procedural-city.js: -------------------------------------------------------------------------------- 1 | function setGlobals() { 2 | let distanceMult = 1.25; 3 | // cameraPosition = new THREE.Vector3(14.5 * distanceMult, 10.4 * distanceMult, 28.9 * distanceMult); 4 | cameraPosition = new THREE.Vector3(-29, 0, 5); 5 | cameraTarget = new THREE.Vector3(fieldSize * 0.05, 0.5, fieldSize * 0.05); 6 | 7 | let dv = new THREE.Vector3(cameraPosition.x, cameraPosition.y, cameraPosition.z); 8 | dv.sub(cameraTarget); 9 | cameraFocalDistance = dv.length(); //33.8; 10 | 11 | 12 | minimumLineSize = 0.005; 13 | 14 | // bokehStrength = 0.018; 15 | bokehStrength = 0.01; 16 | focalPowerFunction = 1; 17 | exposure = 0.0009; 18 | 19 | useBokehTexture = true; 20 | bokehTexturePath = "assets/bokeh/pentagon.png"; 21 | quadsTexturePath = "assets/textures/texture2.png"; 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | let rotationFunction = []; 30 | let cumulativeValue = 0; 31 | for(let i = 0; i < framesCount * motionBlurFrames; i++) { 32 | let rt = i / (framesCount * motionBlurFrames); 33 | 34 | let value = 0; 35 | 36 | // rt += 0.85; 37 | rt = rt % 1; 38 | 39 | if (rt < 0.15) { 40 | let t = (rt) / 0.15; 41 | value = smoothstepAnim(0, 1.6, 0, 1, t); 42 | rotationFunction.push(value); 43 | } else if (rt < 0.3) { 44 | let t = (rt - 0.15) / 0.15; 45 | value = smoothstepAnim(1.6, 0.05, 0, 1, t); 46 | rotationFunction.push(value); 47 | } else if (rt < 0.7) { 48 | value = 0.05; 49 | rotationFunction.push(value); 50 | } else if (rt < 0.85) { 51 | let t = (rt - 0.7) / 0.15; 52 | value = smoothstepAnim(0.05, 1, 0, 1, t); 53 | rotationFunction.push(value); 54 | } else { 55 | let t = (rt - 0.85) / 0.15; 56 | value = smoothstepAnim(1, 0, 0, 1, t); 57 | rotationFunction.push(value); 58 | } 59 | 60 | cumulativeValue += value; 61 | rotationIntegral[i] = cumulativeValue; 62 | } 63 | 64 | for(let i = 0; i < framesCount * motionBlurFrames; i++) { 65 | rotationIntegral[i] /= cumulativeValue; 66 | } 67 | 68 | 69 | 70 | // let dn = Date.now(); 71 | // console.log(dn); 72 | // Utils.setRandomSeed(dn.toString()); 73 | } 74 | 75 | let fieldSize = 110; 76 | let rotateOnce = false; 77 | let rotationIntegral = []; 78 | // use this function to create the lines that will be rendered 79 | function createScene(frame) { 80 | 81 | Utils.setRandomSeed("1565731290467"); 82 | 83 | let field = []; 84 | let field2 = []; 85 | let fieldYOffset = []; 86 | 87 | for(let i = 0; i < fieldSize; i++) { 88 | field.push([]); 89 | field2.push([]); 90 | fieldYOffset.push([]); 91 | for(let j = 0; j < fieldSize; j++) { 92 | field[i].push(0); 93 | fieldYOffset[i].push(0); 94 | field2[i].push(0); 95 | } 96 | } 97 | 98 | 99 | // // required for motion blur to work 100 | // preventOnControlsChangeReset = true; 101 | // controls.rotate(Math.sin(frame / framesCount * Math.PI * 2) * 0.1); 102 | // // // // if(!rotateOnce) { 103 | // // // // controls.rotate(2.85); 104 | // // // // rotateOnce = true; 105 | // // // // } 106 | // // // controls.rotate(2.85); 107 | 108 | // ***************** camera controls 109 | let rt = frame / framesCount; 110 | rt = 0.25; 111 | // rt = 0.44; 112 | let yoa = 0; 113 | let bokehS = 0.125; 114 | if(rt < 0.15) { 115 | // do nothing 116 | bokehStrength = 0.005 + (1) * bokehS; 117 | yoa = 1; 118 | } else if(rt < 0.3) { 119 | let t = (rt-0.15) / 0.15; 120 | t = smoothstepAnim(0, 1, 0, 1, smoothstepAnim(0, 1, 0, 1, t)); 121 | bokehStrength = 0.005 + (1-t) * bokehS; 122 | yoa = 1 - t; 123 | } else if (rt < 0.6) { 124 | let t = (rt-0.3) / 0.3; 125 | yoa = 0; 126 | bokehStrength = 0.005 + (t) * 0.01; 127 | } else { 128 | let t = (rt-0.6) / 0.4; 129 | t = smoothstepAnim(0, 1, 0, 1, smoothstepAnim(0, 1, 0, 1, t)); 130 | yoa = t; 131 | 132 | bokehStrength = 0.005 + 0.01 + (t) * (bokehS - 0.01); 133 | } 134 | 135 | 136 | let cameraLookAt = new THREE.Vector3(fieldSize * 0.05, 0.5, fieldSize * 0.05); 137 | let cameraPos = new THREE.Vector3(-29 - fieldSize * 0.05, 0, 5 - fieldSize * 0.05); 138 | if(rt < 0.1) { 139 | // do nothing 140 | } else if (rt < 0.3) { 141 | let t = (rt - 0.1) / 0.2; 142 | t = smoothstepAnim(0, 1, 0, 1, smoothstepAnim(0, 1, 0, 1, t)); 143 | cameraPos.applyAxisAngle(new THREE.Vector3(0, 0, 1), t * Math.PI * -0.13); 144 | } else if (rt < 0.6) { 145 | cameraPos.applyAxisAngle(new THREE.Vector3(0, 0, 1), 1 * Math.PI * -0.13); 146 | } else { 147 | let t = (rt - 0.6) / 0.4; 148 | t = smoothstepAnim(0, 1, 0, 1, smoothstepAnim(0, 1, 0, 1, t)); 149 | cameraPos.applyAxisAngle(new THREE.Vector3(0, 0, 1), (1-t) * Math.PI * -0.13); 150 | } 151 | let integralT = (frame / framesCount) * Math.PI * 4; 152 | // integralT = -Math.cos((integralT - Math.PI*0.5 - 159/1000)*0.5) + (13679 * integralT) / 10000 - Math.sin(integralT - 159/1000) + 0.4904; 153 | // normalize the integral to be in range [0...1]; 154 | // integralT = (integralT / 17.1895516); 155 | // integralT = -Math.cos((integralT - Math.PI*0.5-17/500) * 0.5) / 5 + (2679*integralT)/2500 - 156 | // Math.sin(integralT - 17/500) + 0.105; 157 | // // normalize the integral to be in range [0...1]; 158 | // integralT = (integralT / 13.46); 159 | 160 | // // original derivative: (sin(x - PI*0.5 - 0.159 + 0.103 + 0.022) + sin((x - PI*0.5 - 0.159 + 0.073 + 0.052)*0.5) * 0.1) + 1.3679 - 0.1873 - 0.110 161 | // integralT = -Math.cos((integralT-Math.PI/2-17/500)/2)/5+(5353*integralT)/5000-Math.sin(integralT-17/500) + 0.105; 162 | // // normalize the integral to be in range [0...1]; 163 | // integralT = (integralT / 13.45); 164 | 165 | 166 | 167 | 168 | // already normalized to be in range [0...1]; 169 | integralT = rotationIntegral[Math.floor(frame * motionBlurFrames)]; 170 | cameraPos.applyAxisAngle(new THREE.Vector3(0, 1, 0), integralT * Math.PI * 2); 171 | 172 | // cameraPos.applyAxisAngle(new THREE.Vector3(0, 1, 0), 173 | // integralT * Math.PI * 2 + (Math.sin(integralT * Math.PI)) * Math.PI*0.175); 174 | cameraPos.add(new THREE.Vector3(fieldSize * 0.05, 0, fieldSize * 0.05)); 175 | // // let savedCameraQuaternion = new THREE.Quaternion().copy(camera.quaternion); 176 | camera.position.set(cameraPos.x, cameraPos.y, cameraPos.z); 177 | camera.lookAt(cameraLookAt); 178 | // ***************** camera controls - END 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | let scalingFactor = 0.1; 199 | 200 | 201 | let minWidth = 5; 202 | let maxWidth = 40; 203 | let step = 20; 204 | let numQuads = 100; 205 | let quadHeight = 10; 206 | for(let i = 0; i < numQuads; i++) { 207 | 208 | if(i > 80) { 209 | minWidth = 10; 210 | maxWidth = 20; 211 | step = 5; 212 | quadHeight = 4; 213 | } 214 | 215 | 216 | 217 | let wt = Utils.rand(); 218 | let ht = Utils.rand(); 219 | 220 | let width = Math.floor(minWidth * wt + maxWidth * (1-wt)); 221 | let height = Math.floor(minWidth * ht + maxWidth * (1-ht)); 222 | 223 | width = width - (width % step); 224 | height = height - (height % step); 225 | 226 | let xt = Math.floor(Utils.rand() * fieldSize); 227 | let zt = Math.floor(Utils.rand() * fieldSize); 228 | 229 | let xs = Math.max(Math.floor(xt - width * 0.5), 0); 230 | let zs = Math.max(Math.floor(zt - height * 0.5), 0); 231 | 232 | let xe = Math.min(Math.floor(xt + width * 0.5), fieldSize); 233 | let ze = Math.min(Math.floor(zt + height * 0.5), fieldSize); 234 | 235 | 236 | let cellHeight = Math.floor(Utils.rand() * quadHeight); 237 | let cellOffset = (Utils.rand() * 2 - 1) * 25 * yoa; 238 | 239 | // now add the coefficients to each cell 240 | for(let x = xs; x < xe; x++) { 241 | for(let z = zs; z < ze; z++) { 242 | field[z][x] += cellHeight; // Math.floor(Utils.rand() * quadHeight); 243 | fieldYOffset[z][x] += cellOffset; 244 | } 245 | } 246 | } 247 | 248 | 249 | let chimneyDrawn = 0; 250 | let flagsDrawn = 0; 251 | let samples = Math.floor(fieldSize * fieldSize * 0.5); 252 | let maxSampleSquareSize = 20; 253 | for(let i = 0; i < samples; i++) { 254 | let xs = Math.floor(Utils.rand() * fieldSize); 255 | let zs = Math.floor(Utils.rand() * fieldSize); 256 | let ys = field[zs][xs]; 257 | let yo = fieldYOffset[zs][xs]; 258 | 259 | let xw = 1; 260 | let zw = 1; 261 | 262 | 263 | // this sample is occupied 264 | if(field2[zs][xs] !== 0) continue; 265 | 266 | 267 | // for(let j = 1; j < maxSampleSquareSize; j++) { 268 | // if((xs + j) >= fieldSize) continue; 269 | 270 | // if(field[zs][xs + j] == ys && field2[zs][xs + j] === 0 ) xw++; 271 | // } 272 | 273 | // for(let j = 1; j < maxSampleSquareSize; j++) { 274 | // if((zs + j) >= fieldSize) continue; 275 | 276 | // if(field[zs + j][xs] == ys && field2[zs + j][xs] === 0 ) zw++; 277 | // } 278 | 279 | function possible(xs, zs, xw, zw) { 280 | 281 | 282 | for(let x = 0; x < xw; x++) { 283 | for(let z = 0; z < zw; z++) { 284 | if( field[zs + z][xs + x] !== ys || 285 | field2[zs + z][xs + x] !== 0) return false; 286 | } 287 | } 288 | 289 | return true; 290 | } 291 | 292 | let bestArea = 1; 293 | let bestAreaX = 1; 294 | let bestAreaZ = 1; 295 | for(let x = 1; x < maxSampleSquareSize; x++) { 296 | for(let z = 1; z < maxSampleSquareSize; z++) { 297 | let area = (x+1) * (z+1); 298 | let xt = x; 299 | let zt = z; 300 | 301 | if((xs + xt) > (fieldSize - 1)) { 302 | xt = fieldSize - 1 - xs; 303 | } 304 | if((zs + zt) > (fieldSize - 1)) { 305 | zt = fieldSize - 1 - zs; 306 | } 307 | 308 | if(possible(xs, zs, xt, zt) && area > bestArea) { 309 | bestArea = area; 310 | bestAreaX = xt; 311 | bestAreaZ = zt; 312 | } 313 | } 314 | } 315 | 316 | xw = bestAreaX; 317 | zw = bestAreaZ; 318 | 319 | for(let xx = 0; xx < xw; xx++) { 320 | for(let zz = 0; zz < zw; zz++) { 321 | field2[zs + zz][xs + xx] = 1; 322 | } 323 | } 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | let color = { 332 | x: 0.045, 333 | y: 0.045, 334 | z: 0.045, 335 | }; 336 | let quadColor = { x: color.x, y: color.y, z: color.z }; 337 | 338 | if(Utils.rand() > (1 - zs * (1 / fieldSize))) { 339 | color = { 340 | x: 0.1 * (1 + Utils.rand()), 341 | y: 0.03, 342 | z: 0.01, 343 | }; 344 | quadColor = { x: color.x, y: color.y * 1.75, z: color.z }; 345 | } 346 | 347 | 348 | // **************** flags 349 | if(Utils.rand() > 0.5 && ys > 13 && bestArea > 15 && bestArea < 45 && flagsDrawn < 4) { 350 | let quad = new Quad(0,0,0, 0.5, 0, 1, 0.5); 351 | let size = Math.min(xw, zw) * 1.5; 352 | let quadColorMult = 150 * Math.pow(size, 1.5); 353 | 354 | flagsDrawn++; 355 | 356 | quad = quad 357 | .scale(size * scalingFactor) 358 | // .rotate(1, 0, 0, Math.PI * 0.5) 359 | .color( 360 | quadColor.x * quadColorMult, 361 | quadColor.y * quadColorMult, 362 | quadColor.z * quadColorMult) 363 | .translate((xs + size * 0.5) * scalingFactor, (ys + yo + size * 0.5) * scalingFactor, (zs - size * 0.0) * scalingFactor) 364 | 365 | quads.push(quad); 366 | } 367 | // **************** flags - END 368 | 369 | 370 | lines.push({ 371 | x1: xs * scalingFactor, 372 | y1: (ys+yo) * scalingFactor, 373 | z1: zs * scalingFactor, 374 | 375 | x2: xs * scalingFactor, 376 | y2: (ys+yo) * scalingFactor, 377 | z2: (zs+zw) * scalingFactor, 378 | 379 | c1r: color.x, 380 | c1g: color.y, 381 | c1b: color.z, 382 | 383 | c2r: color.x, 384 | c2g: color.y, 385 | c2b: color.z, 386 | }); 387 | 388 | lines.push({ 389 | x1: xs * scalingFactor, 390 | y1: (ys+yo) * scalingFactor, 391 | z1: zs * scalingFactor, 392 | 393 | x2: (xs+xw) * scalingFactor, 394 | y2: (ys+yo) * scalingFactor, 395 | z2: (zs) * scalingFactor, 396 | 397 | c1r: color.x, 398 | c1g: color.y, 399 | c1b: color.z, 400 | 401 | c2r: color.x, 402 | c2g: color.y, 403 | c2b: color.z, 404 | }); 405 | 406 | 407 | 408 | 409 | // "X" ceiling ? 410 | if(Utils.rand() > 0.875 && Math.abs(xw - zw) < 3 && (xw + zw) > 5) { 411 | lines.push({ 412 | x1: xs * scalingFactor, 413 | y1: (ys+yo) * scalingFactor, 414 | z1: zs * scalingFactor, 415 | 416 | x2: (xs+xw) * scalingFactor, 417 | y2: (ys+yo) * scalingFactor, 418 | z2: (zs+zw) * scalingFactor, 419 | 420 | c1r: color.x, 421 | c1g: color.y, 422 | c1b: color.z, 423 | 424 | c2r: color.x, 425 | c2g: color.y, 426 | c2b: color.z, 427 | }); 428 | 429 | lines.push({ 430 | x1: xs * scalingFactor, 431 | y1: (ys+yo) * scalingFactor, 432 | z1: (zs+zw) * scalingFactor, 433 | 434 | x2: (xs+xw) * scalingFactor, 435 | y2: (ys+yo) * scalingFactor, 436 | z2: (zs) * scalingFactor, 437 | 438 | c1r: color.x, 439 | c1g: color.y, 440 | c1b: color.z, 441 | 442 | c2r: color.x, 443 | c2g: color.y, 444 | c2b: color.z, 445 | }); 446 | } 447 | 448 | 449 | 450 | // check if you need to make an upward wall 451 | if((zs+zw) <= (fieldSize - 1)) { 452 | let nyVal = field[zs+zw][xs]; 453 | let cm = nyVal < ys ? 0.25 : 1; 454 | 455 | if(nyVal !== ys) { 456 | lines.push({ 457 | x1: xs * scalingFactor, 458 | y1: (ys+yo) * scalingFactor, 459 | z1: (zs+zw) * scalingFactor, 460 | 461 | x2: xs * scalingFactor, 462 | y2: (nyVal+yo) * scalingFactor, 463 | z2: (zs+zw) * scalingFactor, 464 | 465 | c1r: color.x * cm, 466 | c1g: color.y * cm, 467 | c1b: color.z * cm, 468 | 469 | c2r: color.x * cm, 470 | c2g: color.y * cm, 471 | c2b: color.z * cm, 472 | }); 473 | } 474 | 475 | // make windows 476 | if(Utils.rand() > 0.75) {// (Utils.rand() > 0.65 && ys > 5) || (Utils.rand() > 0.9)) { 477 | let yDir = nyVal - ys < 0 ? -1 : 1; 478 | let yDelta = Math.floor(Math.abs(nyVal - ys)); 479 | let windows = Math.floor(Utils.rand() * 10); 480 | for(let r = 0; r < xw; r++) { 481 | for(let r2 = 0; r2 < yDelta; r2++) { 482 | if(Utils.rand() > 0.25) continue; 483 | 484 | windows--; 485 | if(windows >= 0) 486 | makeWindow(xs, ys + yo + r2 * yDir, zs + r, color, cm, scalingFactor, "z"); 487 | } 488 | } 489 | } 490 | } 491 | 492 | // check if you need to make an upward wall 493 | if((xs + xw) <= (fieldSize - 1)) { 494 | let nyVal = field[zs][xs+xw]; 495 | let cm = nyVal < ys ? 0.25 : 1; 496 | 497 | if(nyVal !== ys) { 498 | lines.push({ 499 | x1: (xs+xw) * scalingFactor, 500 | y1: (ys+yo) * scalingFactor, 501 | z1: (zs) * scalingFactor, 502 | 503 | x2: (xs+xw) * scalingFactor, 504 | y2: (nyVal+yo) * scalingFactor, 505 | z2: (zs) * scalingFactor, 506 | 507 | c1r: color.x * cm, 508 | c1g: color.y * cm, 509 | c1b: color.z * cm, 510 | 511 | c2r: color.x * cm, 512 | c2g: color.y * cm, 513 | c2b: color.z * cm, 514 | }); 515 | 516 | 517 | // make windows 518 | if(Utils.rand() > 0.75) {// (Utils.rand() > 0.65 && ys > 5) || (Utils.rand() > 0.9)) { 519 | let yDir = nyVal - ys < 0 ? -1 : 1; 520 | let yDelta = Math.floor(Math.abs(nyVal - ys)); 521 | let windows = Math.floor(Utils.rand() * 10); 522 | for(let r = 0; r < xw; r++) { 523 | for(let r2 = 0; r2 < yDelta; r2++) { 524 | if(Utils.rand() > 0.25) continue; 525 | 526 | windows--; 527 | if(windows >= 0) 528 | makeWindow(xs + r, ys + yo + r2 * yDir, zs, color, cm, scalingFactor, "x"); 529 | } 530 | } 531 | } 532 | 533 | } 534 | } 535 | 536 | 537 | // *************** create enforced walls 538 | if(Utils.rand() > 0.8) { 539 | if((xs + xw) <= (fieldSize - 1)) { 540 | 541 | let nyVal = field[zs][xs+xw]; 542 | if(nyVal !== ys) { 543 | let ic = Math.floor(2.5 * xw * Utils.rand()); 544 | let cm = nyVal < ys ? 0.25 : 1; 545 | 546 | for(let ii = 0; ii < ic; ii++) { 547 | lines.push({ 548 | x1: (xs + (ii/ic) * xw) * scalingFactor, 549 | y1: (ys + yo) * scalingFactor, 550 | z1: (zs) * scalingFactor, 551 | 552 | x2: (xs + (ii/ic) * xw) * scalingFactor, 553 | y2: (nyVal + yo) * scalingFactor, 554 | z2: (zs) * scalingFactor, 555 | 556 | c1r: color.x * 3 * cm, 557 | c1g: color.y * 3 * cm, 558 | c1b: color.z * 3 * cm, 559 | 560 | c2r: color.x * 3 * cm, 561 | c2g: color.y * 3 * cm, 562 | c2b: color.z * 3 * cm, 563 | }); 564 | 565 | // ***************** ceiling 566 | lines.push({ 567 | x1: (xs + (ii/ic) * xw) * scalingFactor, 568 | y1: (ys+yo) * scalingFactor, 569 | z1: (zs) * scalingFactor, 570 | 571 | x2: (xs + (ii/ic) * xw) * scalingFactor, 572 | y2: (ys+yo) * scalingFactor, 573 | z2: (zs + zw) * scalingFactor, 574 | 575 | c1r: color.x * 3, 576 | c1g: color.y * 3, 577 | c1b: color.z * 3, 578 | 579 | c2r: color.x * 3, 580 | c2g: color.y * 3, 581 | c2b: color.z * 3, 582 | }); 583 | // ***************** ceiling - END 584 | } 585 | } 586 | } 587 | 588 | if((zs + zw) <= (fieldSize - 1)) { 589 | 590 | let nyVal = field[zs+zw][xs]; 591 | if(nyVal !== ys) { 592 | let ic = Math.floor(2.5 * zw * Utils.rand()); 593 | let cm = nyVal < ys ? 0.25 : 1; 594 | 595 | for(let ii = 0; ii < ic; ii++) { 596 | lines.push({ 597 | x1: (xs) * scalingFactor, 598 | y1: (ys + yo) * scalingFactor, 599 | z1: (zs + (ii/ic) * zw) * scalingFactor, 600 | 601 | x2: (xs) * scalingFactor, 602 | y2: (nyVal + yo) * scalingFactor, 603 | z2: (zs + (ii/ic) * zw) * scalingFactor, 604 | 605 | c1r: color.x * 3 * cm, 606 | c1g: color.y * 3 * cm, 607 | c1b: color.z * 3 * cm, 608 | 609 | c2r: color.x * 3 * cm, 610 | c2g: color.y * 3 * cm, 611 | c2b: color.z * 3 * cm, 612 | }); 613 | 614 | // lines.push({ 615 | // x1: (xs) * scalingFactor, 616 | // y1: ys * scalingFactor, 617 | // z1: (zs + (ii/ic) * zw) * scalingFactor, 618 | 619 | // x2: (xs + xw) * scalingFactor, 620 | // y2: ys * scalingFactor, 621 | // z2: (zs + (ii/ic) * zw) * scalingFactor, 622 | 623 | // c1r: color.x * 3, 624 | // c1g: color.y * 3, 625 | // c1b: color.z * 3, 626 | 627 | // c2r: color.x * 3, 628 | // c2g: color.y * 3, 629 | // c2b: color.z * 3, 630 | // }); 631 | 632 | } 633 | } 634 | } 635 | } 636 | // *************** create enforced walls - END 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | // ************ glitters in the sky 645 | if(Utils.rand() > 0.95 && ys > 6 && chimneyDrawn < 5) { 646 | 647 | chimneyDrawn++; 648 | let gCount = Math.floor(Utils.rand() * 7) + 4; 649 | let zAcc = 0; 650 | let yt = ys + 1; 651 | 652 | for(let ir = 0; ir < gCount; ir++) { 653 | 654 | let zt = 1; // Utils.rand() > (0.95 / (1 + ir * 1.75)) ? 1 : 0; 655 | zAcc += zt * ir * 0.25; 656 | 657 | let randStart = Utils.rand() * Math.PI * 2; 658 | let xst = Math.sin((frame / framesCount) * Math.PI * 4 + randStart) * (0.2 + ir * 0.3); 659 | let zst = Math.cos((frame / framesCount) * Math.PI * 4 + randStart) * (0.2 + ir * 0.3); 660 | 661 | makeGlitter(xst + xs + zAcc + Utils.rand() * 2, yt + yo + ir * 2, zst + zs + Utils.rand() * 2, color, scalingFactor); 662 | } 663 | } 664 | 665 | if(Utils.rand() > 0.99) { 666 | let randStart = Utils.rand() * Math.PI * 2; 667 | makeGlitter(xs, ys + yo + 5 + Utils.rand() * 20 + Math.sin((frame / framesCount) * Math.PI * 4 + randStart) * 2.5, zs, color, scalingFactor); 668 | } 669 | // ************ glitters in the sky - END 670 | } 671 | 672 | 673 | 674 | 675 | // ******************** ground 676 | let gs = 0.75; 677 | for(let i = 0; i < Math.floor(fieldSize * gs); i++) { 678 | let yp = -0.5; 679 | let cm = Math.abs((i % 2) - 1) * 0.5 + 0.15; 680 | 681 | 682 | 683 | let im = i / gs; 684 | 685 | lines.push({ 686 | x1: (-fieldSize) * scalingFactor, 687 | y1: (yp) * scalingFactor, 688 | z1: (im) * scalingFactor, 689 | 690 | x2: (fieldSize*2) * scalingFactor, 691 | y2: (yp) * scalingFactor, 692 | z2: (im) * scalingFactor, 693 | 694 | weight: 0.05, 695 | 696 | c1r: 0.6 * cm, 697 | c1g: 0.6 * cm, 698 | c1b: 0.6 * cm, 699 | 700 | c2r: 0.6 * cm, 701 | c2g: 0.6 * cm, 702 | c2b: 0.6 * cm, 703 | }); 704 | 705 | lines.push({ 706 | x1: (im) * scalingFactor, 707 | y1: (yp) * scalingFactor, 708 | z1: (-fieldSize) * scalingFactor, 709 | 710 | x2: (im) * scalingFactor, 711 | y2: (yp) * scalingFactor, 712 | z2: (fieldSize*2) * scalingFactor, 713 | 714 | weight: 0.05, 715 | 716 | c1r: 0.6 * cm, 717 | c1g: 0.6 * cm, 718 | c1b: 0.6 * cm, 719 | 720 | c2r: 0.6 * cm, 721 | c2g: 0.6 * cm, 722 | c2b: 0.6 * cm, 723 | }); 724 | } 725 | // ******************** ground - END 726 | 727 | 728 | } 729 | 730 | function makeGlitter(x, y, z, color, scalingFactor) { 731 | for(let it = 0; it < 6; it++) { 732 | 733 | let x1 = 0, x2 = 0, z1 = 0, z2 = 0, y1 = 0, y2 = 0; 734 | let sc = 0.6; 735 | switch(it) { 736 | case 0: 737 | x1 = +1.2 * sc; x2 = 1.6 * sc; break; 738 | case 1: 739 | x1 = -1.2 * sc; x2 = -1.6 * sc; break; 740 | case 2: 741 | z1 = +1.2 * sc; z2 = 1.6 * sc; break; 742 | case 3: 743 | z1 = -1.2 * sc; z2 = -1.6 * sc; break; 744 | case 4: 745 | y1 = +1.2 * sc; y2 = +1.6 * sc; break; 746 | case 5: 747 | y1 = -1.2 * sc; y2 = -1.6 * sc; break; 748 | } 749 | 750 | lines.push({ 751 | x1: (x + x1) * scalingFactor, 752 | y1: (y + y1) * scalingFactor, 753 | z1: (z + z1) * scalingFactor, 754 | 755 | x2: (x + x2) * scalingFactor, 756 | y2: (y + y2) * scalingFactor, 757 | z2: (z + z2) * scalingFactor, 758 | 759 | c1r: color.x * 3, 760 | c1g: color.y * 3, 761 | c1b: color.z * 3, 762 | 763 | c2r: color.x * 3, 764 | c2g: color.y * 3, 765 | c2b: color.z * 3, 766 | }); 767 | } 768 | } 769 | 770 | function makeWindow(x, y, z, color, cm, scalingFactor, dir) { 771 | let xm = 0; 772 | let zm = 0; 773 | if(dir == "x") xm = 1; 774 | if(dir == "z") zm = 1; 775 | 776 | lines.push({ 777 | x1: x * scalingFactor, 778 | y1: y * scalingFactor, 779 | z1: z * scalingFactor, 780 | 781 | x2: (x+0.5*xm) * scalingFactor, 782 | y2: (y) * scalingFactor, 783 | z2: (z+0.5*zm) * scalingFactor, 784 | 785 | c1r: color.x * cm, c1g: color.y * cm, c1b: color.z * cm, 786 | c2r: color.x * cm, c2g: color.y * cm, c2b: color.z * cm, 787 | }); 788 | lines.push({ 789 | x1: x * scalingFactor, 790 | y1: y * scalingFactor, 791 | z1: z * scalingFactor, 792 | 793 | x2: (x) * scalingFactor, 794 | y2: (y-0.5) * scalingFactor, 795 | z2: z * scalingFactor, 796 | 797 | c1r: color.x * cm, c1g: color.y * cm, c1b: color.z * cm, 798 | c2r: color.x * cm, c2g: color.y * cm, c2b: color.z * cm, 799 | }); 800 | lines.push({ 801 | x1: x * scalingFactor, 802 | y1: (y-0.5) * scalingFactor, 803 | z1: z * scalingFactor, 804 | 805 | x2: (x+0.5*xm) * scalingFactor, 806 | y2: (y-0.5) * scalingFactor, 807 | z2: (z+0.5*zm) * scalingFactor, 808 | 809 | c1r: color.x * cm, c1g: color.y * cm, c1b: color.z * cm, 810 | c2r: color.x * cm, c2g: color.y * cm, c2b: color.z * cm, 811 | }); 812 | lines.push({ 813 | x1: (x+0.5*xm) * scalingFactor, 814 | y1: (y-0.5) * scalingFactor, 815 | z1: (z+0.5*zm) * scalingFactor, 816 | 817 | x2: (x+0.5*xm) * scalingFactor, 818 | y2: (y) * scalingFactor, 819 | z2: (z+0.5*zm) * scalingFactor, 820 | 821 | c1r: color.x * cm, c1g: color.y * cm, c1b: color.z * cm, 822 | c2r: color.x * cm, c2g: color.y * cm, c2b: color.z * cm, 823 | }); 824 | } 825 | 826 | 827 | function smoothstepAnim(from, to, animStart, animEnd, animCurrent) { 828 | if(animCurrent > animEnd) animCurrent = animEnd; 829 | 830 | let t = (animCurrent - animStart) / (animEnd - animStart); 831 | t = t * t * (3.0 - 2.0 * t); 832 | let delta = to - from; 833 | 834 | return from + delta * t; 835 | } 836 | 837 | 838 | function indexToUvs(index) { 839 | let charsPerRow = 16; 840 | 841 | let x = index % charsPerRow; 842 | let y = Math.floor(index / charsPerRow); 843 | 844 | let us = x / charsPerRow; 845 | let vs = y / charsPerRow; 846 | 847 | let ue = us + 1 / charsPerRow; 848 | let ve = vs + 1 / charsPerRow; 849 | 850 | return { 851 | us: us * 0.5, 852 | vs: vs * 0.5, 853 | 854 | ue: ue * 0.5, 855 | ve: ve * 0.5, 856 | } 857 | } --------------------------------------------------------------------------------