├── 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 |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 |
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 | }
--------------------------------------------------------------------------------