├── 1 ├── SSAO.js ├── bundle.js ├── index.html ├── main.js ├── object.js ├── post.js └── volume.js ├── 2 ├── Layer.js ├── bundle.js ├── index.html ├── main.js ├── post.js └── scott-gray.js ├── 3 ├── bundle.js ├── index.html ├── main.js └── post.js ├── 4 ├── SSAO.js ├── bundle.js ├── index.html ├── main.js ├── post.js └── sphere.js ├── 5 ├── bundle.js ├── disc.js ├── index.html ├── main.js └── post.js ├── 6 ├── bundle.js ├── desert.js ├── index.html ├── main.js └── post.js ├── 7 ├── bundle.js ├── index.html ├── main.js ├── post.js └── smoke.js ├── 8 ├── RoundedPrismGeometry.js ├── SSAO.js ├── bundle.js ├── index.html ├── main.js └── post.js ├── 10 ├── index.html ├── main.js └── post.js ├── 12 ├── bundle.js ├── index.html ├── main.js ├── post.js └── wisp.js ├── 13 ├── bundle.js ├── index.html ├── main.js ├── object.js └── post.js ├── 17 ├── SSAO.js ├── bundle.js ├── index.html ├── main.js ├── post.js └── wind-block.js ├── 23 ├── bundle.js ├── index.html ├── main.js ├── object.js └── post.js ├── 24 ├── SSAO.js ├── boids.js ├── index.html ├── main.js └── post.js ├── 27 ├── bundle.js ├── index.html ├── main.js ├── object.js └── post.js ├── .gitattributes ├── LICENSE ├── README.md ├── assets ├── box.glb ├── box.obj ├── dodecahedron.glb ├── dodecahedron.obj ├── icosahedron.glb ├── icosahedron.obj ├── matcap.png ├── matcap_1k.jpg ├── noise.png ├── studio_small_03_1k.hdr ├── studio_small_03_2k.hdr └── suzanne.glb ├── index.html ├── modules ├── Fibonacci.js ├── Maf.js ├── Shader3DPass.js ├── Shader3DPingPongPass.js ├── ShaderPass.js ├── ShaderPingPongPass.js ├── bloomPass.js ├── capture.js ├── curl.js ├── easings.js ├── fbo.js ├── geometry-index.js ├── gradient-linear.js ├── hdri.js ├── models.js ├── octree.js ├── orient2d.js ├── palettes.js ├── poisson.js ├── renderer.js └── tweet-button.js ├── screenshots ├── codevember-2022-1.jpg ├── codevember-2022-1.png ├── codevember-2022-12.jpg ├── codevember-2022-12.png ├── codevember-2022-13.jpg ├── codevember-2022-13.png ├── codevember-2022-17.jpg ├── codevember-2022-17.png ├── codevember-2022-2.jpg ├── codevember-2022-2.png ├── codevember-2022-23.jpg ├── codevember-2022-23.png ├── codevember-2022-27.jpg ├── codevember-2022-27.png ├── codevember-2022-3.jpg ├── codevember-2022-3.png ├── codevember-2022-4.jpg ├── codevember-2022-4.png ├── codevember-2022-5.jpg ├── codevember-2022-5.png ├── codevember-2022-6.jpg ├── codevember-2022-6.png ├── codevember-2022-7.jpg ├── codevember-2022-7.png ├── codevember-2022-8.jpg └── codevember-2022-8.png ├── shaders ├── basic.js ├── blur.js ├── chromatic-aberration.js ├── curl.js ├── fast-separable-gaussian-blur.js ├── fxaa.js ├── hsl.js ├── levels.js ├── noise-common.js ├── noise.js ├── noise2d.js ├── noise3d.js ├── ortho.js ├── parabola.js ├── screen.js ├── vignette.js └── worley.js ├── styles └── styles.css └── third_party ├── DeviceOrientationControls.js ├── GLTFLoader.js ├── OBJLoader.js ├── OrbitControls.js ├── RGBELoader.js ├── RoundedBoxGeometry.js ├── delaunator.js ├── perlin.js ├── three.module.js └── util.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Codevember 2022 - 01 - Light 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |

Codevember 2022

34 |

01. Light

35 |

36 | Click and drag to move the camera.
37 | Press space to stop the animation.
38 | Press R to randomize.
39 | Press F to go fullscren. 40 |

41 |

42 | 43 | 44 | 45 |

46 |

47 | Made with 48 | three.js
49 |

50 | 51 |
52 | 53 |
54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /1/main.js: -------------------------------------------------------------------------------- 1 | import { 2 | scene, 3 | getControls, 4 | renderer, 5 | camera, 6 | addResize, 7 | resize, 8 | } from "../modules/renderer.js"; 9 | import { 10 | Vector3, 11 | Mesh, 12 | TorusGeometry, 13 | TorusKnotGeometry, 14 | Group, 15 | BackSide, 16 | FrontSide, 17 | } from "../third_party/three.module.js"; 18 | import { Easings } from "../modules/easings.js"; 19 | import { getFBO } from "../modules/fbo.js"; 20 | import { material } from "./object.js"; 21 | import { Post } from "./post.js"; 22 | import { randomInRange, mod, clamp, parabola } from "../modules/Maf.js"; 23 | // import { capture } from "../modules/capture.js"; 24 | 25 | const geometries = [ 26 | new TorusGeometry(1, 0.5, 40, 200), 27 | 28 | new TorusKnotGeometry(1, 0.4, 200, 40, 3, 1), 29 | new TorusKnotGeometry(1, 0.4, 200, 40, 3, 2), 30 | 31 | new TorusKnotGeometry(1, 0.4, 200, 40, 2, 1), 32 | new TorusKnotGeometry(1, 0.4, 200, 40, 2, 3), 33 | 34 | new TorusKnotGeometry(1, 0.4, 200, 40, 1, 2), 35 | new TorusKnotGeometry(1, 0.4, 200, 40, 1, 3), 36 | new TorusKnotGeometry(1, 0.4, 200, 40, 1, 4), 37 | ]; 38 | 39 | const mesh = new Mesh(geometries[0], material); 40 | 41 | scene.add(mesh); 42 | 43 | const post = new Post(renderer); 44 | 45 | renderer.shadowMap.enabled = true; 46 | 47 | const group = new Group(); 48 | scene.add(group); 49 | 50 | camera.position.set(1, 1, 1).setLength(6); 51 | camera.lookAt(scene.position); 52 | 53 | const controls = getControls(); 54 | controls.minDistance = 4; 55 | controls.maxDistance = 10; 56 | controls.enablePan = false; 57 | 58 | const colorFBO = getFBO(1, 1, { samples: 4 }); 59 | const backZoomFBO = getFBO(1, 1, { samples: 4 }); 60 | const frontZoomFBO = getFBO(1, 1, { samples: 4 }); 61 | 62 | function init() { 63 | render(); 64 | } 65 | 66 | let frames = 0; 67 | 68 | let time = 0; 69 | let prevTime = performance.now(); 70 | 71 | function render() { 72 | const t = performance.now(); 73 | const dt = t - prevTime; 74 | prevTime = t; 75 | 76 | if (running) { 77 | time += dt; 78 | 79 | mesh.material.uniforms.time.value = time; 80 | 81 | mesh.rotation.x += dt / 10000; 82 | mesh.rotation.y += (dt / 10000) * 0.66; 83 | 84 | const a = parabola(mod(time / 2000, 1), 1); 85 | const b = Easings.InOutQuint(a); 86 | const dir = new Vector3(); 87 | } 88 | 89 | // renderer.render(scene, camera); 90 | 91 | renderer.setClearColor(0, 1); 92 | 93 | mesh.material.side = BackSide; 94 | mesh.material.uniforms.matcap.value = false; 95 | renderer.setRenderTarget(backZoomFBO); 96 | renderer.render(scene, camera); 97 | renderer.setRenderTarget(null); 98 | 99 | mesh.material.side = FrontSide; 100 | mesh.material.uniforms.matcap.value = false; 101 | renderer.setRenderTarget(frontZoomFBO); 102 | renderer.render(scene, camera); 103 | renderer.setRenderTarget(null); 104 | 105 | renderer.setClearColor(0x101010, 0); 106 | mesh.material.uniforms.matcap.value = true; 107 | renderer.setRenderTarget(colorFBO); 108 | renderer.render(scene, camera); 109 | renderer.setRenderTarget(null); 110 | 111 | post.render(colorFBO.texture, backZoomFBO.texture, frontZoomFBO.texture); 112 | 113 | // capture(renderer.domElement); 114 | 115 | // if (frames > 10 * 60 && window.capturer.capturing) { 116 | // window.capturer.stop(); 117 | // window.capturer.save(); 118 | // } 119 | // frames++; 120 | 121 | renderer.setAnimationLoop(render); 122 | } 123 | 124 | function randomize() { 125 | mesh.geometry = geometries[Math.floor(Math.random() * geometries.length)]; 126 | mesh.material.uniforms.power.value = randomInRange(2, 10); 127 | } 128 | 129 | function goFullscreen() { 130 | if (renderer.domElement.webkitRequestFullscreen) { 131 | renderer.domElement.webkitRequestFullscreen(); 132 | } else { 133 | renderer.domElement.requestFullscreen(); 134 | } 135 | } 136 | 137 | let running = true; 138 | 139 | window.addEventListener("keydown", (e) => { 140 | if (e.code === "KeyR") { 141 | randomize(); 142 | } 143 | if (e.code === "Space") { 144 | running = !running; 145 | } 146 | if (e.code === "KeyF") { 147 | goFullscreen(); 148 | } 149 | }); 150 | 151 | document.querySelector("#randomizeBtn").addEventListener("click", (e) => { 152 | randomize(); 153 | }); 154 | 155 | document.querySelector("#pauseBtn").addEventListener("click", (e) => { 156 | running = !running; 157 | }); 158 | 159 | document.querySelector("#fullscreenBtn").addEventListener("click", (e) => { 160 | goFullscreen(); 161 | }); 162 | 163 | function myResize(w, h, dPR) { 164 | colorFBO.setSize(w * dPR, h * dPR); 165 | const wz = (w * dPR) / 2; 166 | const hz = (h * dPR) / 2; 167 | backZoomFBO.setSize(wz, hz); 168 | frontZoomFBO.setSize(wz, hz); 169 | post.setSize(w * dPR, h * dPR); 170 | } 171 | addResize(myResize); 172 | 173 | resize(); 174 | init(); 175 | 176 | // window.start = () => { 177 | // frames = 0; 178 | // window.capturer.start(); 179 | // }; 180 | -------------------------------------------------------------------------------- /1/object.js: -------------------------------------------------------------------------------- 1 | import { 2 | GLSL3, 3 | RawShaderMaterial, 4 | TextureLoader, 5 | } from "../third_party/three.module.js"; 6 | import { shader as noiseCommon } from "../shaders/noise-common.js"; 7 | import { shader as noise3d } from "../shaders/noise3d.js"; 8 | import { shader as noise2d } from "../shaders/noise2d.js"; 9 | import { randomInRange } from "../modules/Maf.js"; 10 | 11 | const vertexShader = `precision highp float; 12 | 13 | in vec3 position; 14 | in vec3 normal; 15 | in vec2 uv; 16 | 17 | uniform float time; 18 | 19 | uniform mat4 modelViewMatrix; 20 | uniform mat4 projectionMatrix; 21 | uniform mat3 normalMatrix; 22 | 23 | out vec4 vPosition; 24 | out vec4 mvPosition; 25 | out vec3 vNormal; 26 | out vec2 vUv; 27 | 28 | ${noiseCommon} 29 | ${noise3d} 30 | 31 | void main() { 32 | vUv = uv; 33 | vNormal = normalMatrix * normal; 34 | float n = .5 + noise3d(position + time / 10000.) * .5; 35 | vec3 p = position;// + normal * n * .25; 36 | vPosition = vec4(p, 1.); 37 | mvPosition = modelViewMatrix * vec4(p, 1.); 38 | gl_Position = projectionMatrix * mvPosition; 39 | }`; 40 | 41 | const fragmentShader = `precision highp float; 42 | 43 | in vec4 mvPosition; 44 | in vec4 vPosition; 45 | in vec3 vNormal; 46 | in vec2 vUv; 47 | 48 | uniform float seed; 49 | uniform float time; 50 | uniform sampler2D noiseTexture; 51 | uniform sampler2D matCapMap; 52 | uniform bool matcap; 53 | uniform float power; 54 | 55 | out vec4 color; 56 | 57 | ${noiseCommon} 58 | ${noise2d} 59 | 60 | // https://www.shadertoy.com/view/4djGRh 61 | 62 | #define NUM_CELLS 4.0 63 | #define TILES 1.0 64 | 65 | vec2 Hash2(vec2 p) { 66 | float t = fract(time/1000.*.0003); 67 | return texture(noiseTexture, p*vec2(.135+t, .2325-t), -100.0).xy; 68 | } 69 | 70 | float Cells(in vec2 p, in float numCells) { 71 | p *= numCells; 72 | float d = 1.0e10; 73 | for (int xo = -1; xo <= 1; xo++) { 74 | for (int yo = -1; yo <= 1; yo++) { 75 | vec2 tp = floor(p) + vec2(xo, yo); 76 | tp = p - tp - Hash2(mod(tp, numCells / TILES)); 77 | d = min(d, dot(tp, tp)); 78 | } 79 | } 80 | return sqrt(d); 81 | } 82 | 83 | float luma(vec3 color) { 84 | return dot(color, vec3(0.299, 0.587, 0.114)); 85 | } 86 | 87 | void main() { 88 | 89 | // calculate matcap coordinates. 90 | vec3 n = normalize(vNormal); 91 | vec3 eye = normalize(mvPosition.xyz); 92 | vec3 r = reflect( eye, n ); 93 | float m = 2. * sqrt( pow( r.x, 2. ) + pow( r.y, 2. ) + pow( r.z + 1., 2. ) ); 94 | vec2 vN = r.xy / m + .5; 95 | // lookup matcap. 96 | vec4 mat = texture(matCapMap, vN); 97 | 98 | float rim = dot(n, -eye); 99 | 100 | vec2 uv = vUv; 101 | float t = seed + time/10000.; 102 | float cr = .5 + .5 * Cells(uv + .9 * t, NUM_CELLS); 103 | float cg = .5 + .5 * Cells(uv + 1.1 * t, NUM_CELLS); 104 | float cb = .5 + .5 * Cells(uv + .8 * t, NUM_CELLS); 105 | 106 | vec3 col = vec3(cr, cg, cb); 107 | col = pow(col, vec3(4.)); 108 | float v = length(col); 109 | 110 | color = 2.*vec4(col, 1.); 111 | if(matcap) { 112 | color.rgb *= vec3(1.) * clamp(pow(luma(mat.rgb), power), 0., 1.); 113 | } 114 | }`; 115 | 116 | const loader = new TextureLoader(); 117 | const noiseTexture = loader.load("../assets/noise.png"); 118 | const matcapTexture = loader.load("../assets/matcap.png"); 119 | 120 | const material = new RawShaderMaterial({ 121 | uniforms: { 122 | time: { value: 0 }, 123 | seed: { value: randomInRange(-1000, 1000) }, 124 | noiseTexture: { value: noiseTexture }, 125 | matCapMap: { value: matcapTexture }, 126 | matcap: { value: true }, 127 | power: { value: 3 }, 128 | }, 129 | vertexShader, 130 | fragmentShader, 131 | glslVersion: GLSL3, 132 | }); 133 | 134 | export { material }; 135 | -------------------------------------------------------------------------------- /10/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Codevember 2022 - 10 - Penumbra 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |

Codevember 2022

34 |

10. Penumbra

35 |

36 | Click and drag to move the camera.
37 | Press space to stop the animation.
38 | Press R to randomize.
39 | Press F to go fullscren. 40 |

41 |

42 | 43 | 44 | 45 |

46 |

47 | Made with 48 | three.js
49 |

50 | 51 |
52 | 53 |
54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /10/post.js: -------------------------------------------------------------------------------- 1 | import { 2 | RawShaderMaterial, 3 | RGBAFormat, 4 | UnsignedByteType, 5 | LinearFilter, 6 | ClampToEdgeWrapping, 7 | Vector2, 8 | GLSL3, 9 | } from "../third_party/three.module.js"; 10 | import { shader as orthoVertexShader } from "../shaders/ortho.js"; 11 | import { ShaderPass } from "../modules/ShaderPass.js"; 12 | import { shader as vignette } from "../shaders/vignette.js"; 13 | import { shader as noise } from "../shaders/noise.js"; 14 | import { shader as screen } from "../shaders/screen.js"; 15 | import { BloomPass } from "../modules/bloomPass.js"; 16 | import { shader as chromaticAberration } from "../shaders/chromatic-aberration.js"; 17 | import { getFBO } from "../modules/fbo.js"; 18 | 19 | const finalFragmentShader = ` 20 | precision highp float; 21 | 22 | uniform vec2 resolution; 23 | uniform sampler2D inputTexture; 24 | 25 | uniform sampler2D blur0Texture; 26 | uniform sampler2D blur1Texture; 27 | uniform sampler2D blur2Texture; 28 | uniform sampler2D blur3Texture; 29 | uniform sampler2D blur4Texture; 30 | 31 | uniform float vignetteBoost; 32 | uniform float vignetteReduction; 33 | 34 | uniform float time; 35 | 36 | in vec2 vUv; 37 | 38 | out vec4 fragColor; 39 | 40 | ${vignette} 41 | 42 | ${noise} 43 | 44 | ${screen} 45 | 46 | void main() { 47 | vec4 b0 = texture(blur0Texture, vUv); 48 | vec4 b1 = texture(blur1Texture, vUv); 49 | vec4 b2 = texture(blur2Texture, vUv); 50 | vec4 b3 = texture(blur3Texture, vUv); 51 | vec4 b4 = texture(blur4Texture, vUv); 52 | 53 | vec4 color = texture(inputTexture, vUv); 54 | 55 | float factor = 40.; 56 | vec4 b = b0 / factor; 57 | b += 2.*b1 / factor; 58 | b += 4.*b2 / factor; 59 | b += 8.*b3 / factor; 60 | b += 16.*b4 / factor; 61 | 62 | fragColor = color + b; 63 | float f = .25; 64 | b = clamp(b-f, vec4(0.), vec4(1.))*(1./(1.-f)); 65 | fragColor = screen(color, b, 1.); 66 | fragColor *= vignette(vUv, vignetteBoost, vignetteReduction); 67 | fragColor += .05 * noise(gl_FragCoord.xy, time); 68 | fragColor.a = 1.; 69 | } 70 | `; 71 | 72 | const colorFragmentShader = `precision highp float; 73 | 74 | uniform sampler2D inputTexture; 75 | uniform float time; 76 | 77 | in vec2 vUv; 78 | 79 | out vec4 fragColor; 80 | 81 | ${chromaticAberration} 82 | ${noise} 83 | 84 | void main() { 85 | vec2 uv = .8 * (vUv - .5) + .5; 86 | fragColor = chromaticAberration(inputTexture, uv, .1, (vUv-.5) ); 87 | fragColor += .05 * noise(gl_FragCoord.xy, time/100.); 88 | fragColor.a = 1.; 89 | }`; 90 | 91 | class Post { 92 | constructor(renderer, params = {}) { 93 | this.renderer = renderer; 94 | 95 | this.colorFBO = getFBO(1, 1, { samples: 4 }); 96 | 97 | this.finalShader = new RawShaderMaterial({ 98 | uniforms: { 99 | resolution: { value: new Vector2(1, 1) }, 100 | vignetteBoost: { value: params.vignetteBoost || 1.2 }, 101 | vignetteReduction: { value: params.vignetteReduction || 1 }, 102 | inputTexture: { value: null }, 103 | blur0Texture: { value: null }, 104 | blur1Texture: { value: null }, 105 | blur2Texture: { value: null }, 106 | blur3Texture: { value: null }, 107 | blur4Texture: { value: null }, 108 | time: { value: 0 }, 109 | }, 110 | vertexShader: orthoVertexShader, 111 | fragmentShader: finalFragmentShader, 112 | glslVersion: GLSL3, 113 | }); 114 | this.finalPass = new ShaderPass(this.finalShader, { 115 | format: RGBAFormat, 116 | type: UnsignedByteType, 117 | minFilter: LinearFilter, 118 | magFilter: LinearFilter, 119 | wrapS: ClampToEdgeWrapping, 120 | wrapT: ClampToEdgeWrapping, 121 | }); 122 | 123 | const rgbShader = new RawShaderMaterial({ 124 | uniforms: { 125 | inputTexture: { value: this.finalPass.texture }, 126 | time: { value: 0 }, 127 | }, 128 | vertexShader: orthoVertexShader, 129 | fragmentShader: colorFragmentShader, 130 | glslVersion: GLSL3, 131 | }); 132 | this.rgbPass = new ShaderPass(rgbShader); 133 | 134 | this.bloomPass = new BloomPass(3, 5); 135 | } 136 | 137 | setSize(w0, h0, dpr) { 138 | const w = w0 * dpr; 139 | const h = h0 * dpr; 140 | this.colorFBO.setSize(w, h); 141 | this.finalPass.setSize(w, h); 142 | this.finalShader.uniforms.resolution.value.set(w, h); 143 | this.bloomPass.setSize(w, h); 144 | this.rgbPass.setSize(w, h); 145 | } 146 | 147 | render(scene, camera) { 148 | this.renderer.setRenderTarget(this.colorFBO); 149 | this.renderer.render(scene, camera); 150 | this.renderer.setRenderTarget(null); 151 | 152 | this.bloomPass.source = this.colorFBO.texture; 153 | this.finalPass.shader.uniforms.inputTexture.value = this.colorFBO.texture; 154 | this.bloomPass.render(this.renderer); 155 | 156 | this.finalPass.shader.uniforms.blur0Texture.value = 157 | this.bloomPass.blurPasses[0].texture; 158 | this.finalPass.shader.uniforms.blur1Texture.value = 159 | this.bloomPass.blurPasses[1].texture; 160 | this.finalPass.shader.uniforms.blur2Texture.value = 161 | this.bloomPass.blurPasses[2].texture; 162 | this.finalPass.shader.uniforms.blur3Texture.value = 163 | this.bloomPass.blurPasses[3].texture; 164 | this.finalPass.shader.uniforms.blur4Texture.value = 165 | this.bloomPass.blurPasses[4].texture; 166 | this.finalPass.shader.uniforms.time.value = Math.random() * 100000; 167 | this.rgbPass.shader.uniforms.time.value = Math.random() * 100000; 168 | 169 | this.finalPass.render(this.renderer); 170 | 171 | this.rgbPass.render(this.renderer, true); 172 | } 173 | } 174 | 175 | export { Post }; 176 | -------------------------------------------------------------------------------- /12/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Codevember 2022 - 12 - Wisp 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |

Codevember 2022

34 |

12. Wisp

35 |

36 | Click and drag to move the camera.
37 | Press space to stop the animation.
38 | Press R to randomize.
39 | Press F to go fullscren. 40 |

41 |

42 | 43 | 44 | 45 |

46 |

47 | Made with 48 | three.js
49 |

50 | 51 |
52 | 53 |
54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /12/main.js: -------------------------------------------------------------------------------- 1 | import { 2 | scene, 3 | getControls, 4 | renderer, 5 | camera, 6 | addResize, 7 | resize, 8 | } from "../modules/renderer.js"; 9 | import { Group } from "../third_party/three.module.js"; 10 | import { 11 | mesh, 12 | mesh2, 13 | simulation, 14 | step, 15 | randomizeColors, 16 | interpolate, 17 | } from "./wisp.js"; 18 | import { Post } from "./post.js"; 19 | import { randomInRange, mod, clamp, parabola } from "../modules/Maf.js"; 20 | // import { capture } from "../modules/capture.js"; 21 | 22 | const group = new Group(); 23 | mesh.scale.setScalar(0.9); 24 | group.add(mesh); 25 | group.add(mesh2); 26 | scene.add(group); 27 | 28 | const post = new Post(renderer); 29 | 30 | renderer.shadowMap.enabled = true; 31 | 32 | camera.position.set(10, 10, 10); 33 | camera.lookAt(scene.position); 34 | 35 | const controls = getControls(); 36 | controls.minDistance = 3; 37 | controls.maxDistance = 100; 38 | controls.enablePan = false; 39 | 40 | function init() { 41 | render(); 42 | } 43 | 44 | let frames = 0; 45 | let invalidate = false; 46 | 47 | let time = 0; 48 | let prevTime = performance.now(); 49 | 50 | function render() { 51 | const t = performance.now(); 52 | const dt = t - prevTime; 53 | prevTime = t; 54 | 55 | if (running || invalidate) { 56 | if (!invalidate) { 57 | time += dt; 58 | 59 | group.rotation.x += dt / 10000; 60 | group.rotation.y += (dt / 10000) * 0.66; 61 | } 62 | step(renderer, time / 1000, dt / 16); 63 | 64 | mesh2.material.uniforms.positions.value = simulation.texture; 65 | interpolate(time, renderer); 66 | invalidate = false; 67 | } 68 | 69 | // renderer.render(scene, camera); 70 | 71 | post.render(scene, camera); 72 | 73 | // capture(renderer.domElement); 74 | 75 | // if (frames > 10 * 60 && window.capturer.capturing) { 76 | // window.capturer.stop(); 77 | // window.capturer.save(); 78 | // } 79 | // frames++; 80 | 81 | renderer.setAnimationLoop(render); 82 | } 83 | 84 | randomize(); 85 | 86 | function randomize() { 87 | const bkg = randomizeColors(); 88 | renderer.setClearColor(bkg, 1); 89 | const offset = randomInRange(-1000, 1000); 90 | mesh.material.uniforms.offset.value = offset; 91 | simulation.shader.uniforms.offset.value = offset; 92 | invalidate = true; 93 | } 94 | 95 | function goFullscreen() { 96 | if (renderer.domElement.webkitRequestFullscreen) { 97 | renderer.domElement.webkitRequestFullscreen(); 98 | } else { 99 | renderer.domElement.requestFullscreen(); 100 | } 101 | } 102 | 103 | let running = true; 104 | 105 | window.addEventListener("keydown", (e) => { 106 | if (e.code === "KeyR") { 107 | randomize(); 108 | } 109 | if (e.code === "Space") { 110 | running = !running; 111 | } 112 | if (e.code === "KeyF") { 113 | goFullscreen(); 114 | } 115 | }); 116 | 117 | document.querySelector("#randomizeBtn").addEventListener("click", (e) => { 118 | randomize(); 119 | }); 120 | 121 | document.querySelector("#pauseBtn").addEventListener("click", (e) => { 122 | running = !running; 123 | }); 124 | 125 | document.querySelector("#fullscreenBtn").addEventListener("click", (e) => { 126 | goFullscreen(); 127 | }); 128 | 129 | function myResize(w, h, dPR) { 130 | post.setSize(w * dPR, h * dPR); 131 | } 132 | addResize(myResize); 133 | 134 | resize(); 135 | init(); 136 | 137 | // window.start = () => { 138 | // frames = 0; 139 | // window.capturer.start(); 140 | // }; 141 | -------------------------------------------------------------------------------- /12/post.js: -------------------------------------------------------------------------------- 1 | import { 2 | RawShaderMaterial, 3 | HalfFloatType, 4 | NearestFilter, 5 | RGBAFormat, 6 | UnsignedByteType, 7 | LinearFilter, 8 | ClampToEdgeWrapping, 9 | BackSide, 10 | FrontSide, 11 | Vector2, 12 | GLSL3, 13 | Vector3, 14 | DataTexture3D, 15 | RedFormat, 16 | FloatType, 17 | } from "../third_party/three.module.js"; 18 | import { getFBO } from "../modules/fbo.js"; 19 | import { shader as orthoVertexShader } from "../shaders/ortho.js"; 20 | import { ShaderPass } from "../modules/ShaderPass.js"; 21 | import { shader as vignette } from "../shaders/vignette.js"; 22 | import { shader as noise } from "../shaders/noise.js"; 23 | import { shader as screen } from "../shaders/screen.js"; 24 | import { shader as levels } from "../shaders/levels.js"; 25 | import { BloomPass } from "../modules/bloomPass.js"; 26 | 27 | const finalFragmentShader = ` 28 | precision highp float; 29 | 30 | uniform vec2 resolution; 31 | uniform sampler2D inputTexture; 32 | 33 | uniform sampler2D blur0Texture; 34 | uniform sampler2D blur1Texture; 35 | uniform sampler2D blur2Texture; 36 | uniform sampler2D blur3Texture; 37 | uniform sampler2D blur4Texture; 38 | 39 | uniform float vignetteBoost; 40 | uniform float vignetteReduction; 41 | 42 | uniform float time; 43 | 44 | in vec2 vUv; 45 | 46 | out vec4 fragColor; 47 | 48 | ${vignette} 49 | 50 | ${noise} 51 | 52 | ${screen} 53 | 54 | ${levels} 55 | 56 | void main() { 57 | vec4 b0 = texture(blur0Texture, vUv); 58 | vec4 b1 = texture(blur1Texture, vUv); 59 | vec4 b2 = texture(blur2Texture, vUv); 60 | vec4 b3 = texture(blur3Texture, vUv); 61 | vec4 b4 = texture(blur4Texture, vUv); 62 | 63 | vec4 color = texture(inputTexture, vUv); 64 | 65 | float s= 40.; 66 | vec4 b = b0 / s; 67 | b += 2.*b1 / s; 68 | b += 4.*b2 / s; 69 | b += 8.*b3 / s; 70 | b += 16.*b4 / s; 71 | 72 | fragColor = screen(color, b, 1.); 73 | fragColor *= vignette(vUv, vignetteBoost, vignetteReduction); 74 | fragColor += .01 * noise(gl_FragCoord.xy, time); 75 | fragColor.a = 1.; 76 | 77 | fragColor.rgb = finalLevels(fragColor.rgb, vec3(.2), vec3(1.), vec3(.8)); 78 | } 79 | `; 80 | 81 | const colorFragmentShader = `precision highp float; 82 | 83 | uniform sampler2D inputTexture; 84 | uniform float time; 85 | 86 | in vec2 vUv; 87 | 88 | out vec4 fragColor; 89 | 90 | ${noise} 91 | 92 | void main() { 93 | vec2 size = vec2(textureSize(inputTexture, 0)); 94 | int steps = 10; 95 | float total = 0.; 96 | float fSteps = float(steps); 97 | vec4 accum = vec4(0.); 98 | for( int i = 0; i < steps; i++){ 99 | vec2 inc = 20. * float(i) / (fSteps*size); 100 | vec2 dir = vUv-.5; 101 | vec4 r = texture(inputTexture, vUv - dir * inc); 102 | vec4 g = texture(inputTexture, vUv); 103 | vec4 b = texture(inputTexture, vUv + dir * inc); 104 | float w = float(steps - i)/fSteps; 105 | accum += vec4(r.r, g.g, b.b, 0.) * w; 106 | total += w; 107 | } 108 | accum /= total; 109 | fragColor = vec4(accum.rgb , 1.); 110 | fragColor += .01 * noise(gl_FragCoord.xy, time); 111 | }`; 112 | 113 | class Post { 114 | constructor(renderer, params = {}) { 115 | this.renderer = renderer; 116 | 117 | this.colorFBO = getFBO(1, 1, { samples: 4 }); 118 | 119 | this.colorShader = new RawShaderMaterial({ 120 | uniforms: { 121 | inputTexture: { value: this.colorFBO.texture }, 122 | time: { value: 0 }, 123 | }, 124 | vertexShader: orthoVertexShader, 125 | fragmentShader: colorFragmentShader, 126 | glslVersion: GLSL3, 127 | }); 128 | 129 | this.colorPass = new ShaderPass(this.colorShader, { 130 | format: RGBAFormat, 131 | type: UnsignedByteType, 132 | minFilter: LinearFilter, 133 | magFilter: LinearFilter, 134 | wrapS: ClampToEdgeWrapping, 135 | wrapT: ClampToEdgeWrapping, 136 | }); 137 | 138 | this.finalShader = new RawShaderMaterial({ 139 | uniforms: { 140 | resolution: { value: new Vector2(1, 1) }, 141 | vignetteBoost: { value: params.vignetteBoost || 1.1 }, 142 | vignetteReduction: { value: params.vignetteReduction || 0.8 }, 143 | inputTexture: { value: this.colorPass.texture }, 144 | blur0Texture: { value: null }, 145 | blur1Texture: { value: null }, 146 | blur2Texture: { value: null }, 147 | blur3Texture: { value: null }, 148 | blur4Texture: { value: null }, 149 | time: { value: 0 }, 150 | }, 151 | vertexShader: orthoVertexShader, 152 | fragmentShader: finalFragmentShader, 153 | glslVersion: GLSL3, 154 | }); 155 | this.finalPass = new ShaderPass(this.finalShader, { 156 | format: RGBAFormat, 157 | type: UnsignedByteType, 158 | minFilter: LinearFilter, 159 | magFilter: LinearFilter, 160 | wrapS: ClampToEdgeWrapping, 161 | wrapT: ClampToEdgeWrapping, 162 | }); 163 | 164 | this.bloomPass = new BloomPass(5, 5); 165 | } 166 | 167 | setSize(w, h) { 168 | this.colorFBO.setSize(w, h); 169 | this.colorPass.setSize(w, h); 170 | this.finalPass.setSize(w, h); 171 | this.finalShader.uniforms.resolution.value.set(w, h); 172 | this.bloomPass.setSize(w, h); 173 | } 174 | 175 | render(scene, camera) { 176 | const t = Math.random() * 100000; 177 | this.colorPass.shader.uniforms.time.value = t; 178 | this.renderer.setRenderTarget(this.colorFBO); 179 | this.renderer.render(scene, camera, this.colorFBO); 180 | this.renderer.setRenderTarget(null); 181 | 182 | this.colorPass.shader.uniforms.inputTexture.value = this.colorFBO.texture; 183 | this.colorPass.render(this.renderer); 184 | 185 | this.bloomPass.source = this.colorPass.texture; 186 | this.bloomPass.render(this.renderer); 187 | 188 | this.finalPass.shader.uniforms.blur0Texture.value = 189 | this.bloomPass.blurPasses[0].texture; 190 | this.finalPass.shader.uniforms.blur1Texture.value = 191 | this.bloomPass.blurPasses[1].texture; 192 | this.finalPass.shader.uniforms.blur2Texture.value = 193 | this.bloomPass.blurPasses[2].texture; 194 | this.finalPass.shader.uniforms.blur3Texture.value = 195 | this.bloomPass.blurPasses[3].texture; 196 | this.finalPass.shader.uniforms.blur4Texture.value = 197 | this.bloomPass.blurPasses[4].texture; 198 | this.finalPass.shader.uniforms.time.value = t; 199 | 200 | this.finalPass.render(this.renderer, true); 201 | } 202 | } 203 | 204 | export { Post }; 205 | -------------------------------------------------------------------------------- /13/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Codevember 2022 - 13 - Bloom 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |

Codevember 2022

34 |

13. Bloom

35 |

36 | Click and drag to move the camera.
37 | Press space to stop the animation.
38 | Press F to go fullscren. 39 |

40 |

41 | 42 | 43 |

44 |

45 | Made with 46 | three.js
47 |

48 | 49 |
50 | 51 |
52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /13/main.js: -------------------------------------------------------------------------------- 1 | import { 2 | scene, 3 | getControls, 4 | renderer, 5 | camera, 6 | addResize, 7 | resize, 8 | } from "../modules/renderer.js"; 9 | import { Mesh, Group, TorusKnotGeometry } from "../third_party/three.module.js"; 10 | import { material } from "./object.js"; 11 | import { Easings } from "../modules/easings.js"; 12 | 13 | import { Post } from "./post.js"; 14 | import { mod, PI, TAU } from "../modules/Maf.js"; 15 | 16 | const post = new Post(renderer); 17 | 18 | const geo = new TorusKnotGeometry(1, 0.25, 400, 36, 3, 5); 19 | const group = new Group(); 20 | 21 | const mesh = new Mesh(geo, material); 22 | group.add(mesh); 23 | const e = 0.05; 24 | const mesh2 = new Mesh(geo, material.clone()); 25 | mesh2.material.uniforms.offset.value = e; 26 | mesh2.material.uniforms.color.value.setHex(0x93d6ce); 27 | group.add(mesh2); 28 | const mesh3 = new Mesh(geo, material.clone()); 29 | mesh3.material.uniforms.offset.value = -e; 30 | mesh3.material.uniforms.color.value.setHex(0xc1cb3f); 31 | group.add(mesh3); 32 | 33 | scene.add(group); 34 | 35 | const controls = getControls(); 36 | // controls.enableZoom = false; 37 | // controls.enablePan = false; 38 | 39 | renderer.setClearColor(0x050505, 1); 40 | 41 | camera.position.set(1, 1, 1); 42 | camera.lookAt(scene.position); 43 | 44 | async function init() { 45 | render(); 46 | } 47 | 48 | let frames = 0; 49 | 50 | let time = 0; 51 | let prevTime = performance.now(); 52 | 53 | function render() { 54 | const t = performance.now(); 55 | const dt = t - prevTime; 56 | prevTime = t; 57 | 58 | if (running) { 59 | time += dt; 60 | 61 | const t = mod(time, 30000) / 30000; 62 | mesh.material.uniforms.time.value = t; 63 | mesh2.material.uniforms.time.value = t; 64 | mesh3.material.uniforms.time.value = t; 65 | 66 | mesh.rotation.x = Easings.InOutQuint(t) * TAU; 67 | mesh2.rotation.y = Easings.InOutQuint(mod(t + 1 / 3, 1)) * TAU; 68 | mesh3.rotation.z = Easings.InOutQuint(mod(t + 2 / 3, 1)) * TAU; 69 | 70 | group.rotation.x = time / 1000; 71 | } 72 | 73 | // renderer.render(scene, camera); 74 | post.render(scene, camera); 75 | 76 | // capture(renderer.domElement); 77 | 78 | // if (frames > 10 * 60 && window.capturer.capturing) { 79 | // window.capturer.stop(); 80 | // window.capturer.save(); 81 | // } 82 | // frames++; 83 | 84 | renderer.setAnimationLoop(render); 85 | } 86 | 87 | function randomize() {} 88 | 89 | function goFullscreen() { 90 | if (renderer.domElement.webkitRequestFullscreen) { 91 | renderer.domElement.webkitRequestFullscreen(); 92 | } else { 93 | renderer.domElement.requestFullscreen(); 94 | } 95 | } 96 | 97 | let running = true; 98 | 99 | window.addEventListener("keydown", (e) => { 100 | // if (e.code === "KeyR") { 101 | // randomize(); 102 | // } 103 | if (e.code === "Space") { 104 | running = !running; 105 | } 106 | if (e.code === "KeyF") { 107 | goFullscreen(); 108 | } 109 | }); 110 | 111 | document.querySelector("#pauseBtn").addEventListener("click", (e) => { 112 | running = !running; 113 | }); 114 | 115 | document.querySelector("#fullscreenBtn").addEventListener("click", (e) => { 116 | goFullscreen(); 117 | }); 118 | 119 | function myResize(w, h, dPR) { 120 | post.setSize(w * dPR, h * dPR); 121 | } 122 | addResize(myResize); 123 | 124 | resize(); 125 | init(); 126 | 127 | // window.start = () => { 128 | // frames = 0; 129 | // window.capturer.start(); 130 | // }; 131 | -------------------------------------------------------------------------------- /13/object.js: -------------------------------------------------------------------------------- 1 | import { 2 | AdditiveBlending, 3 | Color, 4 | DoubleSide, 5 | GLSL3, 6 | RawShaderMaterial, 7 | } from "../third_party/three.module.js"; 8 | import { shader as parabola } from "../shaders/parabola.js"; 9 | 10 | const vertexShader = ` 11 | precision highp float; 12 | 13 | in vec3 position; 14 | in vec2 uv; 15 | 16 | uniform mat4 modelViewMatrix; 17 | uniform mat4 projectionMatrix; 18 | uniform vec3 cameraPosition; 19 | uniform float time; 20 | uniform float offset; 21 | 22 | out vec3 vPosition; 23 | out vec2 vUv; 24 | out float vDepth; 25 | out float vAngle; 26 | 27 | #define PI 3.1415926535897932384626433832795 28 | #define TAU (2.*PI) 29 | 30 | ${parabola} 31 | 32 | void main() { 33 | vUv = uv; 34 | vec3 p = position; 35 | float speed = 5.; 36 | float repeat = 1.; 37 | float s = parabola(mod(repeat*vUv.x+offset+speed*time,1.),20.); 38 | p *= s; 39 | vAngle = atan(p.x,p.y); 40 | vec4 mvPosition = modelViewMatrix * vec4( p, 1. ); 41 | gl_Position = projectionMatrix * mvPosition; 42 | vDepth = .5 * abs(gl_Position.z); 43 | } 44 | `; 45 | 46 | const fragmentShader = ` 47 | precision highp float; 48 | 49 | uniform float time; 50 | uniform float offset; 51 | uniform vec3 color; 52 | uniform float glow; 53 | 54 | in vec2 vUv; 55 | in float vDepth; 56 | in float vAngle; 57 | 58 | out vec4 fragColor; 59 | 60 | #define PI 3.1415926535897932384626433832795 61 | #define TAU (2.*PI) 62 | 63 | ${parabola} 64 | 65 | void main(){ 66 | float speed = 25.; 67 | float repeat = 2.; 68 | float f = mod(repeat*vUv.x+offset+speed*time,1.); 69 | fragColor = vec4(vDepth*vec3(parabola(f, 100.)/3.)*color, 1.); 70 | } 71 | `; 72 | 73 | const material = new RawShaderMaterial({ 74 | uniforms: { 75 | time: { value: 0 }, 76 | glow: { value: 0 }, 77 | offset: { value: 0 }, 78 | color: { value: new Color(0xf97a4d) }, 79 | }, 80 | vertexShader, 81 | fragmentShader, 82 | // transparent: true, 83 | // depthWrite: false, 84 | depthTest: false, 85 | blending: AdditiveBlending, 86 | wireframe: false, 87 | side: DoubleSide, 88 | glslVersion: GLSL3, 89 | }); 90 | 91 | export { material }; 92 | -------------------------------------------------------------------------------- /13/post.js: -------------------------------------------------------------------------------- 1 | import { 2 | RawShaderMaterial, 3 | RGBAFormat, 4 | UnsignedByteType, 5 | LinearFilter, 6 | ClampToEdgeWrapping, 7 | Vector2, 8 | GLSL3, 9 | } from "../third_party/three.module.js"; 10 | import { getFBO } from "../modules/fbo.js"; 11 | import { shader as orthoVertexShader } from "../shaders/ortho.js"; 12 | import { ShaderPass } from "../modules/ShaderPass.js"; 13 | import { shader as vignette } from "../shaders/vignette.js"; 14 | import { shader as noise } from "../shaders/noise.js"; 15 | import { shader as screen } from "../shaders/screen.js"; 16 | import { shader as levels } from "../shaders/levels.js"; 17 | 18 | import { BloomPass } from "../modules/bloomPass.js"; 19 | 20 | const finalFragmentShader = ` 21 | precision highp float; 22 | 23 | uniform vec2 resolution; 24 | uniform sampler2D inputTexture; 25 | 26 | uniform sampler2D blur0Texture; 27 | uniform sampler2D blur1Texture; 28 | uniform sampler2D blur2Texture; 29 | uniform sampler2D blur3Texture; 30 | uniform sampler2D blur4Texture; 31 | 32 | uniform float vignetteBoost; 33 | uniform float vignetteReduction; 34 | 35 | uniform float time; 36 | 37 | in vec2 vUv; 38 | 39 | out vec4 fragColor; 40 | 41 | ${vignette} 42 | 43 | ${noise} 44 | 45 | ${screen} 46 | 47 | ${levels} 48 | 49 | void main() { 50 | vec4 b0 = texture(blur0Texture, vUv); 51 | vec4 b1 = texture(blur1Texture, vUv); 52 | vec4 b2 = texture(blur2Texture, vUv); 53 | vec4 b3 = texture(blur3Texture, vUv); 54 | vec4 b4 = texture(blur4Texture, vUv); 55 | 56 | vec4 color = texture(inputTexture, vUv); 57 | 58 | float s = 10.; 59 | vec4 b = b0 / s; 60 | b += 2.*b1 / s; 61 | b += 4.*b2 / s; 62 | b += 8.*b3 / s; 63 | b += 16.*b4 / s; 64 | 65 | fragColor = screen(color, b, 1.); 66 | fragColor *= vignette(vUv, vignetteBoost, vignetteReduction); 67 | fragColor += .05 * noise(gl_FragCoord.xy, time); 68 | fragColor.a = 1.; 69 | 70 | fragColor.rgb = finalLevels(fragColor.rgb, vec3(.1), vec3(1.), vec3(.8)); 71 | } 72 | `; 73 | 74 | const colorFragmentShader = `precision highp float; 75 | 76 | uniform sampler2D inputTexture; 77 | uniform float time; 78 | 79 | in vec2 vUv; 80 | 81 | out vec4 fragColor; 82 | 83 | ${noise} 84 | 85 | void main() { 86 | vec2 size = vec2(textureSize(inputTexture, 0)); 87 | int steps = 10; 88 | float total = 0.; 89 | float fSteps = float(steps); 90 | vec4 accum = vec4(0.); 91 | for( int i = 0; i < steps; i++){ 92 | vec2 inc = 20. * float(i) / (fSteps*size); 93 | vec2 dir = vUv-.5; 94 | vec4 r = texture(inputTexture, vUv - dir * inc); 95 | vec4 g = texture(inputTexture, vUv); 96 | vec4 b = texture(inputTexture, vUv + dir * inc); 97 | float w = float(steps - i)/fSteps; 98 | accum += vec4(r.r, g.g, b.b, 0.) * w; 99 | total += w; 100 | } 101 | accum /= total; 102 | fragColor = vec4(accum.rgb , 1.); 103 | fragColor += .05 * noise(gl_FragCoord.xy, time); 104 | }`; 105 | 106 | class Post { 107 | constructor(renderer, params = {}) { 108 | this.renderer = renderer; 109 | 110 | const supersampled = true; 111 | 112 | this.colorFBO = getFBO(1, 1, {}, supersampled); 113 | 114 | this.colorShader = new RawShaderMaterial({ 115 | uniforms: { 116 | inputTexture: { value: this.colorFBO.texture }, 117 | time: { value: 0 }, 118 | }, 119 | vertexShader: orthoVertexShader, 120 | fragmentShader: colorFragmentShader, 121 | glslVersion: GLSL3, 122 | }); 123 | 124 | this.colorPass = new ShaderPass(this.colorShader, { 125 | format: RGBAFormat, 126 | type: UnsignedByteType, 127 | minFilter: LinearFilter, 128 | magFilter: LinearFilter, 129 | wrapS: ClampToEdgeWrapping, 130 | wrapT: ClampToEdgeWrapping, 131 | }); 132 | 133 | this.finalShader = new RawShaderMaterial({ 134 | uniforms: { 135 | resolution: { value: new Vector2(1, 1) }, 136 | vignetteBoost: { value: params.vignetteBoost || 1.2 }, 137 | vignetteReduction: { value: params.vignetteReduction || 1.2 }, 138 | inputTexture: { value: this.colorPass.texture }, 139 | blur0Texture: { value: null }, 140 | blur1Texture: { value: null }, 141 | blur2Texture: { value: null }, 142 | blur3Texture: { value: null }, 143 | blur4Texture: { value: null }, 144 | time: { value: 0 }, 145 | }, 146 | vertexShader: orthoVertexShader, 147 | fragmentShader: finalFragmentShader, 148 | glslVersion: GLSL3, 149 | }); 150 | this.finalPass = new ShaderPass(this.finalShader, { 151 | format: RGBAFormat, 152 | type: UnsignedByteType, 153 | minFilter: LinearFilter, 154 | magFilter: LinearFilter, 155 | wrapS: ClampToEdgeWrapping, 156 | wrapT: ClampToEdgeWrapping, 157 | }); 158 | 159 | this.bloomPass = new BloomPass(3, 5); 160 | } 161 | 162 | setSize(w, h) { 163 | this.colorFBO.setSize(w, h); 164 | this.colorPass.setSize(w, h); 165 | this.finalPass.setSize(w, h); 166 | this.finalShader.uniforms.resolution.value.set(w, h); 167 | this.bloomPass.setSize(w, h); 168 | } 169 | 170 | render(scene, camera) { 171 | this.renderer.setRenderTarget(this.colorFBO); 172 | this.renderer.render(scene, camera, this.colorFBO); 173 | this.renderer.setRenderTarget(null); 174 | 175 | this.colorPass.shader.uniforms.inputTexture.value = this.colorFBO.texture; 176 | this.colorPass.shader.uniforms.time.value = Math.random() * 100000; 177 | this.colorPass.render(this.renderer); 178 | 179 | this.bloomPass.source = this.colorPass.texture; 180 | this.bloomPass.render(this.renderer); 181 | 182 | this.finalPass.shader.uniforms.blur0Texture.value = 183 | this.bloomPass.blurPasses[0].texture; 184 | this.finalPass.shader.uniforms.blur1Texture.value = 185 | this.bloomPass.blurPasses[1].texture; 186 | this.finalPass.shader.uniforms.blur2Texture.value = 187 | this.bloomPass.blurPasses[2].texture; 188 | this.finalPass.shader.uniforms.blur3Texture.value = 189 | this.bloomPass.blurPasses[3].texture; 190 | this.finalPass.shader.uniforms.blur4Texture.value = 191 | this.bloomPass.blurPasses[4].texture; 192 | this.finalPass.shader.uniforms.time.value = Math.random() * 100000; 193 | 194 | this.finalPass.render(this.renderer, true); 195 | } 196 | } 197 | 198 | export { Post }; 199 | -------------------------------------------------------------------------------- /17/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Codevember 2022 - 17 - Wind 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |

Codevember 2022

34 |

17. Wind

35 |

36 | Click and drag to move the camera.
37 | Press space to stop the animation.
38 | Press R to randomize.
39 | Press F to go fullscren. 40 |

41 |

42 | 43 | 44 | 45 |

46 |

47 | Made with 48 | three.js
49 |

50 | 51 |
52 | 53 |
54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /17/main.js: -------------------------------------------------------------------------------- 1 | import { 2 | scene, 3 | getControls, 4 | renderer, 5 | camera, 6 | addResize, 7 | resize, 8 | } from "../modules/renderer.js"; 9 | import { 10 | mesh, 11 | update, 12 | curlPass, 13 | arrowGeometry, 14 | lineGeometry, 15 | } from "./wind-block.js"; 16 | 17 | import { SSAO } from "./SSAO.js"; 18 | import { Post } from "./post.js"; 19 | import { randomInRange } from "../modules/Maf.js"; 20 | // import { capture } from "../modules/capture.js"; 21 | 22 | const ssao = new SSAO(renderer); 23 | const post = new Post(renderer); 24 | 25 | camera.near = 3; 26 | camera.far = 10; 27 | camera.updateProjectionMatrix(); 28 | camera.position.set(1, 1, 1).setLength(8); 29 | 30 | scene.add(mesh); 31 | 32 | const debugFs = `precision highp float; 33 | precision highp sampler3D; 34 | 35 | in vec2 vUv; 36 | 37 | uniform sampler3D map; 38 | 39 | out vec4 color; 40 | 41 | void main() { 42 | color = texture(map, vec3(vUv, .5)); 43 | }`; 44 | 45 | const controls = getControls(); 46 | controls.minDistance = 5; 47 | controls.maxDistance = 10; 48 | controls.enablePan = false; 49 | 50 | async function init() { 51 | render(); 52 | } 53 | 54 | let frames = 0; 55 | 56 | let time = 0; 57 | let prevTime = performance.now(); 58 | 59 | function render() { 60 | const t = performance.now(); 61 | const dt = t - prevTime; 62 | prevTime = t; 63 | 64 | const r = 2; 65 | camera.near = camera.position.length() - r; 66 | camera.far = camera.position.length() + r; 67 | camera.updateProjectionMatrix(); 68 | 69 | if (running || invalidate) { 70 | if (!invalidate) { 71 | time += dt; 72 | mesh.rotation.y += dt / 10000; 73 | } 74 | update(renderer, time / 10000); 75 | ssao.shader.uniforms.time.value = time / 1000; 76 | 77 | ssao.shader.uniforms.directionsTex.value = curlPass.texture; 78 | ssao.interpolate(time / 1000); 79 | invalidate = false; 80 | } 81 | ssao.render(scene, camera); 82 | post.render(ssao.output); 83 | 84 | // capture(renderer.domElement); 85 | 86 | // if (frames > 10 * 60 && window.capturer.capturing) { 87 | // window.capturer.stop(); 88 | // window.capturer.save(); 89 | // } 90 | // frames++; 91 | 92 | renderer.setAnimationLoop(render); 93 | } 94 | 95 | let invalidate = false; 96 | const geometries = [arrowGeometry, lineGeometry]; 97 | function randomize() { 98 | mesh.geometry = geometries[Math.floor(Math.random() * geometries.length)]; 99 | ssao.shader.uniforms.scale.value.set( 100 | randomInRange(0.8, 1.2), 101 | randomInRange(0.8, 2) 102 | ); 103 | ssao.shader.uniforms.threshold.value = randomInRange(1, 3); 104 | ssao.randomizeColors(); 105 | invalidate = true; 106 | } 107 | 108 | function goFullscreen() { 109 | if (renderer.domElement.webkitRequestFullscreen) { 110 | renderer.domElement.webkitRequestFullscreen(); 111 | } else { 112 | renderer.domElement.requestFullscreen(); 113 | } 114 | } 115 | 116 | let running = true; 117 | 118 | window.addEventListener("keydown", (e) => { 119 | if (e.code === "KeyR") { 120 | randomize(); 121 | } 122 | if (e.code === "Space") { 123 | running = !running; 124 | } 125 | if (e.code === "KeyF") { 126 | goFullscreen(); 127 | } 128 | }); 129 | 130 | document.querySelector("#randomizeBtn").addEventListener("click", (e) => { 131 | randomize(); 132 | }); 133 | 134 | document.querySelector("#pauseBtn").addEventListener("click", (e) => { 135 | running = !running; 136 | }); 137 | 138 | document.querySelector("#fullscreenBtn").addEventListener("click", (e) => { 139 | goFullscreen(); 140 | }); 141 | 142 | renderer.setClearColor(0x101010, 1); 143 | 144 | function myResize(w, h, dPR) { 145 | ssao.setSize(w, h, dPR); 146 | post.setSize(w, h, dPR); 147 | } 148 | addResize(myResize); 149 | 150 | resize(); 151 | init(); 152 | 153 | // window.start = () => { 154 | // frames = 0; 155 | // window.capturer.start(); 156 | // }; 157 | -------------------------------------------------------------------------------- /17/post.js: -------------------------------------------------------------------------------- 1 | import { RawShaderMaterial, GLSL3 } from "../third_party/three.module.js"; 2 | import { shader as orthoVertexShader } from "../shaders/ortho.js"; 3 | import { ShaderPass } from "../modules/ShaderPass.js"; 4 | import { shader as vignette } from "../shaders/vignette.js"; 5 | import { shader as noise } from "../shaders/noise.js"; 6 | import { shader as chromaticAberration } from "../shaders/chromatic-aberration.js"; 7 | import { shader as levels } from "../shaders/levels.js"; 8 | import { randomInRange } from "../modules/Maf.js"; 9 | 10 | const colorFragmentShader = `precision highp float; 11 | 12 | uniform sampler2D inputTexture; 13 | uniform float time; 14 | uniform float vignetteBoost; 15 | uniform float vignetteReduction; 16 | 17 | in vec2 vUv; 18 | 19 | out vec4 fragColor; 20 | 21 | ${chromaticAberration} 22 | ${noise} 23 | ${levels} 24 | ${vignette} 25 | 26 | void main() { 27 | vec2 uv = .8 * (vUv - .5) + .5; 28 | fragColor = chromaticAberration(inputTexture, uv, .1, (vUv-.5) ); 29 | // fragColor += .05 * noise(gl_FragCoord.xy, time/100.); 30 | fragColor.rgb = finalLevels(fragColor.rgb, vec3(.1), vec3(1.), vec3(.8)); 31 | fragColor *= vignette(vUv, vignetteBoost, vignetteReduction); 32 | fragColor += .05 * noise(gl_FragCoord.xy, time); 33 | fragColor.a = 1.; 34 | }`; 35 | 36 | class Post { 37 | constructor(renderer, params = {}) { 38 | this.renderer = renderer; 39 | 40 | const rgbShader = new RawShaderMaterial({ 41 | uniforms: { 42 | inputTexture: { value: null }, 43 | vignetteBoost: { value: params.vignetteBoost || 1.2 }, 44 | vignetteReduction: { value: params.vignetteReduction || 1 }, 45 | time: { value: 0 }, 46 | }, 47 | vertexShader: orthoVertexShader, 48 | fragmentShader: colorFragmentShader, 49 | glslVersion: GLSL3, 50 | }); 51 | this.rgbPass = new ShaderPass(rgbShader); 52 | } 53 | 54 | setSize(w0, h0, dpr) { 55 | const w = w0 * dpr; 56 | const h = h0 * dpr; 57 | this.rgbPass.setSize(w, h); 58 | } 59 | 60 | render(src) { 61 | this.rgbPass.shader.uniforms.time.value = randomInRange(-1000, 1000); 62 | this.rgbPass.shader.uniforms.inputTexture.value = src; 63 | this.rgbPass.render(this.renderer, true); 64 | } 65 | } 66 | 67 | export { Post }; 68 | -------------------------------------------------------------------------------- /2/Layer.js: -------------------------------------------------------------------------------- 1 | import { 2 | RawShaderMaterial, 3 | GLSL3, 4 | Mesh, 5 | IcosahedronGeometry, 6 | DoubleSide, 7 | TorusKnotGeometry, 8 | TorusGeometry, 9 | } from "../third_party/three.module.js"; 10 | import { shader as noiseCommon } from "../shaders/noise-common.js"; 11 | import { shader as noise3d } from "../shaders/noise3d.js"; 12 | import { shader as noise2d } from "../shaders/noise2d.js"; 13 | import { shader as noise } from "../shaders/noise.js"; 14 | import { shader as screen } from "../shaders/screen.js"; 15 | import { shader as hsl } from "../shaders/hsl.js"; 16 | 17 | const vertexShader = ` 18 | precision highp float; 19 | 20 | in vec3 position; 21 | in vec3 normal; 22 | in vec2 uv; 23 | 24 | uniform mat4 modelViewMatrix; 25 | uniform mat4 projectionMatrix; 26 | uniform mat3 normalMatrix; 27 | 28 | uniform float level; 29 | 30 | out vec2 vUv; 31 | out vec3 vPosition; 32 | out vec3 vViewPosition; 33 | out vec3 vNormal; 34 | 35 | void main() { 36 | vUv = uv; 37 | vNormal = normalMatrix * normal; 38 | vPosition = position - .025 * normal * level; 39 | vec4 mvPosition = modelViewMatrix * vec4( vPosition, 1.0 ); 40 | vViewPosition = -mvPosition.xyz; 41 | gl_Position = projectionMatrix * mvPosition; 42 | }`; 43 | 44 | const fragmentShader = `precision highp float; 45 | 46 | uniform sampler2D inputMap; 47 | uniform float level; 48 | uniform sampler2D gradientMap1; 49 | uniform sampler2D gradientMap2; 50 | uniform float time; 51 | uniform float scale; 52 | uniform float seed; 53 | uniform bool sphericalUvs; 54 | 55 | in vec2 vUv; 56 | in vec3 vPosition; 57 | in vec3 vViewPosition; 58 | in vec3 vNormal; 59 | 60 | out vec4 fragColor; 61 | 62 | #define M_PI 3.1415926535897932384626433832795 63 | #define M_TAU (2. * M_PI) 64 | 65 | float aastep(float threshold, float value) { 66 | float afwidth = length(vec2(dFdx(value), dFdy(value))) * 0.70710678118654757; 67 | return smoothstep(threshold-afwidth, threshold+afwidth, value); 68 | } 69 | 70 | vec2 sphericalToUV(vec3 p) { 71 | return vec2((atan(p.x, -p.z) / M_PI + 1.) / 2., asin(p.y) / M_PI + .5); 72 | } 73 | 74 | ${noiseCommon} 75 | 76 | ${noise3d} 77 | 78 | ${noise2d} 79 | 80 | ${noise} 81 | 82 | ${screen} 83 | 84 | ${hsl} 85 | 86 | void main() { 87 | vec2 uv = vUv; 88 | if(sphericalUvs) { 89 | uv = sphericalToUV(normalize(vPosition.yzx*vec3(-1.,1.,1.))); 90 | } 91 | 92 | float n = smoothstep(.45, .55, .5 * noise3d(vPosition * scale * 5. + seed) + .5); 93 | vec4 c = texture(inputMap, uv); 94 | c.g += .01; 95 | c.g += .05 * (noise(gl_FragCoord.xy, time) - .5) * c.g; 96 | 97 | float a = aastep(.5 - .5 * level, c.g) - .5 * level; 98 | float shade = mix(0., 1., 1. - level ); 99 | vec4 color1 = texture(gradientMap1, vec2(c.g, 0.)); 100 | vec4 color2 = texture(gradientMap2, vec2(c.g, 0.)); 101 | vec4 color = mix(color1, color2, n); 102 | vec3 h = rgb2hsv(color.rgb); 103 | h.y = clamp(h.y +.5, 0., 1.) ; 104 | h.z *= shade; 105 | color.rgb = hsv2rgb(h); 106 | 107 | vec3 e = normalize( vViewPosition ); 108 | float rim = pow(abs(dot(e,vNormal)),.5); 109 | color.rgb = mix(vec3(40., 122., 224.) / 255., color.rgb, rim * (1.-c.g)); 110 | 111 | fragColor = vec4(color.rgb, a); 112 | }`; 113 | 114 | const copyFs = `precision highp float; 115 | 116 | uniform sampler2D inputMap; 117 | 118 | in vec2 vUv; 119 | 120 | out vec4 fragColor; 121 | 122 | void main() { 123 | vec4 c = texture(inputMap, vUv); 124 | fragColor = c; 125 | }`; 126 | 127 | const geo1 = new TorusGeometry(0.25, 0.1, 100, 30); 128 | const geo2 = new TorusKnotGeometry(0.25, 0.1, 100, 30).scale(0.75, 0.75, 0.75); 129 | const geo3 = new IcosahedronGeometry(0.25, 4); 130 | 131 | class Layer extends Mesh { 132 | constructor(renderer) { 133 | const material = new RawShaderMaterial({ 134 | uniforms: { 135 | inputMap: { value: null }, 136 | level: { value: 0 }, 137 | gradientMap1: { value: null }, 138 | gradientMap2: { value: null }, 139 | time: { value: 0 }, 140 | seed: { value: 0 }, 141 | scale: { value: 0 }, 142 | sphericalUvs: { value: false }, 143 | }, 144 | vertexShader, 145 | fragmentShader, 146 | glslVersion: GLSL3, 147 | side: DoubleSide, 148 | transparent: true, 149 | }); 150 | 151 | super(geo1, material); 152 | this.renderer = renderer; 153 | } 154 | 155 | set gradient(texture) { 156 | this.material.uniforms.gradientMap1.value = texture; 157 | } 158 | 159 | set gradient2(texture) { 160 | this.material.uniforms.gradientMap2.value = texture; 161 | } 162 | 163 | setTorus() { 164 | this.geometry = geo1; 165 | } 166 | 167 | setTorusKnot() { 168 | this.geometry = geo2; 169 | } 170 | 171 | setSphere() { 172 | this.geometry = geo3; 173 | } 174 | } 175 | 176 | export { Layer }; 177 | -------------------------------------------------------------------------------- /2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Codevember 2022 - 02 - Coral 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 20 | 21 | 22 | 23 | 27 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 |
39 |

Codevember 2022

40 |

02. Coral

41 |

42 | Move the mouse around/tap to seed the reaction-diffusion.
43 | Click and drag to move the camera.
44 | Press space to stop the animation.
45 | Press R/C/S to randomize. Press F to go fullscren. 46 |

47 |

48 | 49 | 50 |

51 |

Randomize:

52 |

53 | 54 | 55 | 56 |

57 |

58 | Made with 59 | three.js
60 |

61 | 62 |
63 | 64 |
65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /2/scott-gray.js: -------------------------------------------------------------------------------- 1 | import { randomInRange } from "../modules/Maf.js"; 2 | import { ShaderPingPongPass } from "../modules/ShaderPingPongPass.js"; 3 | import { 4 | RawShaderMaterial, 5 | RGBAFormat, 6 | FloatType, 7 | Vector2, 8 | LinearFilter, 9 | RepeatWrapping, 10 | } from "../third_party/three.module.js"; 11 | 12 | const vertexShader = `#version 300 es 13 | precision highp float; 14 | 15 | in vec3 position; 16 | in vec2 uv; 17 | 18 | uniform vec2 resolution; 19 | 20 | out vec2 vUv; 21 | 22 | void main() { 23 | vUv = uv; 24 | gl_Position = vec4( position, 1. ); 25 | } 26 | `; 27 | 28 | const fragmentShader = `#version 300 es 29 | precision highp float; 30 | 31 | in vec2 vUv; 32 | 33 | uniform sampler2D map; 34 | uniform sampler2D test; 35 | uniform sampler2D modulate; 36 | uniform vec2 resolution; 37 | uniform vec2 pointer; 38 | uniform float feed; 39 | uniform float kill; 40 | uniform float da; 41 | uniform float db; 42 | uniform float radius; 43 | uniform bool contact; 44 | uniform bool spherical; 45 | 46 | out vec4 color; 47 | 48 | #define M_PI 3.1415926535897932384626433832795 49 | #define M_TAU (2. * M_PI) 50 | 51 | float azimuth( vec3 vector ) { 52 | return atan( vector.z, - 1.0 * vector.x ); 53 | } 54 | 55 | float inclination( vec3 vector ) { 56 | return atan( - vector.y, sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); 57 | } 58 | 59 | vec2 lookupPolar(vec2 uv) { 60 | uv = mod(uv + 1., vec2(1.)); 61 | float phi = uv.x * M_TAU; 62 | float theta = uv.y * M_PI; 63 | float x = sin(theta) * cos(phi); 64 | float y = sin(theta) * sin(phi); 65 | float z = cos(theta); 66 | vec3 p = vec3(x,y,z); 67 | vec2 uv2 = vec2( azimuth( p ) / 2. / M_PI + 0.5, inclination( p ) / M_PI + 0.5 ); 68 | return uv2; 69 | } 70 | 71 | vec2 lookupRect(vec2 uv) { 72 | return uv; 73 | } 74 | 75 | vec2 lookup(vec2 uv) { 76 | if(spherical) { 77 | return lookupPolar(uv); 78 | } 79 | return lookupRect(uv); 80 | } 81 | 82 | void main() { 83 | 84 | vec2 step = 1./resolution; 85 | 86 | vec2 uv = texture(map, lookup(vUv)).xy; 87 | 88 | vec2 uv0 = texture(map, lookup(vUv+vec2(-step.x, 0.0))).xy; 89 | vec2 uv1 = texture(map, lookup(vUv+vec2(step.x, 0.0))).xy; 90 | vec2 uv2 = texture(map, lookup(vUv+vec2(0.0, -step.y))).xy; 91 | vec2 uv3 = texture(map, lookup(vUv+vec2(0.0, step.y))).xy; 92 | 93 | float delta = 1.; 94 | 95 | vec2 lapl = (uv0 + uv1 + uv2 + uv3 - 4.0*uv); 96 | float a = uv.x; 97 | float b = uv.y; 98 | float reaction = a*b*b; 99 | float du = da * lapl.x - reaction + feed*(1.0 - a); 100 | float dv = db * lapl.y + reaction - (feed+kill)*b; 101 | vec2 dst = uv + delta*vec2(du, dv); 102 | 103 | color = vec4(dst, 0., 1.); 104 | vec2 p = lookup(pointer); 105 | if(contact && length(lookup(vUv)-lookup(p)) 2 | 3 | 4 | 5 | Codevember 2022 - 23 - Iridescent 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |

Codevember 2022

34 |

23. Iridescent

35 |

36 | Click and drag to move the camera.
37 | Press space to stop the animation.
38 | Press S to randomize shape, M to randomize material.
39 | Press R to randomize all.
40 | Press F to go fullscren. 41 |

42 |

43 | 44 | 45 |

46 |

Randomize: 47 | 48 | 49 | 50 |

51 |

52 | Made with 53 | three.js
54 |

55 | 56 |
57 | 58 |
59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /23/main.js: -------------------------------------------------------------------------------- 1 | import { 2 | scene, 3 | getControls, 4 | renderer, 5 | camera, 6 | addResize, 7 | resize, 8 | } from "../modules/renderer.js"; 9 | import { DoubleSide, FrontSide } from "../third_party/three.module.js"; 10 | import { init as initMesh, load as loadGeometries } from "./object.js"; 11 | 12 | import { Post } from "./post.js"; 13 | import { randomInRange } from "../modules/Maf.js"; 14 | // import { DeviceOrientationControls } from "../third_party/DeviceOrientationControls.js"; 15 | // import { capture } from "../modules/capture.js"; 16 | 17 | const post = new Post(renderer); 18 | 19 | camera.position.set(0, 0, 1).setLength(8); 20 | 21 | const controls = getControls(); 22 | // controls.enableZoom = false; 23 | // controls.enablePan = false; 24 | 25 | let mesh; 26 | let geometries; 27 | async function init() { 28 | geometries = await loadGeometries(); 29 | mesh = await initMesh(geometries); 30 | const a = 0.3; 31 | mesh.rotation.set(-a, 0, a); 32 | scene.add(mesh); 33 | 34 | render(); 35 | } 36 | 37 | let frames = 0; 38 | 39 | let time = 0; 40 | let prevTime = performance.now(); 41 | 42 | function render() { 43 | const t = performance.now(); 44 | const dt = t - prevTime; 45 | prevTime = t; 46 | 47 | if (running) { 48 | time += dt; 49 | mesh.rotation.y = time / 4000; 50 | } 51 | 52 | mesh.material.uniforms.time.value = time / 2000; 53 | // renderer.render(scene, camera); 54 | post.render(scene, camera); 55 | 56 | // capture(renderer.domElement); 57 | 58 | // if (frames > 10 * 60 && window.capturer.capturing) { 59 | // window.capturer.stop(); 60 | // window.capturer.save(); 61 | // } 62 | // frames++; 63 | 64 | renderer.setAnimationLoop(render); 65 | } 66 | 67 | let transparent = true; 68 | let curShape = 0; 69 | 70 | function randomizeShape() { 71 | let newShape; 72 | if (transparent) { 73 | do { 74 | newShape = Math.floor(Math.random() * geometries.transparent.length); 75 | } while (newShape === curShape); 76 | curShape = newShape; 77 | mesh.geometry = geometries.transparent[newShape]; 78 | } else { 79 | do { 80 | newShape = Math.floor(Math.random() * geometries.solid.length); 81 | } while (newShape === curShape); 82 | curShape = newShape; 83 | mesh.geometry = geometries.solid[newShape]; 84 | } 85 | } 86 | 87 | function randomizeMaterial() { 88 | if (transparent) { 89 | mesh.material.transparent = true; 90 | mesh.material.depthWrite = false; 91 | mesh.material.side = DoubleSide; 92 | mesh.material.uniforms.opacity.value = randomInRange(0.75, 1); 93 | mesh.material.uniforms.boost.value = randomInRange(0.5, 1); 94 | } else { 95 | mesh.material.transparent = false; 96 | mesh.material.depthWrite = true; 97 | mesh.material.side = FrontSide; 98 | mesh.material.uniforms.opacity.value = randomInRange(0, 1); 99 | mesh.material.uniforms.boost.value = randomInRange(1, 3); 100 | } 101 | mesh.material.uniforms.roughness.value = randomInRange(0, 0.01); 102 | mesh.material.uniforms.distort.value = randomInRange(0, 0.2); 103 | mesh.material.uniforms.offset.value = randomInRange(-1000, 1000); 104 | mesh.material.uniforms.rimBoost.value = randomInRange(1, 10); 105 | window.uniforms = mesh.material.uniforms; 106 | } 107 | 108 | function goFullscreen() { 109 | if (renderer.domElement.webkitRequestFullscreen) { 110 | renderer.domElement.webkitRequestFullscreen(); 111 | } else { 112 | renderer.domElement.requestFullscreen(); 113 | } 114 | } 115 | 116 | function randomize() { 117 | transparent = Math.random() > 0.5; 118 | randomizeShape(); 119 | randomizeMaterial(); 120 | } 121 | 122 | let running = true; 123 | 124 | window.addEventListener("keydown", (e) => { 125 | if (e.code === "KeyR") { 126 | randomize(); 127 | } 128 | if (e.code === "KeyS") { 129 | randomizeShape(); 130 | } 131 | if (e.code === "KeyM") { 132 | randomizeMaterial(); 133 | } 134 | if (e.code === "Space") { 135 | running = !running; 136 | } 137 | if (e.code === "KeyF") { 138 | goFullscreen(); 139 | } 140 | }); 141 | 142 | document.querySelector("#randomizeShpBtn").addEventListener("click", (e) => { 143 | randomizeShape(); 144 | }); 145 | 146 | document.querySelector("#randomizeMtlBtn").addEventListener("click", (e) => { 147 | randomizeMaterial(); 148 | }); 149 | 150 | document.querySelector("#randomizeAllBtn").addEventListener("click", (e) => { 151 | randomize(); 152 | }); 153 | 154 | document.querySelector("#pauseBtn").addEventListener("click", (e) => { 155 | running = !running; 156 | }); 157 | 158 | document.querySelector("#fullscreenBtn").addEventListener("click", (e) => { 159 | goFullscreen(); 160 | }); 161 | 162 | renderer.setClearColor(0x101010, 1); 163 | 164 | function myResize(w, h, dPR) { 165 | post.setSize(w, h, dPR); 166 | } 167 | addResize(myResize); 168 | 169 | resize(); 170 | init(); 171 | 172 | // window.start = () => { 173 | // frames = 0; 174 | // window.capturer.start(); 175 | // }; 176 | -------------------------------------------------------------------------------- /23/post.js: -------------------------------------------------------------------------------- 1 | import { 2 | RawShaderMaterial, 3 | RGBAFormat, 4 | UnsignedByteType, 5 | LinearFilter, 6 | ClampToEdgeWrapping, 7 | GLSL3, 8 | } from "../third_party/three.module.js"; 9 | import { shader as orthoVertexShader } from "../shaders/ortho.js"; 10 | import { ShaderPass } from "../modules/ShaderPass.js"; 11 | import { shader as vignette } from "../shaders/vignette.js"; 12 | import { shader as noise } from "../shaders/noise.js"; 13 | import { shader as chromaticAberration } from "../shaders/chromatic-aberration.js"; 14 | import { getFBO } from "../modules/fbo.js"; 15 | 16 | const finalFragmentShader = ` 17 | precision highp float; 18 | 19 | uniform sampler2D inputTexture; 20 | 21 | uniform float vignetteBoost; 22 | uniform float vignetteReduction; 23 | 24 | uniform float time; 25 | 26 | in vec2 vUv; 27 | 28 | out vec4 fragColor; 29 | 30 | ${vignette} 31 | 32 | ${noise} 33 | 34 | void main() { 35 | vec4 color = texture(inputTexture, vUv); 36 | fragColor = color; 37 | fragColor *= vignette(vUv, vignetteBoost, vignetteReduction); 38 | fragColor += color.a * .01 * noise(gl_FragCoord.xy, time/100.); 39 | fragColor.a = 1.; 40 | } 41 | `; 42 | 43 | const colorFragmentShader = `precision highp float; 44 | 45 | uniform sampler2D inputTexture; 46 | uniform float time; 47 | 48 | in vec2 vUv; 49 | 50 | out vec4 fragColor; 51 | 52 | ${chromaticAberration} 53 | ${noise} 54 | 55 | void main() { 56 | vec2 uv = .8 * (vUv - .5) + .5; 57 | fragColor = chromaticAberration(inputTexture, uv, .1, (vUv-.5) ); 58 | fragColor += .05 * noise(gl_FragCoord.xy, time/100.); 59 | fragColor.a = 1.; 60 | }`; 61 | 62 | class Post { 63 | constructor(renderer, params = {}) { 64 | this.renderer = renderer; 65 | 66 | this.colorFBO = getFBO(1, 1, { samples: 4 }); 67 | 68 | this.finalShader = new RawShaderMaterial({ 69 | uniforms: { 70 | vignetteBoost: { value: params.vignetteBoost || 1.3 }, 71 | vignetteReduction: { value: params.vignetteReduction || 1 }, 72 | inputTexture: { value: null }, 73 | blur0Texture: { value: null }, 74 | blur1Texture: { value: null }, 75 | blur2Texture: { value: null }, 76 | blur3Texture: { value: null }, 77 | blur4Texture: { value: null }, 78 | time: { value: 0 }, 79 | }, 80 | vertexShader: orthoVertexShader, 81 | fragmentShader: finalFragmentShader, 82 | glslVersion: GLSL3, 83 | }); 84 | this.finalPass = new ShaderPass(this.finalShader, { 85 | format: RGBAFormat, 86 | type: UnsignedByteType, 87 | minFilter: LinearFilter, 88 | magFilter: LinearFilter, 89 | wrapS: ClampToEdgeWrapping, 90 | wrapT: ClampToEdgeWrapping, 91 | }); 92 | 93 | const rgbShader = new RawShaderMaterial({ 94 | uniforms: { 95 | inputTexture: { value: this.finalPass.texture }, 96 | time: { value: 0 }, 97 | }, 98 | vertexShader: orthoVertexShader, 99 | fragmentShader: colorFragmentShader, 100 | glslVersion: GLSL3, 101 | }); 102 | this.rgbPass = new ShaderPass(rgbShader); 103 | } 104 | 105 | setSize(w0, h0, dpr) { 106 | const w = w0 * dpr; 107 | const h = h0 * dpr; 108 | this.colorFBO.setSize(w, h); 109 | this.finalPass.setSize(w, h); 110 | this.rgbPass.setSize(w, h); 111 | } 112 | 113 | render(scene, camera) { 114 | this.renderer.setRenderTarget(this.colorFBO); 115 | this.renderer.render(scene, camera); 116 | this.renderer.setRenderTarget(null); 117 | 118 | this.finalPass.shader.uniforms.inputTexture.value = this.colorFBO.texture; 119 | 120 | this.finalPass.render(this.renderer); 121 | 122 | this.rgbPass.render(this.renderer, true); 123 | } 124 | } 125 | 126 | export { Post }; 127 | -------------------------------------------------------------------------------- /24/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Codevember 2022 - 24 - Murmuration 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |

Codevember 2022

34 |

24. Murmuration

35 |

36 | Click and drag to move the camera.
37 | Press space to stop the animation.
38 | Press R to randomize.
39 | Press F to go fullscren. 40 |

41 |

42 | 43 | 44 | 45 |

46 |

47 | Made with 48 | three.js
49 |

50 | 51 |
52 | 53 |
54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /24/main.js: -------------------------------------------------------------------------------- 1 | import { 2 | scene, 3 | getControls, 4 | renderer, 5 | camera, 6 | addResize, 7 | resize, 8 | } from "../modules/renderer.js"; 9 | import { 10 | InstancedMesh, 11 | Matrix4, 12 | Group, 13 | Object3D, 14 | Vector3, 15 | PCFSoftShadowMap, 16 | DirectionalLight, 17 | sRGBEncoding, 18 | HemisphereLight, 19 | DynamicDrawUsage, 20 | Vector2, 21 | Mesh, 22 | BoxGeometry, 23 | MeshBasicMaterial, 24 | PlaneGeometry, 25 | PlaneBufferGeometry, 26 | MeshNormalMaterial, 27 | IcosahedronGeometry, 28 | DoubleSide, 29 | Raycaster, 30 | } from "../third_party/three.module.js"; 31 | 32 | import { SSAO } from "./SSAO.js"; 33 | import { Post } from "./post.js"; 34 | // import { DeviceOrientationControls } from "../third_party/DeviceOrientationControls.js"; 35 | // import { capture } from "../modules/capture.js"; 36 | import { 37 | particles, 38 | step as stepSim, 39 | simShader, 40 | randomizeColors, 41 | randomizeValues, 42 | } from "./boids.js"; 43 | 44 | const ssao = new SSAO(); 45 | const post = new Post(renderer); 46 | 47 | let reset = true; 48 | scene.add(particles); 49 | 50 | const debug = new Mesh( 51 | new PlaneGeometry(1, 1), 52 | new MeshBasicMaterial({ 53 | map: particles.material.uniforms.velocityTexture.value, 54 | }) 55 | ); 56 | // scene.add(debug); 57 | 58 | const hitPlane = new Mesh( 59 | new PlaneGeometry(100, 100), 60 | new MeshBasicMaterial({ side: DoubleSide }) 61 | ); 62 | scene.add(hitPlane); 63 | // hitPlane.rotation.x = Math.PI / 2; 64 | hitPlane.visible = false; 65 | const raycaster = new Raycaster(); 66 | const mouse = new Vector2(); 67 | 68 | window.addEventListener("mousemove", (e) => { 69 | mouse.x = (e.clientX / window.innerWidth) * 2 - 1; 70 | mouse.y = -(e.clientY / window.innerHeight) * 2 + 1; 71 | }); 72 | 73 | const controls = getControls(); 74 | // controls.enableZoom = false; 75 | // controls.enablePan = false; 76 | 77 | async function init() { 78 | render(); 79 | } 80 | 81 | let frames = 0; 82 | 83 | let time = 0; 84 | let prevTime = performance.now(); 85 | 86 | stepSim(renderer, true); 87 | debug.material.map = simShader.uniforms.velocityTexture.value; 88 | 89 | function step() { 90 | stepSim(renderer, reset); 91 | reset = false; 92 | debug.material.map = simShader.uniforms.velocityTexture.value; 93 | } 94 | 95 | window.step = step; 96 | 97 | function render() { 98 | const t = performance.now(); 99 | const dt = t - prevTime; 100 | prevTime = t; 101 | 102 | if (running) { 103 | time += dt; 104 | step(); 105 | } 106 | 107 | hitPlane.lookAt(camera.position); 108 | raycaster.setFromCamera(mouse, camera); 109 | const intersects = raycaster.intersectObject(hitPlane); 110 | if (intersects.length) { 111 | simShader.uniforms.predator.value.copy(intersects[0].point); 112 | } 113 | 114 | // renderer.autoClear = false; 115 | // renderer.render(scene, camera); 116 | 117 | ssao.shader.uniforms.positionTexture.value = 118 | particles.material.uniforms.positionTexture.value; 119 | ssao.shader.uniforms.velocityTexture.value = 120 | particles.material.uniforms.velocityTexture.value; 121 | ssao.shader.uniforms.gradientTexture.value = 122 | particles.material.uniforms.gradientTexture.value; 123 | ssao.shader.uniforms.types.value = particles.material.uniforms.types.value; 124 | ssao.shader.uniforms.bkgColor.value.copy( 125 | particles.material.uniforms.bkgColor.value 126 | ); 127 | ssao.combineShader.uniforms.bkgColor.value.copy( 128 | particles.material.uniforms.bkgColor.value 129 | ); 130 | // renderer.render(scene, camera); 131 | ssao.render(renderer, scene, camera); 132 | post.render(ssao.output); 133 | 134 | // capture(renderer.domElement); 135 | 136 | // if (frames > 10 * 60 && window.capturer.capturing) { 137 | // window.capturer.stop(); 138 | // window.capturer.save(); 139 | // } 140 | // frames++; 141 | 142 | renderer.setAnimationLoop(render); 143 | } 144 | 145 | function randomize() { 146 | //reset = true; 147 | randomizeColors(renderer); 148 | randomizeValues(); 149 | } 150 | randomize(); 151 | 152 | function goFullscreen() { 153 | if (renderer.domElement.webkitRequestFullscreen) { 154 | renderer.domElement.webkitRequestFullscreen(); 155 | } else { 156 | renderer.domElement.requestFullscreen(); 157 | } 158 | } 159 | 160 | let running = true; 161 | 162 | window.addEventListener("keydown", (e) => { 163 | if (e.code === "KeyR") { 164 | randomize(); 165 | } 166 | if (e.code === "Space") { 167 | running = !running; 168 | } 169 | if (e.code === "KeyF") { 170 | goFullscreen(); 171 | } 172 | }); 173 | 174 | document.querySelector("#randomizeBtn").addEventListener("click", (e) => { 175 | randomize(); 176 | }); 177 | 178 | document.querySelector("#pauseBtn").addEventListener("click", (e) => { 179 | running = !running; 180 | }); 181 | 182 | document.querySelector("#fullscreenBtn").addEventListener("click", (e) => { 183 | goFullscreen(); 184 | }); 185 | 186 | function myResize(w, h, dPR) { 187 | ssao.setSize(w, h, dPR); 188 | post.setSize(w, h, dPR); 189 | } 190 | addResize(myResize); 191 | 192 | resize(); 193 | init(); 194 | 195 | // window.start = () => { 196 | // frames = 0; 197 | // window.capturer.start(); 198 | // }; 199 | -------------------------------------------------------------------------------- /24/post.js: -------------------------------------------------------------------------------- 1 | import { RawShaderMaterial, GLSL3 } from "../third_party/three.module.js"; 2 | import { shader as orthoVertexShader } from "../shaders/ortho.js"; 3 | import { ShaderPass } from "../modules/ShaderPass.js"; 4 | import { shader as vignette } from "../shaders/vignette.js"; 5 | import { shader as noise } from "../shaders/noise.js"; 6 | import { shader as chromaticAberration } from "../shaders/chromatic-aberration.js"; 7 | import { shader as levels } from "../shaders/levels.js"; 8 | import { randomInRange } from "../modules/Maf.js"; 9 | 10 | const colorFragmentShader = `precision highp float; 11 | 12 | uniform sampler2D inputTexture; 13 | uniform float time; 14 | uniform float vignetteBoost; 15 | uniform float vignetteReduction; 16 | 17 | in vec2 vUv; 18 | 19 | out vec4 fragColor; 20 | 21 | ${chromaticAberration} 22 | ${noise} 23 | ${levels} 24 | ${vignette} 25 | 26 | void main() { 27 | vec2 uv = .8 * (vUv - .5) + .5; 28 | fragColor = chromaticAberration(inputTexture, uv, .05, (vUv-.5) ); 29 | // fragColor += .05 * noise(gl_FragCoord.xy, time/100.); 30 | fragColor.rgb = finalLevels(fragColor.rgb, vec3(.1), vec3(1.), vec3(.8)); 31 | fragColor *= vignette(vUv, vignetteBoost, vignetteReduction); 32 | fragColor += .05 * noise(gl_FragCoord.xy, time); 33 | fragColor.a = 1.; 34 | }`; 35 | 36 | class Post { 37 | constructor(renderer, params = {}) { 38 | this.renderer = renderer; 39 | 40 | const rgbShader = new RawShaderMaterial({ 41 | uniforms: { 42 | inputTexture: { value: null }, 43 | vignetteBoost: { value: params.vignetteBoost || 1.2 }, 44 | vignetteReduction: { value: params.vignetteReduction || 1 }, 45 | time: { value: 0 }, 46 | }, 47 | vertexShader: orthoVertexShader, 48 | fragmentShader: colorFragmentShader, 49 | glslVersion: GLSL3, 50 | }); 51 | this.rgbPass = new ShaderPass(rgbShader); 52 | } 53 | 54 | setSize(w0, h0, dpr) { 55 | const w = w0 * dpr; 56 | const h = h0 * dpr; 57 | this.rgbPass.setSize(w, h); 58 | } 59 | 60 | render(src) { 61 | this.rgbPass.shader.uniforms.time.value = randomInRange(-1000, 1000); 62 | this.rgbPass.shader.uniforms.inputTexture.value = src; 63 | this.rgbPass.render(this.renderer, true); 64 | } 65 | } 66 | 67 | export { Post }; 68 | -------------------------------------------------------------------------------- /27/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Codevember 2022 - 27 - Strata 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |

Codevember 2022

34 |

27. Strata

35 |

36 | Click and drag to move the camera.
37 | Press space to stop the animation.
38 | Press R to randomize.
39 | Press F to go fullscren. 40 |

41 |

42 | 43 | 44 |

45 |

46 | Randomize 47 | 48 | 49 | 50 |

51 |

52 | Made with 53 | three.js
54 |

55 | 56 |
57 | 58 |
59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /27/main.js: -------------------------------------------------------------------------------- 1 | import { 2 | scene, 3 | getControls, 4 | renderer, 5 | camera, 6 | addResize, 7 | resize, 8 | } from "../modules/renderer.js"; 9 | import { 10 | IcosahedronGeometry, 11 | Mesh, 12 | TorusGeometry, 13 | TorusKnotGeometry, 14 | } from "../third_party/three.module.js"; 15 | import { material, randomize as randomizeColors } from "./object.js"; 16 | import { 17 | loadSuzanne, 18 | loadBox, 19 | loadIcosahedron, 20 | generateBlob, 21 | loadDodecahedron, 22 | } from "../modules/models.js"; 23 | 24 | import { Post } from "./post.js"; 25 | // import { capture } from "../modules/capture.js"; 26 | 27 | const post = new Post(renderer); 28 | 29 | camera.position.set(0, 0, 1).setLength(4); 30 | 31 | const controls = getControls(); 32 | // controls.enableZoom = false; 33 | // controls.enablePan = false; 34 | 35 | let mesh; 36 | let geometries; 37 | let currentGeometry = null; 38 | 39 | async function init() { 40 | randomizeColors(renderer); 41 | geometries = await Promise.all([ 42 | loadDodecahedron(), 43 | loadIcosahedron(), 44 | loadBox(), 45 | generateBlob(), 46 | loadSuzanne(), 47 | new IcosahedronGeometry(0.75, 10), 48 | new TorusKnotGeometry(0.5, 0.15, 200, 40), 49 | new TorusGeometry(0.5, 0.2, 200, 40), 50 | ]); 51 | currentGeometry = geometries[0]; 52 | mesh = new Mesh(currentGeometry, material); 53 | mesh.rotation.set(0.1, 0.0, 0.2); 54 | scene.add(mesh); 55 | 56 | render(); 57 | } 58 | 59 | let frames = 0; 60 | 61 | let time = 0; 62 | let prevTime = performance.now(); 63 | 64 | function render() { 65 | const t = performance.now(); 66 | const dt = t - prevTime; 67 | prevTime = t; 68 | 69 | if (running) { 70 | time += dt; 71 | } 72 | mesh.rotation.y = time / 10000; 73 | mesh.material.uniforms.time.value = time / 100000; 74 | // renderer.render(scene, camera); 75 | post.render(scene, camera); 76 | 77 | // capture(renderer.domElement); 78 | 79 | // if (frames > 10 * 60 && window.capturer.capturing) { 80 | // window.capturer.stop(); 81 | // window.capturer.save(); 82 | // } 83 | // frames++; 84 | 85 | renderer.setAnimationLoop(render); 86 | } 87 | 88 | function goFullscreen() { 89 | if (renderer.domElement.webkitRequestFullscreen) { 90 | renderer.domElement.webkitRequestFullscreen(); 91 | } else { 92 | renderer.domElement.requestFullscreen(); 93 | } 94 | } 95 | 96 | function randomizeGeometry() { 97 | let geo; 98 | do { 99 | geo = Math.floor(Math.random() * geometries.length); 100 | } while (geo === currentGeometry); 101 | mesh.geometry = geometries[geo]; 102 | currentGeometry = geo; 103 | } 104 | 105 | function randomize() { 106 | randomizeGeometry(); 107 | randomizeColors(renderer); 108 | } 109 | 110 | let running = true; 111 | 112 | window.addEventListener("keydown", (e) => { 113 | if (e.code === "KeyR") { 114 | randomize(); 115 | } 116 | if (e.code === "KeyS") { 117 | randomizeGeometry(); 118 | } 119 | if (e.code === "KeyM") { 120 | randomizeColors(renderer); 121 | } 122 | if (e.code === "Space") { 123 | running = !running; 124 | } 125 | if (e.code === "KeyF") { 126 | goFullscreen(); 127 | } 128 | }); 129 | 130 | document.querySelector("#randomizeShapeBtn").addEventListener("click", (e) => { 131 | randomizeGeometry(); 132 | }); 133 | 134 | document 135 | .querySelector("#randomizeMaterialBtn") 136 | .addEventListener("click", (e) => { 137 | randomizeColors(renderer); 138 | }); 139 | 140 | document.querySelector("#randomizeAllBtn").addEventListener("click", (e) => { 141 | randomize(); 142 | }); 143 | 144 | document.querySelector("#pauseBtn").addEventListener("click", (e) => { 145 | running = !running; 146 | }); 147 | 148 | document.querySelector("#fullscreenBtn").addEventListener("click", (e) => { 149 | goFullscreen(); 150 | }); 151 | 152 | renderer.setClearColor(0x101010, 1); 153 | 154 | function myResize(w, h, dPR) { 155 | post.setSize(w, h, dPR); 156 | } 157 | addResize(myResize); 158 | 159 | resize(); 160 | init(); 161 | 162 | // window.start = () => { 163 | // frames = 0; 164 | // window.capturer.start(); 165 | // }; 166 | -------------------------------------------------------------------------------- /27/post.js: -------------------------------------------------------------------------------- 1 | import { 2 | RawShaderMaterial, 3 | RGBAFormat, 4 | UnsignedByteType, 5 | LinearFilter, 6 | ClampToEdgeWrapping, 7 | GLSL3, 8 | } from "../third_party/three.module.js"; 9 | import { shader as orthoVertexShader } from "../shaders/ortho.js"; 10 | import { ShaderPass } from "../modules/ShaderPass.js"; 11 | import { shader as vignette } from "../shaders/vignette.js"; 12 | import { shader as noise } from "../shaders/noise.js"; 13 | import { shader as chromaticAberration } from "../shaders/chromatic-aberration.js"; 14 | import { getFBO } from "../modules/fbo.js"; 15 | 16 | const finalFragmentShader = ` 17 | precision highp float; 18 | 19 | uniform sampler2D inputTexture; 20 | 21 | uniform float vignetteBoost; 22 | uniform float vignetteReduction; 23 | 24 | uniform float time; 25 | 26 | in vec2 vUv; 27 | 28 | out vec4 fragColor; 29 | 30 | ${vignette} 31 | 32 | ${noise} 33 | 34 | void main() { 35 | vec4 color = texture(inputTexture, vUv); 36 | fragColor = color; 37 | fragColor *= vignette(vUv, vignetteBoost, vignetteReduction); 38 | fragColor += color.a * .01 * noise(gl_FragCoord.xy, time/100.); 39 | fragColor.a = 1.; 40 | } 41 | `; 42 | 43 | const colorFragmentShader = `precision highp float; 44 | 45 | uniform sampler2D inputTexture; 46 | uniform float time; 47 | 48 | in vec2 vUv; 49 | 50 | out vec4 fragColor; 51 | 52 | ${chromaticAberration} 53 | ${noise} 54 | 55 | void main() { 56 | vec2 uv = .8 * (vUv - .5) + .5; 57 | fragColor = chromaticAberration(inputTexture, uv, .1, (vUv-.5) ); 58 | fragColor += .05 * noise(gl_FragCoord.xy, time/100.); 59 | fragColor.a = 1.; 60 | }`; 61 | 62 | class Post { 63 | constructor(renderer, params = {}) { 64 | this.renderer = renderer; 65 | 66 | this.colorFBO = getFBO(1, 1, { samples: 4 }); 67 | 68 | this.finalShader = new RawShaderMaterial({ 69 | uniforms: { 70 | vignetteBoost: { value: params.vignetteBoost || 1.3 }, 71 | vignetteReduction: { value: params.vignetteReduction || 1.2 }, 72 | inputTexture: { value: null }, 73 | blur0Texture: { value: null }, 74 | blur1Texture: { value: null }, 75 | blur2Texture: { value: null }, 76 | blur3Texture: { value: null }, 77 | blur4Texture: { value: null }, 78 | time: { value: 0 }, 79 | }, 80 | vertexShader: orthoVertexShader, 81 | fragmentShader: finalFragmentShader, 82 | glslVersion: GLSL3, 83 | }); 84 | this.finalPass = new ShaderPass(this.finalShader, { 85 | format: RGBAFormat, 86 | type: UnsignedByteType, 87 | minFilter: LinearFilter, 88 | magFilter: LinearFilter, 89 | wrapS: ClampToEdgeWrapping, 90 | wrapT: ClampToEdgeWrapping, 91 | }); 92 | 93 | const rgbShader = new RawShaderMaterial({ 94 | uniforms: { 95 | inputTexture: { value: this.finalPass.texture }, 96 | time: { value: 0 }, 97 | }, 98 | vertexShader: orthoVertexShader, 99 | fragmentShader: colorFragmentShader, 100 | glslVersion: GLSL3, 101 | }); 102 | this.rgbPass = new ShaderPass(rgbShader); 103 | } 104 | 105 | setSize(w0, h0, dpr) { 106 | const w = w0 * dpr; 107 | const h = h0 * dpr; 108 | this.colorFBO.setSize(w, h); 109 | this.finalPass.setSize(w, h); 110 | this.rgbPass.setSize(w, h); 111 | } 112 | 113 | render(scene, camera) { 114 | this.renderer.setRenderTarget(this.colorFBO); 115 | this.renderer.render(scene, camera); 116 | this.renderer.setRenderTarget(null); 117 | 118 | this.finalPass.shader.uniforms.inputTexture.value = this.colorFBO.texture; 119 | 120 | this.finalPass.render(this.renderer); 121 | 122 | this.rgbPass.render(this.renderer, true); 123 | } 124 | } 125 | 126 | export { Post }; 127 | -------------------------------------------------------------------------------- /3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Codevember 2022 - 03 - Reflect 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 20 | 21 | 22 | 23 | 27 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 |
39 |

Codevember 2022

40 |

03. Reflect

41 |

42 | Click and drag to move the camera.
43 | Press space to stop the animation.
44 | Press F to go fullscren. 45 |

46 |

47 | 48 | 49 |

50 |

51 | Made with 52 | three.js
53 |

54 | 55 |
56 | 57 |
58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /3/post.js: -------------------------------------------------------------------------------- 1 | import { 2 | RawShaderMaterial, 3 | HalfFloatType, 4 | NearestFilter, 5 | RGBAFormat, 6 | UnsignedByteType, 7 | LinearFilter, 8 | sRGBEncoding, 9 | ClampToEdgeWrapping, 10 | BackSide, 11 | FrontSide, 12 | Vector2, 13 | GLSL3, 14 | Vector3, 15 | DataTexture3D, 16 | RedFormat, 17 | FloatType, 18 | } from "../third_party/three.module.js"; 19 | import { getFBO } from "../modules/fbo.js"; 20 | import { shader as orthoVertexShader } from "../shaders/ortho.js"; 21 | import { ShaderPass } from "../modules/ShaderPass.js"; 22 | import { shader as vignette } from "../shaders/vignette.js"; 23 | import { shader as noise } from "../shaders/noise.js"; 24 | import { shader as screen } from "../shaders/screen.js"; 25 | // import { shader as fxaa } from "../shaders/fxaa.js"; 26 | // import { shader as softLight } from "../shaders/soft-light.js"; 27 | // import { shader as colorDodge } from "../shaders/color-dodge.js"; 28 | // import { shader as rgbShift } from "../shaders/rgb-shift.js"; 29 | import { BloomPass } from "../modules/bloomPass.js"; 30 | 31 | const finalFragmentShader = ` 32 | precision highp float; 33 | 34 | uniform vec2 resolution; 35 | uniform sampler2D inputTexture; 36 | 37 | uniform sampler2D blur0Texture; 38 | uniform sampler2D blur1Texture; 39 | uniform sampler2D blur2Texture; 40 | uniform sampler2D blur3Texture; 41 | uniform sampler2D blur4Texture; 42 | 43 | uniform float vignetteBoost; 44 | uniform float vignetteReduction; 45 | 46 | uniform float time; 47 | 48 | in vec2 vUv; 49 | 50 | out vec4 fragColor; 51 | 52 | ${vignette} 53 | 54 | ${noise} 55 | 56 | ${screen} 57 | 58 | void main() { 59 | vec4 b0 = texture(blur0Texture, vUv); 60 | vec4 b1 = texture(blur1Texture, vUv); 61 | vec4 b2 = texture(blur2Texture, vUv); 62 | vec4 b3 = texture(blur3Texture, vUv); 63 | vec4 b4 = texture(blur4Texture, vUv); 64 | 65 | vec4 color = texture(inputTexture, vUv); 66 | 67 | float f= 40.; 68 | vec4 b = b0 / f; 69 | b += 2.*b1 / f; 70 | b += 4.*b2 / f; 71 | b += 8.*b3 / f; 72 | b += 16.*b4 / f; 73 | 74 | fragColor = screen(color, b, .5); 75 | fragColor *= vignette(vUv, vignetteBoost, vignetteReduction); 76 | fragColor += .01 * noise(gl_FragCoord.xy, time); 77 | fragColor.a = 1.; 78 | } 79 | `; 80 | 81 | const colorFragmentShader = `precision highp float; 82 | 83 | uniform sampler2D inputTexture; 84 | 85 | in vec2 vUv; 86 | 87 | out vec4 fragColor; 88 | 89 | void main() { 90 | vec2 size = vec2(textureSize(inputTexture, 0)); 91 | int steps = 10; 92 | float total = 0.; 93 | float fSteps = float(steps); 94 | vec4 accum = vec4(0.); 95 | for( int i = 0; i < steps; i++){ 96 | vec2 inc = 20. * float(i) / (fSteps*size); 97 | vec2 dir = vUv-.5; 98 | vec4 r = texture(inputTexture, vUv - dir * inc); 99 | vec4 g = texture(inputTexture, vUv); 100 | vec4 b = texture(inputTexture, vUv + dir * inc); 101 | float w = float(steps - i)/fSteps; 102 | accum += vec4(r.r, g.g, b.b, 0.) * w; 103 | total += w; 104 | } 105 | accum /= total; 106 | fragColor = vec4(accum.rgb , 1.); 107 | }`; 108 | 109 | class Post { 110 | constructor(renderer, params = {}) { 111 | this.renderer = renderer; 112 | 113 | this.colorFBO = getFBO(1, 1, { encoding: sRGBEncoding, samples: 4 }); 114 | 115 | this.colorShader = new RawShaderMaterial({ 116 | uniforms: { 117 | inputTexture: { value: this.colorFBO.texture }, 118 | }, 119 | vertexShader: orthoVertexShader, 120 | fragmentShader: colorFragmentShader, 121 | glslVersion: GLSL3, 122 | }); 123 | 124 | this.colorPass = new ShaderPass(this.colorShader, { 125 | format: RGBAFormat, 126 | encoding: sRGBEncoding, 127 | type: UnsignedByteType, 128 | minFilter: LinearFilter, 129 | magFilter: LinearFilter, 130 | wrapS: ClampToEdgeWrapping, 131 | wrapT: ClampToEdgeWrapping, 132 | }); 133 | 134 | this.finalShader = new RawShaderMaterial({ 135 | uniforms: { 136 | resolution: { value: new Vector2(1, 1) }, 137 | vignetteBoost: { value: params.vignetteBoost || 1.2 }, 138 | vignetteReduction: { value: params.vignetteReduction || 0.8 }, 139 | inputTexture: { value: this.colorPass.texture }, 140 | blur0Texture: { value: null }, 141 | blur1Texture: { value: null }, 142 | blur2Texture: { value: null }, 143 | blur3Texture: { value: null }, 144 | blur4Texture: { value: null }, 145 | time: { value: 0 }, 146 | }, 147 | vertexShader: orthoVertexShader, 148 | fragmentShader: finalFragmentShader, 149 | glslVersion: GLSL3, 150 | }); 151 | this.finalPass = new ShaderPass(this.finalShader, { 152 | format: RGBAFormat, 153 | encoding: sRGBEncoding, 154 | type: UnsignedByteType, 155 | minFilter: LinearFilter, 156 | magFilter: LinearFilter, 157 | wrapS: ClampToEdgeWrapping, 158 | wrapT: ClampToEdgeWrapping, 159 | }); 160 | 161 | this.bloomPass = new BloomPass(5, 5); 162 | } 163 | 164 | setSize(w, h) { 165 | this.colorFBO.setSize(w, h); 166 | this.colorPass.setSize(w, h); 167 | this.finalPass.setSize(w, h); 168 | this.finalShader.uniforms.resolution.value.set(w, h); 169 | this.bloomPass.setSize(w, h); 170 | } 171 | 172 | render(scene, camera) { 173 | this.renderer.setRenderTarget(this.colorFBO); 174 | this.renderer.render(scene, camera, this.colorFBO); 175 | this.renderer.setRenderTarget(null); 176 | 177 | this.colorPass.shader.uniforms.inputTexture.value = this.colorFBO.texture; 178 | this.colorPass.render(this.renderer); 179 | 180 | this.bloomPass.source = this.colorPass.texture; 181 | this.bloomPass.render(this.renderer); 182 | 183 | this.finalPass.shader.uniforms.blur0Texture.value = 184 | this.bloomPass.blurPasses[0].texture; 185 | this.finalPass.shader.uniforms.blur1Texture.value = 186 | this.bloomPass.blurPasses[1].texture; 187 | this.finalPass.shader.uniforms.blur2Texture.value = 188 | this.bloomPass.blurPasses[2].texture; 189 | this.finalPass.shader.uniforms.blur3Texture.value = 190 | this.bloomPass.blurPasses[3].texture; 191 | this.finalPass.shader.uniforms.blur4Texture.value = 192 | this.bloomPass.blurPasses[4].texture; 193 | this.finalPass.shader.uniforms.time.value = Math.random() * 100000; 194 | 195 | this.finalPass.render(this.renderer, true); 196 | } 197 | } 198 | 199 | export { Post }; 200 | -------------------------------------------------------------------------------- /4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Codevember 2022 - 04 - Fragment 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |

Codevember 2022

34 |

04. Fragment

35 |

36 | Click and drag to move the camera.
37 | Press space to stop the animation.
38 | Press R to randomize.
39 | Press F to go fullscren. 40 |

41 |

42 | 43 | 44 | 45 |

46 |

47 | Made with 48 | three.js
49 |

50 | 51 |
52 | 53 |
54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /5/disc.js: -------------------------------------------------------------------------------- 1 | import { 2 | Mesh, 3 | PlaneGeometry, 4 | RawShaderMaterial, 5 | GLSL3, 6 | } from "../third_party/three.module.js"; 7 | 8 | const vertexShader = `precision highp float; 9 | 10 | in vec3 position; 11 | in vec2 uv; 12 | 13 | uniform mat4 modelViewMatrix; 14 | uniform mat4 projectionMatrix; 15 | 16 | out vec2 vUv; 17 | 18 | void main() { 19 | vUv = position.xy; 20 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.); 21 | }`; 22 | 23 | const fragmentShader = `precision highp float; 24 | 25 | in vec2 vUv; 26 | 27 | uniform float time; 28 | 29 | out vec4 color; 30 | 31 | // https://www.shadertoy.com/view/wdcSzr 32 | 33 | float fog(in vec2 uv) { 34 | vec2 q=2.0*uv; 35 | 36 | for(float i=1.0;i<40.0;i*=1.1){ 37 | vec2 o=q; 38 | o.x+=(0.5/i)*cos(i*q.y+time*0.297+0.03*i)+1.3; 39 | o.y+=(0.5/i)*cos(i*q.x+time*0.414+0.03*(i+10.0))+1.9; 40 | q=o; 41 | } 42 | 43 | vec3 col=vec3(0.5*sin(3.0*q.x)+0.5,0.5*sin(3.0*q.y)+0.5,sin(1.3*q.x+1.7*q.y)); 44 | float f=0.43*(col.x+col.y+col.z); 45 | 46 | return length(col.rgb); 47 | } 48 | 49 | void main() { 50 | float a = 1. - .5 * length(vUv); 51 | a = clamp(a, 0., 1.); 52 | a = pow(a, 2.); 53 | color= vec4(1., 1., 1., a); 54 | color.rgb *= .5 + .5 * fog(vUv); 55 | }`; 56 | 57 | const disc = new Mesh( 58 | new PlaneGeometry(4, 4), 59 | new RawShaderMaterial({ 60 | uniforms: { time: { value: 0 } }, 61 | vertexShader, 62 | fragmentShader, 63 | glslVersion: GLSL3, 64 | transparent: true, 65 | }) 66 | ); 67 | 68 | export { disc }; 69 | -------------------------------------------------------------------------------- /5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Codevember 2022 - 05 - Eclipse 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 20 | 21 | 22 | 23 | 27 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 |
39 |

Codevember 2022

40 |

05. Eclipse

41 |

42 | Press space to stop the animation.
43 | Press F to go fullscren. 44 |

45 |

46 | 47 | 48 |

49 |

50 | Made with 51 | three.js
52 |

53 | 54 |
55 | 56 |
57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /5/main.js: -------------------------------------------------------------------------------- 1 | import { 2 | scene, 3 | renderer, 4 | camera, 5 | addResize, 6 | resize, 7 | getControls, 8 | } from "../modules/renderer.js"; 9 | import { 10 | Matrix4, 11 | Mesh, 12 | MeshBasicMaterial, 13 | } from "../third_party/three.module.js"; 14 | import { disc } from "./disc.js"; 15 | import { OBJLoader } from "../third_party/OBJLoader.js"; 16 | import { getFBO } from "../modules/fbo.js"; 17 | import { Post } from "./post.js"; 18 | // import { capture } from "../modules/capture.js"; 19 | 20 | // const controls = getControls(); 21 | 22 | const post = new Post(renderer); 23 | 24 | renderer.shadowMap.enabled = true; 25 | 26 | scene.add(disc); 27 | 28 | camera.position.set(0, 0, 10); 29 | camera.lookAt(scene.position); 30 | 31 | const colorFBO = getFBO(1, 1, { samples: 4 }); 32 | const zoomFBO = getFBO(1, 1, { samples: 4 }); 33 | 34 | async function loadModel(file) { 35 | return new Promise((resolve, reject) => { 36 | const loader = new OBJLoader(); 37 | loader.load(file, resolve, null, reject); 38 | }); 39 | } 40 | 41 | async function loadIcosahedron() { 42 | const model = await loadModel("../assets/icosahedron.obj"); 43 | const geo = model.children[0].geometry; 44 | geo.center(); 45 | const scale = 0.5; 46 | geo.applyMatrix4(new Matrix4().makeScale(scale, scale, scale)); 47 | return geo; 48 | } 49 | 50 | let sun; 51 | let planet; 52 | async function init() { 53 | const geo = await loadIcosahedron(); 54 | planet = new Mesh(geo, new MeshBasicMaterial({ color: 0 })); 55 | scene.add(planet); 56 | planet.position.z = 2; 57 | 58 | sun = new Mesh(geo, new MeshBasicMaterial({ color: 0xffffff })); 59 | sun.scale.setScalar(1.2); 60 | scene.add(sun); 61 | 62 | render(); 63 | } 64 | 65 | let frames = 0; 66 | 67 | let time = 0; 68 | let prevTime = performance.now(); 69 | 70 | function render() { 71 | const t = performance.now(); 72 | const dt = t - prevTime; 73 | prevTime = t; 74 | 75 | if (running) { 76 | time += dt; 77 | 78 | disc.lookAt(camera.position); 79 | disc.material.uniforms.time.value = time / 500; 80 | 81 | camera.position.set(0, 0, 10 + 2 * Math.cos(time / 2000)); 82 | 83 | planet.position.x = 0.1 * Math.cos(time / 1500); 84 | planet.position.y = 0.1 * Math.cos(time / 1250); 85 | planet.position.z = 2 + Math.cos(time / 1000); 86 | 87 | planet.rotation.x += dt / 5000; 88 | planet.rotation.y += (dt / 5000) * 0.66; 89 | 90 | sun.rotation.x += (dt / 5000) * 0.37; 91 | sun.rotation.y += (dt / 5000) * 0.26; 92 | } 93 | 94 | // renderer.render(scene, camera); 95 | 96 | renderer.setRenderTarget(colorFBO); 97 | renderer.render(scene, camera); 98 | renderer.setRenderTarget(null); 99 | 100 | post.render(colorFBO.texture); 101 | 102 | // capture(renderer.domElement); 103 | 104 | // if (frames > 10 * 60 && window.capturer.capturing) { 105 | // window.capturer.stop(); 106 | // window.capturer.save(); 107 | // } 108 | // frames++; 109 | 110 | renderer.setAnimationLoop(render); 111 | } 112 | 113 | function goFullscreen() { 114 | if (renderer.domElement.webkitRequestFullscreen) { 115 | renderer.domElement.webkitRequestFullscreen(); 116 | } else { 117 | renderer.domElement.requestFullscreen(); 118 | } 119 | } 120 | 121 | let running = true; 122 | 123 | window.addEventListener("keydown", (e) => { 124 | if (e.code === "Space") { 125 | running = !running; 126 | } 127 | if (e.code === "KeyF") { 128 | goFullscreen(); 129 | } 130 | }); 131 | 132 | document.querySelector("#pauseBtn").addEventListener("click", (e) => { 133 | running = !running; 134 | }); 135 | 136 | document.querySelector("#fullscreenBtn").addEventListener("click", (e) => { 137 | goFullscreen(); 138 | }); 139 | 140 | function myResize(w, h, dPR) { 141 | colorFBO.setSize(w * dPR, h * dPR); 142 | zoomFBO.setSize(w * dPR, h * dPR); 143 | post.setSize(w, h, dPR); 144 | } 145 | addResize(myResize); 146 | 147 | resize(); 148 | init(); 149 | 150 | // window.start = () => { 151 | // frames = 0; 152 | // window.capturer.start(); 153 | // }; 154 | -------------------------------------------------------------------------------- /6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Codevember 2022 - 06 - Dune 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |

Codevember 2022

34 |

06. Dune

35 |

36 | Click and drag to move the camera.
37 | Press space to stop the animation.
38 | Press R to randomize.
39 | Press F to go fullscren. 40 |

41 |

42 | 43 | 44 | 45 |

46 |

47 | Made with 48 | three.js
49 |

50 | 51 |
52 | 53 |
54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /6/main.js: -------------------------------------------------------------------------------- 1 | import { 2 | scene, 3 | getControls, 4 | renderer, 5 | camera, 6 | addResize, 7 | resize, 8 | } from "../modules/renderer.js"; 9 | import { Group, Vector2 } from "../third_party/three.module.js"; 10 | import { group as dunes, step, randomize as randomizeDunes } from "./desert.js"; 11 | import { Post } from "./post.js"; 12 | import { randomInRange, mod, clamp, parabola } from "../modules/Maf.js"; 13 | // import { capture } from "../modules/capture.js"; 14 | 15 | scene.add(dunes); 16 | 17 | const post = new Post(renderer); 18 | 19 | renderer.shadowMap.enabled = true; 20 | 21 | const group = new Group(); 22 | scene.add(group); 23 | 24 | camera.position.set(10, 10, 10); 25 | camera.lookAt(scene.position); 26 | 27 | const controls = getControls(); 28 | // controls.minDistance = 3; 29 | // controls.maxDistance = 100; 30 | // controls.enablePan = false; 31 | 32 | function init() { 33 | randomize(); 34 | render(); 35 | } 36 | 37 | let frames = 0; 38 | 39 | let time = 0; 40 | let prevTime = performance.now(); 41 | const offset = new Vector2(0, 0); 42 | let rotation = 0; 43 | 44 | function render() { 45 | const t = performance.now(); 46 | const dt = t - prevTime; 47 | prevTime = t; 48 | 49 | if (running || invalidate) { 50 | if (running) { 51 | time += dt; 52 | } 53 | 54 | const a = time / 200000; 55 | const r = 100; 56 | offset.set(r * Math.cos(a), r * Math.sin(a)); 57 | rotation = a * 50; 58 | step(renderer, time / 20000, offset, rotation); 59 | invalidate = false; 60 | } 61 | 62 | // renderer.render(scene, camera); 63 | 64 | post.render(scene, camera); 65 | 66 | // capture(renderer.domElement); 67 | 68 | // if (frames > 10 * 60 && window.capturer.capturing) { 69 | // window.capturer.stop(); 70 | // window.capturer.save(); 71 | // } 72 | // frames++; 73 | 74 | renderer.setAnimationLoop(render); 75 | } 76 | 77 | function randomize() { 78 | renderer.setClearColor(randomizeDunes(), 1); 79 | invalidate = true; 80 | } 81 | 82 | function goFullscreen() { 83 | if (renderer.domElement.webkitRequestFullscreen) { 84 | renderer.domElement.webkitRequestFullscreen(); 85 | } else { 86 | renderer.domElement.requestFullscreen(); 87 | } 88 | } 89 | 90 | let running = true; 91 | let invalidate = false; 92 | 93 | window.addEventListener("keydown", (e) => { 94 | if (e.code === "KeyR") { 95 | randomize(); 96 | } 97 | if (e.code === "Space") { 98 | running = !running; 99 | } 100 | if (e.code === "KeyF") { 101 | goFullscreen(); 102 | } 103 | }); 104 | 105 | document.querySelector("#randomizeBtn").addEventListener("click", (e) => { 106 | randomize(); 107 | }); 108 | 109 | document.querySelector("#pauseBtn").addEventListener("click", (e) => { 110 | running = !running; 111 | }); 112 | 113 | document.querySelector("#fullscreenBtn").addEventListener("click", (e) => { 114 | goFullscreen(); 115 | }); 116 | 117 | function myResize(w, h, dPR) { 118 | post.setSize(w * dPR, h * dPR); 119 | } 120 | addResize(myResize); 121 | 122 | resize(); 123 | init(); 124 | 125 | // window.start = () => { 126 | // frames = 0; 127 | // window.capturer.start(); 128 | // }; 129 | -------------------------------------------------------------------------------- /6/post.js: -------------------------------------------------------------------------------- 1 | import { 2 | RawShaderMaterial, 3 | HalfFloatType, 4 | NearestFilter, 5 | RGBAFormat, 6 | UnsignedByteType, 7 | LinearFilter, 8 | ClampToEdgeWrapping, 9 | BackSide, 10 | FrontSide, 11 | Vector2, 12 | GLSL3, 13 | Vector3, 14 | DataTexture3D, 15 | RedFormat, 16 | FloatType, 17 | } from "../third_party/three.module.js"; 18 | import { getFBO } from "../modules/fbo.js"; 19 | import { shader as orthoVertexShader } from "../shaders/ortho.js"; 20 | import { ShaderPass } from "../modules/ShaderPass.js"; 21 | import { shader as vignette } from "../shaders/vignette.js"; 22 | import { shader as noise } from "../shaders/noise.js"; 23 | import { shader as screen } from "../shaders/screen.js"; 24 | import { shader as levels } from "../shaders/levels.js"; 25 | import { BloomPass } from "../modules/bloomPass.js"; 26 | 27 | const finalFragmentShader = ` 28 | precision highp float; 29 | 30 | uniform vec2 resolution; 31 | uniform sampler2D inputTexture; 32 | 33 | uniform sampler2D blur0Texture; 34 | uniform sampler2D blur1Texture; 35 | uniform sampler2D blur2Texture; 36 | uniform sampler2D blur3Texture; 37 | uniform sampler2D blur4Texture; 38 | 39 | uniform float vignetteBoost; 40 | uniform float vignetteReduction; 41 | 42 | uniform float time; 43 | 44 | in vec2 vUv; 45 | 46 | out vec4 fragColor; 47 | 48 | ${vignette} 49 | 50 | ${noise} 51 | 52 | ${screen} 53 | 54 | ${levels} 55 | 56 | void main() { 57 | vec4 b0 = texture(blur0Texture, vUv); 58 | vec4 b1 = texture(blur1Texture, vUv); 59 | vec4 b2 = texture(blur2Texture, vUv); 60 | vec4 b3 = texture(blur3Texture, vUv); 61 | vec4 b4 = texture(blur4Texture, vUv); 62 | 63 | vec4 color = texture(inputTexture, vUv); 64 | 65 | float s= 80.; 66 | vec4 b = b0 / s; 67 | b += 2.*b1 / s; 68 | b += 4.*b2 / s; 69 | b += 8.*b3 / s; 70 | b += 16.*b4 / s; 71 | 72 | fragColor = screen(color, b, 1.); 73 | fragColor *= vignette(vUv, vignetteBoost, vignetteReduction); 74 | fragColor += .01 * noise(gl_FragCoord.xy, time); 75 | fragColor.a = 1.; 76 | 77 | fragColor.rgb = finalLevels(fragColor.rgb, vec3(.1), vec3(1.), vec3(.8)); 78 | } 79 | `; 80 | 81 | const colorFragmentShader = `precision highp float; 82 | 83 | uniform sampler2D inputTexture; 84 | uniform float time; 85 | 86 | in vec2 vUv; 87 | 88 | out vec4 fragColor; 89 | 90 | ${noise} 91 | 92 | void main() { 93 | vec2 size = vec2(textureSize(inputTexture, 0)); 94 | int steps = 10; 95 | float total = 0.; 96 | float fSteps = float(steps); 97 | vec4 accum = vec4(0.); 98 | for( int i = 0; i < steps; i++){ 99 | vec2 inc = 20. * float(i) / (fSteps*size); 100 | vec2 dir = vUv-.5; 101 | vec4 r = texture(inputTexture, vUv - dir * inc); 102 | vec4 g = texture(inputTexture, vUv); 103 | vec4 b = texture(inputTexture, vUv + dir * inc); 104 | float w = float(steps - i)/fSteps; 105 | accum += vec4(r.r, g.g, b.b, 0.) * w; 106 | total += w; 107 | } 108 | accum /= total; 109 | fragColor = vec4(accum.rgb , 1.); 110 | fragColor += .01 * noise(gl_FragCoord.xy, time); 111 | }`; 112 | 113 | class Post { 114 | constructor(renderer, params = {}) { 115 | this.renderer = renderer; 116 | 117 | this.colorFBO = getFBO(1, 1, { samples: 4 }); 118 | 119 | this.colorShader = new RawShaderMaterial({ 120 | uniforms: { 121 | inputTexture: { value: this.colorFBO.texture }, 122 | time: { value: 0 }, 123 | }, 124 | vertexShader: orthoVertexShader, 125 | fragmentShader: colorFragmentShader, 126 | glslVersion: GLSL3, 127 | }); 128 | 129 | this.colorPass = new ShaderPass(this.colorShader, { 130 | format: RGBAFormat, 131 | type: UnsignedByteType, 132 | minFilter: LinearFilter, 133 | magFilter: LinearFilter, 134 | wrapS: ClampToEdgeWrapping, 135 | wrapT: ClampToEdgeWrapping, 136 | }); 137 | 138 | this.finalShader = new RawShaderMaterial({ 139 | uniforms: { 140 | resolution: { value: new Vector2(1, 1) }, 141 | vignetteBoost: { value: params.vignetteBoost || 1.2 }, 142 | vignetteReduction: { value: params.vignetteReduction || 1.1 }, 143 | inputTexture: { value: this.colorPass.texture }, 144 | blur0Texture: { value: null }, 145 | blur1Texture: { value: null }, 146 | blur2Texture: { value: null }, 147 | blur3Texture: { value: null }, 148 | blur4Texture: { value: null }, 149 | time: { value: 0 }, 150 | }, 151 | vertexShader: orthoVertexShader, 152 | fragmentShader: finalFragmentShader, 153 | glslVersion: GLSL3, 154 | }); 155 | this.finalPass = new ShaderPass(this.finalShader, { 156 | format: RGBAFormat, 157 | type: UnsignedByteType, 158 | minFilter: LinearFilter, 159 | magFilter: LinearFilter, 160 | wrapS: ClampToEdgeWrapping, 161 | wrapT: ClampToEdgeWrapping, 162 | }); 163 | 164 | this.bloomPass = new BloomPass(5, 5); 165 | } 166 | 167 | setSize(w, h) { 168 | this.colorFBO.setSize(w, h); 169 | this.colorPass.setSize(w, h); 170 | this.finalPass.setSize(w, h); 171 | this.finalShader.uniforms.resolution.value.set(w, h); 172 | this.bloomPass.setSize(w, h); 173 | } 174 | 175 | render(scene, camera) { 176 | const t = Math.random() * 100000; 177 | this.colorPass.shader.uniforms.time.value = t; 178 | this.renderer.setRenderTarget(this.colorFBO); 179 | this.renderer.render(scene, camera, this.colorFBO); 180 | this.renderer.setRenderTarget(null); 181 | 182 | this.colorPass.shader.uniforms.inputTexture.value = this.colorFBO.texture; 183 | this.colorPass.render(this.renderer); 184 | 185 | this.bloomPass.source = this.colorPass.texture; 186 | this.bloomPass.render(this.renderer); 187 | 188 | this.finalPass.shader.uniforms.blur0Texture.value = 189 | this.bloomPass.blurPasses[0].texture; 190 | this.finalPass.shader.uniforms.blur1Texture.value = 191 | this.bloomPass.blurPasses[1].texture; 192 | this.finalPass.shader.uniforms.blur2Texture.value = 193 | this.bloomPass.blurPasses[2].texture; 194 | this.finalPass.shader.uniforms.blur3Texture.value = 195 | this.bloomPass.blurPasses[3].texture; 196 | this.finalPass.shader.uniforms.blur4Texture.value = 197 | this.bloomPass.blurPasses[4].texture; 198 | this.finalPass.shader.uniforms.time.value = t; 199 | 200 | this.finalPass.render(this.renderer, true); 201 | } 202 | } 203 | 204 | export { Post }; 205 | -------------------------------------------------------------------------------- /7/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Codevember 2022 - 07 - Pigment 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |

Codevember 2022

34 |

07. Pigment

35 |

36 | Click and drag to move the camera.
37 | Press space to stop the animation.
38 | Press R to randomize.
39 | Press F to go fullscren. 40 |

41 |

42 | Number of particles 43 |

44 |

45 | 46 | 47 | 48 |

49 |

50 | 51 | 52 | 53 |

54 |

55 | Made with 56 | three.js
57 |

58 | 59 |
60 | 61 |
62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /7/post.js: -------------------------------------------------------------------------------- 1 | import { 2 | RawShaderMaterial, 3 | HalfFloatType, 4 | NearestFilter, 5 | RGBAFormat, 6 | UnsignedByteType, 7 | LinearFilter, 8 | ClampToEdgeWrapping, 9 | BackSide, 10 | FrontSide, 11 | Vector2, 12 | GLSL3, 13 | Vector3, 14 | DataTexture3D, 15 | RedFormat, 16 | FloatType, 17 | } from "../third_party/three.module.js"; 18 | import { getFBO } from "../modules/fbo.js"; 19 | import { shader as orthoVertexShader } from "../shaders/ortho.js"; 20 | import { ShaderPass } from "../modules/ShaderPass.js"; 21 | import { shader as vignette } from "../shaders/vignette.js"; 22 | import { shader as noise } from "../shaders/noise.js"; 23 | import { shader as screen } from "../shaders/screen.js"; 24 | import { shader as levels } from "../shaders/levels.js"; 25 | import { BloomPass } from "../modules/bloomPass.js"; 26 | 27 | const finalFragmentShader = ` 28 | precision highp float; 29 | 30 | uniform vec2 resolution; 31 | uniform sampler2D inputTexture; 32 | 33 | uniform sampler2D blur0Texture; 34 | uniform sampler2D blur1Texture; 35 | uniform sampler2D blur2Texture; 36 | uniform sampler2D blur3Texture; 37 | uniform sampler2D blur4Texture; 38 | 39 | uniform float vignetteBoost; 40 | uniform float vignetteReduction; 41 | 42 | uniform float time; 43 | 44 | in vec2 vUv; 45 | 46 | out vec4 fragColor; 47 | 48 | ${vignette} 49 | 50 | ${noise} 51 | 52 | ${screen} 53 | 54 | ${levels} 55 | 56 | void main() { 57 | vec4 b0 = texture(blur0Texture, vUv); 58 | vec4 b1 = texture(blur1Texture, vUv); 59 | vec4 b2 = texture(blur2Texture, vUv); 60 | vec4 b3 = texture(blur3Texture, vUv); 61 | vec4 b4 = texture(blur4Texture, vUv); 62 | 63 | vec4 color = texture(inputTexture, vUv); 64 | 65 | float s= 40.; 66 | vec4 b = b0 / s; 67 | b += 2.*b1 / s; 68 | b += 4.*b2 / s; 69 | b += 8.*b3 / s; 70 | b += 16.*b4 / s; 71 | 72 | fragColor = screen(color, b, 1.); 73 | fragColor *= vignette(vUv, vignetteBoost, vignetteReduction); 74 | fragColor += .02 * noise(gl_FragCoord.xy, time); 75 | fragColor.a = 1.; 76 | 77 | fragColor.rgb = finalLevels(fragColor.rgb, vec3(.1), vec3(1.2), vec3(.9)); 78 | } 79 | `; 80 | 81 | class Post { 82 | constructor(renderer, params = {}) { 83 | this.renderer = renderer; 84 | 85 | this.colorFBO = getFBO(1, 1, { samples: 4 }); 86 | 87 | this.finalShader = new RawShaderMaterial({ 88 | uniforms: { 89 | resolution: { value: new Vector2(1, 1) }, 90 | vignetteBoost: { value: params.vignetteBoost || 1.2 }, 91 | vignetteReduction: { value: params.vignetteReduction || 0.8 }, 92 | inputTexture: { value: null }, 93 | blur0Texture: { value: null }, 94 | blur1Texture: { value: null }, 95 | blur2Texture: { value: null }, 96 | blur3Texture: { value: null }, 97 | blur4Texture: { value: null }, 98 | time: { value: 0 }, 99 | }, 100 | vertexShader: orthoVertexShader, 101 | fragmentShader: finalFragmentShader, 102 | glslVersion: GLSL3, 103 | }); 104 | this.finalPass = new ShaderPass(this.finalShader, { 105 | format: RGBAFormat, 106 | type: UnsignedByteType, 107 | minFilter: LinearFilter, 108 | magFilter: LinearFilter, 109 | wrapS: ClampToEdgeWrapping, 110 | wrapT: ClampToEdgeWrapping, 111 | }); 112 | 113 | this.bloomPass = new BloomPass(3, 5); 114 | } 115 | 116 | setSize(w, h) { 117 | this.colorFBO.setSize(w, h); 118 | this.finalPass.setSize(w, h); 119 | this.finalShader.uniforms.resolution.value.set(w, h); 120 | this.bloomPass.setSize(w, h); 121 | } 122 | 123 | render(scene, camera) { 124 | const t = Math.random() * 100000; 125 | 126 | this.renderer.setRenderTarget(this.colorFBO); 127 | this.renderer.render(scene, camera); 128 | this.renderer.setRenderTarget(null); 129 | 130 | this.finalShader.uniforms.inputTexture.value = this.colorFBO.texture; 131 | this.bloomPass.source = this.colorFBO.texture; 132 | this.bloomPass.render(this.renderer); 133 | 134 | this.finalPass.shader.uniforms.blur0Texture.value = 135 | this.bloomPass.blurPasses[0].texture; 136 | this.finalPass.shader.uniforms.blur1Texture.value = 137 | this.bloomPass.blurPasses[1].texture; 138 | this.finalPass.shader.uniforms.blur2Texture.value = 139 | this.bloomPass.blurPasses[2].texture; 140 | this.finalPass.shader.uniforms.blur3Texture.value = 141 | this.bloomPass.blurPasses[3].texture; 142 | this.finalPass.shader.uniforms.blur4Texture.value = 143 | this.bloomPass.blurPasses[4].texture; 144 | this.finalPass.shader.uniforms.time.value = t; 145 | 146 | this.finalPass.render(this.renderer, true); 147 | } 148 | } 149 | 150 | export { Post }; 151 | -------------------------------------------------------------------------------- /8/RoundedPrismGeometry.js: -------------------------------------------------------------------------------- 1 | import { 2 | Vector3, 3 | CylinderGeometry, 4 | BufferGeometry, 5 | } from "../third_party/three.module.js"; 6 | 7 | class RoundedPrismGeometry extends CylinderGeometry { 8 | constructor(width, height, sides, border) { 9 | const f = Math.sqrt(3); 10 | super(width / f, width / f, height, sides); 11 | 12 | const vertices = this.attributes.position.array; 13 | const v = new Vector3(); 14 | for (let i = 0; i < vertices.length; i += 3) { 15 | v.set(vertices[i], vertices[i + 1], vertices[i + 2]); 16 | if (v.y > 0) { 17 | const a = Math.atan2(v.z, v.x); 18 | const r = Math.sqrt(v.x * v.x + v.z * v.z) - border; 19 | v.x = r * Math.cos(a); 20 | v.z = r * Math.sin(a); 21 | } 22 | if (v.y === 0) { 23 | v.y = width / 2 - border; 24 | } 25 | vertices[i] = v.x; 26 | vertices[i + 1] = v.y; 27 | vertices[i + 2] = v.z; 28 | } 29 | this.computeVertexNormals(); 30 | } 31 | } 32 | 33 | export { RoundedPrismGeometry }; 34 | -------------------------------------------------------------------------------- /8/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Codevember 2022 - 08 - Shadow 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |

Codevember 2022

34 |

08. Shadow

35 |

36 | Click and drag to move the camera.
37 | Press space to stop the animation.
38 | Press R to randomize.
39 | Press F to go fullscren. 40 |

41 |

42 | 43 | 44 | 45 |

46 |

47 | Made with 48 | three.js
49 |

50 | 51 |
52 | 53 |
54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /8/post.js: -------------------------------------------------------------------------------- 1 | import { 2 | RawShaderMaterial, 3 | RGBAFormat, 4 | UnsignedByteType, 5 | LinearFilter, 6 | ClampToEdgeWrapping, 7 | Vector2, 8 | GLSL3, 9 | } from "../third_party/three.module.js"; 10 | import { shader as orthoVertexShader } from "../shaders/ortho.js"; 11 | import { ShaderPass } from "../modules/ShaderPass.js"; 12 | import { shader as noise } from "../shaders/noise.js"; 13 | import { shader as levels } from "../shaders/levels.js"; 14 | import { shader as noiseCommon } from "../shaders/noise-common.js"; 15 | 16 | import { BloomPass } from "../modules/bloomPass.js"; 17 | 18 | const colorFragmentShader = `precision highp float; 19 | 20 | uniform sampler2D colorTexture; 21 | uniform sampler2D shadowTexture; 22 | uniform float time; 23 | 24 | in vec2 vUv; 25 | 26 | out vec4 fragColor; 27 | 28 | ${noise} 29 | ${noiseCommon} 30 | ${levels} 31 | 32 | float random(vec2 n, float offset ){ 33 | return .5 - fract(sin(dot(n.xy + vec2( offset, 0. ), vec2(12.9898, 78.233)))* 43758.5453); 34 | } 35 | 36 | void main() { 37 | fragColor = texture(colorTexture, vUv); 38 | fragColor.rgb = finalLevels(fragColor.rgb, vec3(.2), vec3(1.), vec3(.8)); 39 | fragColor += .05 * noise(gl_FragCoord.xy, time); 40 | fragColor.a = 1.; 41 | }`; 42 | 43 | class Post { 44 | constructor(renderer, params = {}) { 45 | this.renderer = renderer; 46 | 47 | this.colorShader = new RawShaderMaterial({ 48 | uniforms: { 49 | colorTexture: { value: null }, 50 | shadowTexture: { value: null }, 51 | time: { value: 0 }, 52 | }, 53 | vertexShader: orthoVertexShader, 54 | fragmentShader: colorFragmentShader, 55 | glslVersion: GLSL3, 56 | }); 57 | 58 | this.colorPass = new ShaderPass(this.colorShader, { 59 | format: RGBAFormat, 60 | type: UnsignedByteType, 61 | minFilter: LinearFilter, 62 | magFilter: LinearFilter, 63 | wrapS: ClampToEdgeWrapping, 64 | wrapT: ClampToEdgeWrapping, 65 | }); 66 | } 67 | 68 | setSize(w, h) { 69 | this.colorPass.setSize(w, h); 70 | } 71 | 72 | render(colorFBO) { 73 | this.colorPass.shader.uniforms.colorTexture.value = colorFBO; 74 | this.colorPass.render(this.renderer, true); 75 | } 76 | } 77 | 78 | export { Post }; 79 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 thespite 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # codevember-2022 2 | 3 | -------------------------------------------------------------------------------- /assets/box.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/assets/box.glb -------------------------------------------------------------------------------- /assets/dodecahedron.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/assets/dodecahedron.glb -------------------------------------------------------------------------------- /assets/icosahedron.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/assets/icosahedron.glb -------------------------------------------------------------------------------- /assets/matcap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/assets/matcap.png -------------------------------------------------------------------------------- /assets/matcap_1k.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/assets/matcap_1k.jpg -------------------------------------------------------------------------------- /assets/noise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/assets/noise.png -------------------------------------------------------------------------------- /assets/studio_small_03_1k.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/assets/studio_small_03_1k.hdr -------------------------------------------------------------------------------- /assets/studio_small_03_2k.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/assets/studio_small_03_2k.hdr -------------------------------------------------------------------------------- /assets/suzanne.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/assets/suzanne.glb -------------------------------------------------------------------------------- /modules/Fibonacci.js: -------------------------------------------------------------------------------- 1 | import { Vector3 } from "../third_party/three.module.js"; 2 | 3 | function pointsOnSphere(n) { 4 | const pts = []; 5 | const inc = Math.PI * (3 - Math.sqrt(5)); 6 | const off = 2.0 / n; 7 | let r; 8 | var phi; 9 | let dmin = 10000; 10 | const prev = new Vector3(); 11 | const cur = new Vector3(); 12 | 13 | for (var k = 0; k < n; k++) { 14 | cur.y = k * off - 1 + off / 2; 15 | r = Math.sqrt(1 - cur.y * cur.y); 16 | phi = k * inc; 17 | cur.x = Math.cos(phi) * r; 18 | cur.z = Math.sin(phi) * r; 19 | 20 | const dist = cur.distanceTo(prev); 21 | if (dist < dmin) dmin = dist; 22 | 23 | pts.push(cur.clone()); 24 | prev.copy(cur); 25 | } 26 | 27 | return pts; 28 | } 29 | 30 | export { pointsOnSphere }; 31 | -------------------------------------------------------------------------------- /modules/Shader3DPass.js: -------------------------------------------------------------------------------- 1 | import { 2 | OrthographicCamera, 3 | Scene, 4 | Mesh, 5 | PlaneGeometry, 6 | WebGL3DRenderTarget, 7 | ClampToEdgeWrapping, 8 | LinearFilter, 9 | RGBAFormat, 10 | UnsignedByteType, 11 | } from "../third_party/three.module.js"; 12 | 13 | class Shader3DPass { 14 | constructor(shader, options = {}) { 15 | this.shader = shader; 16 | this.orthoScene = new Scene(); 17 | this.fbo = new WebGL3DRenderTarget(1, 1, 1); 18 | this.fbo.texture.format = options.format || RGBAFormat; 19 | this.fbo.texture.type = options.type || UnsignedByteType; 20 | this.fbo.texture.wrapS = options.wrapS || ClampToEdgeWrapping; 21 | this.fbo.texture.wrapT = options.wrapT || ClampToEdgeWrapping; 22 | this.fbo.texture.minFilter = options.minFilter || LinearFilter; 23 | this.fbo.texture.magFilter = options.magFilter || LinearFilter; 24 | this.fbo.stencilBuffer = options.stencilBuffer || false; 25 | this.fbo.depthBuffer = options.depthBuffer || false; 26 | this.orthoCamera = new OrthographicCamera(-1, -1, -1, -1, 0, 1); 27 | this.orthoQuad = new Mesh(new PlaneGeometry(2, 2), this.shader); 28 | this.orthoQuad.scale.set(1, 1, 1); 29 | this.orthoScene.add(this.orthoQuad); 30 | this.texture = this.fbo.texture; 31 | } 32 | 33 | render(renderer, z) { 34 | renderer.setRenderTarget(this.fbo, z); 35 | renderer.render(this.orthoScene, this.orthoCamera); 36 | renderer.setRenderTarget(null); 37 | } 38 | 39 | setSize(width, height, depth) { 40 | this.fbo.setSize(width, height, depth); 41 | this.orthoQuad.scale.set(width, height, 1); 42 | this.orthoCamera.left = -width / 2; 43 | this.orthoCamera.right = width / 2; 44 | this.orthoCamera.top = height / 2; 45 | this.orthoCamera.bottom = -height / 2; 46 | this.orthoCamera.updateProjectionMatrix(); 47 | } 48 | } 49 | 50 | export { Shader3DPass }; 51 | -------------------------------------------------------------------------------- /modules/Shader3DPingPongPass.js: -------------------------------------------------------------------------------- 1 | import { 2 | OrthographicCamera, 3 | Scene, 4 | Mesh, 5 | WebGL3DRenderTarget, 6 | ClampToEdgeWrapping, 7 | LinearFilter, 8 | RGBAFormat, 9 | UnsignedByteType, 10 | PlaneGeometry, 11 | } from "../third_party/three.module.js"; 12 | 13 | class Shader3DPingPongPass { 14 | constructor(shader, options = {}) { 15 | this.shader = shader; 16 | this.orthoScene = new Scene(); 17 | this.fbo = new WebGL3DRenderTarget(1, 1, 1); 18 | this.fbo.texture.format = options.format || RGBAFormat; 19 | this.fbo.texture.type = options.type || UnsignedByteType; 20 | this.fbo.texture.wrapR = options.wrapR || ClampToEdgeWrapping; 21 | this.fbo.texture.wrapS = options.wrapS || ClampToEdgeWrapping; 22 | this.fbo.texture.wrapT = options.wrapT || ClampToEdgeWrapping; 23 | this.fbo.texture.minFilter = options.minFilter || LinearFilter; 24 | this.fbo.texture.magFilter = options.magFilter || LinearFilter; 25 | this.fbo.stencilBuffer = options.stencilBuffer || false; 26 | this.fbo.depthBuffer = options.depthBuffer || false; 27 | this.fbos = [this.fbo, this.fbo.clone()]; 28 | this.currentFBO = 0; 29 | this.orthoCamera = new OrthographicCamera(-1, -1, -1, -1, 0, 1); 30 | this.orthoQuad = new Mesh(new PlaneGeometry(2, 2), this.shader); 31 | this.orthoQuad.scale.set(1, 1, 1); 32 | this.orthoScene.add(this.orthoQuad); 33 | } 34 | 35 | render(renderer, slice) { 36 | renderer.setRenderTarget(this.fbos[1 - this.currentFBO], slice); 37 | renderer.render(this.orthoScene, this.orthoCamera); 38 | renderer.setRenderTarget(null); 39 | } 40 | 41 | flip() { 42 | this.currentFBO = 1 - this.currentFBO; 43 | } 44 | 45 | get current() { 46 | return this.fbos[this.currentFBO]; 47 | } 48 | 49 | get prev() { 50 | return this.fbos[1 - this.currentFBO]; 51 | } 52 | 53 | get texture() { 54 | return this.current.texture; 55 | } 56 | 57 | setSize(width, height, depth) { 58 | this.orthoQuad.scale.set(width, height); 59 | this.fbos[0].setSize(width, height, depth); 60 | this.fbos[1].setSize(width, height, depth); 61 | } 62 | } 63 | 64 | export { Shader3DPingPongPass }; 65 | -------------------------------------------------------------------------------- /modules/ShaderPass.js: -------------------------------------------------------------------------------- 1 | import { 2 | OrthographicCamera, 3 | Scene, 4 | Mesh, 5 | PlaneGeometry, 6 | } from "../third_party/three.module.js"; 7 | import { getFBO } from "./fbo.js"; 8 | 9 | class ShaderPass { 10 | constructor(shader, options = {}, antialiased) { 11 | this.shader = shader; 12 | this.orthoScene = new Scene(); 13 | this.fbo = getFBO(1, 1, options, antialiased); 14 | this.orthoCamera = new OrthographicCamera(-1, -1, -1, -1, 0, 1); 15 | this.orthoQuad = new Mesh(new PlaneGeometry(2, 2), this.shader); 16 | this.orthoQuad.scale.set(1, 1, 1); 17 | this.orthoScene.add(this.orthoQuad); 18 | this.texture = this.fbo.texture; 19 | } 20 | 21 | render(renderer, final) { 22 | if (!final) { 23 | renderer.setRenderTarget(this.fbo); 24 | } 25 | renderer.render(this.orthoScene, this.orthoCamera); 26 | renderer.setRenderTarget(null); 27 | } 28 | 29 | setSize(width, height) { 30 | this.fbo.setSize(width, height); 31 | this.orthoQuad.scale.set(width, height, 1); 32 | } 33 | } 34 | 35 | export { ShaderPass }; 36 | -------------------------------------------------------------------------------- /modules/ShaderPingPongPass.js: -------------------------------------------------------------------------------- 1 | import { 2 | OrthographicCamera, 3 | Scene, 4 | Mesh, 5 | PlaneGeometry, 6 | } from "../third_party/three.module.js"; 7 | import { getFBO } from "./fbo.js"; 8 | 9 | class ShaderPingPongPass { 10 | constructor(shader, options = {}) { 11 | this.shader = shader; 12 | this.orthoScene = new Scene(); 13 | this.fbo = getFBO(1, 1, options); 14 | this.fbos = [this.fbo, this.fbo.clone()]; 15 | this.currentFBO = 0; 16 | this.orthoCamera = new OrthographicCamera(-1, -1, -1, -1, 0, 1); 17 | this.orthoQuad = new Mesh(new PlaneGeometry(2, 2), this.shader); 18 | this.orthoQuad.scale.set(1, 1, 1); 19 | this.orthoScene.add(this.orthoQuad); 20 | } 21 | 22 | render(renderer, final) { 23 | if (!final) { 24 | renderer.setRenderTarget(this.fbos[1 - this.currentFBO]); 25 | } 26 | renderer.render(this.orthoScene, this.orthoCamera); 27 | renderer.setRenderTarget(null); 28 | this.currentFBO = 1 - this.currentFBO; 29 | } 30 | 31 | get current() { 32 | return this.fbos[this.currentFBO]; 33 | } 34 | 35 | get prev() { 36 | return this.fbos[1 - this.currentFBO]; 37 | } 38 | 39 | get texture() { 40 | return this.current.texture; 41 | } 42 | 43 | setSize(width, height) { 44 | this.orthoQuad.scale.set(width, height, 1); 45 | this.fbos[0].setSize(width, height); 46 | this.fbos[1].setSize(width, height); 47 | } 48 | } 49 | 50 | export { ShaderPingPongPass }; 51 | -------------------------------------------------------------------------------- /modules/bloomPass.js: -------------------------------------------------------------------------------- 1 | import { 2 | RawShaderMaterial, 3 | Vector2, 4 | GLSL3, 5 | sRGBEncoding, 6 | RGBAFormat, 7 | } from "../third_party/three.module.js"; 8 | import { ShaderPingPongPass } from "./ShaderPingPongPass.js"; 9 | import { shader as orthoVs } from "../shaders/ortho.js"; 10 | import { shader as blurFs } from "../shaders/blur.js"; 11 | 12 | const blurShader = new RawShaderMaterial({ 13 | uniforms: { 14 | inputTexture: { value: null }, 15 | direction: { value: new Vector2(0, 1) }, 16 | }, 17 | vertexShader: orthoVs, 18 | fragmentShader: blurFs, 19 | glslVersion: GLSL3, 20 | }); 21 | 22 | class BloomPass { 23 | constructor(strength = 1, levels = 5) { 24 | this.strength = strength; 25 | this.levels = levels; 26 | this.blurPasses = []; 27 | this.width = 1; 28 | this.height = 1; 29 | this.aspectRatio = 1; 30 | for (let i = 0; i < this.levels; i++) { 31 | const blurPass = new ShaderPingPongPass(blurShader, { 32 | format: RGBAFormat, 33 | encoding: sRGBEncoding, 34 | }); 35 | this.blurPasses.push(blurPass); 36 | } 37 | } 38 | 39 | setSize(w, h) { 40 | this.width = w; 41 | this.height = h; 42 | this.aspectRatio = w / h; 43 | let tw = w; 44 | let th = h; 45 | for (let i = 0; i < this.levels; i++) { 46 | tw /= 2; 47 | th /= 2; 48 | tw = Math.round(tw); 49 | th = Math.round(th); 50 | this.blurPasses[i].setSize(tw, th); 51 | } 52 | } 53 | 54 | set source(texture) { 55 | blurShader.uniforms.inputTexture.value = texture; 56 | } 57 | 58 | render(renderer) { 59 | let offset = this.strength; 60 | const u = blurShader.uniforms; 61 | for (let j = 0; j < this.levels; j++) { 62 | const blurPass = this.blurPasses[j]; 63 | 64 | u.direction.value.set(offset, 0); 65 | blurPass.render(renderer); 66 | 67 | u.inputTexture.value = blurPass.current.texture; 68 | u.direction.value.set(0, offset / 2); 69 | 70 | blurPass.render(renderer); 71 | u.inputTexture.value = blurPass.current.texture; 72 | } 73 | } 74 | } 75 | 76 | export { BloomPass }; 77 | -------------------------------------------------------------------------------- /modules/capture.js: -------------------------------------------------------------------------------- 1 | import { timewarp } from "../ccapture2/timewarp.js"; 2 | import { CCapture } from "../ccapture2/ccapture.js"; 3 | 4 | const capturer = new CCapture({ 5 | format: "png", 6 | quality: 1, 7 | timewarp: window.timewarp, 8 | timeLimit: 60, 9 | }); 10 | 11 | async function start() { 12 | await capturer.start(); 13 | return capturer; 14 | } 15 | 16 | async function capture(canvas) { 17 | await capturer.capture(canvas); 18 | capturer.step(); 19 | } 20 | 21 | function stop() { 22 | capturer.stop(); 23 | capturer.save(); 24 | } 25 | 26 | window.capturer = capturer; 27 | 28 | window.start = start; 29 | window.stop = stop(); 30 | 31 | export { start, capture }; 32 | -------------------------------------------------------------------------------- /modules/curl.js: -------------------------------------------------------------------------------- 1 | import { Vector3 } from "../third_party/three.module.js"; 2 | import { simplex3 } from "../third_party/perlin.js"; 3 | import { randomInRange } from "./Maf.js"; 4 | 5 | const generateNoiseFunction = () => { 6 | const a = randomInRange(-100, 100); 7 | const b = randomInRange(-100, 100); 8 | const c = randomInRange(-100, 100); 9 | const d = randomInRange(-100, 100); 10 | const e = randomInRange(-100, 100); 11 | const f = randomInRange(-100, 100); 12 | console.log(`const func = seedFunc(${a},${b},${c},${d},${e},${f});`); 13 | return function (v) { 14 | const s = simplex3(v.x, v.y, v.z); 15 | const s1 = simplex3(v.y + a, v.z + b, v.x + c); 16 | const s2 = simplex3(v.z + c, v.x + d, v.y + f); 17 | return new Vector3(s, s1, s2); 18 | }; 19 | }; 20 | 21 | const seedFunc = (a, b, c, d, e, f) => { 22 | return function (v) { 23 | const s = simplex3(v.x, v.y, v.z); 24 | const s1 = simplex3(v.y + a, v.z + b, v.x + c); 25 | const s2 = simplex3(v.z + c, v.x + d, v.y + f); 26 | return new Vector3(s, s1, s2); 27 | }; 28 | }; 29 | 30 | const noiseFunc0 = seedFunc(-19.1, 33.4, 47.2, 74.2, -124.5, 99.4); 31 | 32 | const e = 0.1; 33 | const dx = new Vector3(e, 0.0, 0.0); 34 | const dy = new Vector3(0.0, e, 0.0); 35 | const dz = new Vector3(0.0, 0.0, e); 36 | const tmp = new Vector3(); 37 | const res = new Vector3(); 38 | 39 | const curl = (p, noiseFunc = noiseFunc0) => { 40 | const p_x0 = noiseFunc(tmp.copy(p).sub(dx)); 41 | const p_x1 = noiseFunc(tmp.copy(p).add(dx)); 42 | const p_y0 = noiseFunc(tmp.copy(p).sub(dy)); 43 | const p_y1 = noiseFunc(tmp.copy(p).add(dy)); 44 | const p_z0 = noiseFunc(tmp.copy(p).sub(dz)); 45 | const p_z1 = noiseFunc(tmp.copy(p).add(dz)); 46 | const x = p_y1.z - p_y0.z - p_z1.y + p_z0.y; 47 | const y = p_z1.x - p_z0.x - p_x1.z + p_x0.z; 48 | const z = p_x1.y - p_x0.y - p_y1.x + p_y0.x; 49 | const divisor = 1.0 / (2.0 * e); 50 | res.set(x, y, z).multiplyScalar(divisor).normalize(); 51 | return res; 52 | }; 53 | 54 | export { curl, generateNoiseFunction, seedFunc }; 55 | -------------------------------------------------------------------------------- /modules/easings.js: -------------------------------------------------------------------------------- 1 | const Easings = { 2 | Linear: function (t) { 3 | return t; 4 | }, 5 | InQuad: function (t) { 6 | return t * t; 7 | }, 8 | OutQuad: function (t) { 9 | return t * (2 - t); 10 | }, 11 | InOutQuad: function (t) { 12 | if ((t *= 2) < 1) { 13 | return 0.5 * t * t; 14 | } 15 | return -0.5 * (--t * (t - 2) - 1); 16 | }, 17 | InCubic: function (t) { 18 | return t * t * t; 19 | }, 20 | OutCubic: function (t) { 21 | return --t * t * t + 1; 22 | }, 23 | InOutCubic: function (t) { 24 | if ((t *= 2) < 1) { 25 | return 0.5 * t * t * t; 26 | } 27 | return 0.5 * ((t -= 2) * t * t + 2); 28 | }, 29 | InQuart: function (t) { 30 | return t * t * t * t; 31 | }, 32 | OutQuart: function (t) { 33 | return 1 - --t * t * t * t; 34 | }, 35 | InOutQuart: function (t) { 36 | if ((t *= 2) < 1) { 37 | return 0.5 * t * t * t * t; 38 | } 39 | return 0.5 * ((t -= 2) * t * t * t - 2); 40 | }, 41 | InQuint: function (t) { 42 | return t * t * t * t * t; 43 | }, 44 | OutQuint: function (t) { 45 | return --t * t * t * t * t + 1; 46 | }, 47 | InOutQuint: function (t) { 48 | if ((t *= 2) < 1) return 0.5 * t * t * t * t * t; 49 | return 0.5 * ((t -= 2) * t * t * t * t + 2); 50 | }, 51 | InSine: function (t) { 52 | return 1 - Math.cos((t * Math.PI) / 2); 53 | }, 54 | OutSine: function (t) { 55 | return Math.sin((t * Math.PI) / 2); 56 | }, 57 | InOutSine: function (t) { 58 | return 0.5 * (1 - Math.cos(Math.PI * t)); 59 | }, 60 | InBounce: function (t) { 61 | return 1 - outBounce(1 - t); 62 | }, 63 | OutBounce: function (t) { 64 | if (t < 0.36363636363636365) { 65 | return 7.5625 * t * t; 66 | } else if (t < 0.7272727272727273) { 67 | t = t - 0.5454545454545454; 68 | return 7.5625 * t * t + 0.75; 69 | } else if (t < 0.9090909090909091) { 70 | t = t - 0.8181818181818182; 71 | return 7.5625 * t * t + 0.9375; 72 | } else { 73 | t = t - 0.9545454545454546; 74 | return 7.5625 * t * t + 0.984375; 75 | } 76 | }, 77 | InOutBounce: function (t) { 78 | if (t < 0.5) { 79 | return Easings.InBounce(t * 2) * 0.5; 80 | } 81 | return Easings.OutBounce(t * 2 - 1) * 0.5 + 1 * 0.5; 82 | }, 83 | InElastic: function (t, amplitude, period) { 84 | if (typeof period == "undefined") { 85 | period = 0; 86 | } 87 | if (typeof amplitude == "undefined") { 88 | amplitude = 1; 89 | } 90 | var offset = 1.70158; 91 | 92 | if (t == 0) return 0; 93 | if (t == 1) return 1; 94 | 95 | if (!period) { 96 | period = 0.3; 97 | } 98 | 99 | if (amplitude < 1) { 100 | amplitude = 1; 101 | offset = period / 4; 102 | } else { 103 | offset = (period / (2 * Math.PI)) * Math.asin(1 / amplitude); 104 | } 105 | 106 | return -( 107 | amplitude * 108 | Math.pow(2, 10 * (t -= 1)) * 109 | Math.sin(((t - offset) * (Math.PI * 2)) / period) 110 | ); 111 | }, 112 | OutElastic: function (t, amplitude, period) { 113 | if (typeof period == "undefined") { 114 | period = 0; 115 | } 116 | if (typeof amplitude == "undefined") { 117 | amplitude = 1; 118 | } 119 | var offset = 1.70158; 120 | 121 | if (t == 0) return 0; 122 | if (t == 1) return 1; 123 | 124 | if (!period) { 125 | period = 0.3; 126 | } 127 | 128 | if (amplitude < 1) { 129 | amplitude = 1; 130 | offset = period / 4; 131 | } else { 132 | offset = (period / (2 * Math.PI)) * Math.asin(1 / amplitude); 133 | } 134 | 135 | return ( 136 | amplitude * 137 | Math.pow(2, -10 * t) * 138 | Math.sin(((t - offset) * (Math.PI * 2)) / period) + 139 | 1 140 | ); 141 | }, 142 | InOutElastic: function (t, amplitude, period) { 143 | var offset; 144 | t = t / 2 - 1; 145 | // escape early for 0 and 1 146 | if (t === 0 || t === 1) { 147 | return t; 148 | } 149 | if (!period) { 150 | period = 0.44999999999999996; 151 | } 152 | if (!amplitude) { 153 | amplitude = 1; 154 | offset = period / 4; 155 | } else { 156 | offset = (period / (Math.PI * 2.0)) * Math.asin(1 / amplitude); 157 | } 158 | return ( 159 | (amplitude * 160 | Math.pow(2, 10 * t) * 161 | Math.sin(((t - offset) * (Math.PI * 2)) / period)) / 162 | -2 163 | ); 164 | }, 165 | InExpo: function (t) { 166 | return Math.pow(2, 10 * (t - 1)); 167 | }, 168 | OutExpo: function (t) { 169 | return -Math.pow(2, -10 * t) + 1; 170 | }, 171 | InOutExpo: function (t) { 172 | if (t == 0) return 0; 173 | if (t == 1) return 1; 174 | if ((t /= 0.5) < 1) return 0.5 * Math.pow(2, 10 * (t - 1)); 175 | return 0.5 * (-Math.pow(2, -10 * --t) + 2); 176 | }, 177 | InCirc: function (t) { 178 | return -1 * (Math.sqrt(1 - t * t) - 1); 179 | }, 180 | OutCirc: function (t) { 181 | t = t - 1; 182 | return Math.sqrt(1 - t * t); 183 | }, 184 | InOutCirc: function (t) { 185 | var c = 1; 186 | if ((t /= 0.5) < 1) return -0.5 * (Math.sqrt(1 - t * t) - 1); 187 | return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); 188 | }, 189 | InBack: function (t, overshoot) { 190 | if (!overshoot && overshoot !== 0) { 191 | overshoot = 1.70158; 192 | } 193 | return 1 * t * t * ((overshoot + 1) * t - overshoot); 194 | }, 195 | OutBack: function (t, overshoot) { 196 | if (!overshoot && overshoot !== 0) { 197 | overshoot = 1.70158; 198 | } 199 | t = t - 1; 200 | return t * t * ((overshoot + 1) * t + overshoot) + 1; 201 | }, 202 | InOutBack: function (t, overshoot) { 203 | if (overshoot == undefined) overshoot = 1.70158; 204 | if ((t /= 0.5) < 1) 205 | return 0.5 * (t * t * (((overshoot *= 1.525) + 1) * t - overshoot)); 206 | return ( 207 | 0.5 * ((t -= 2) * t * (((overshoot *= 1.525) + 1) * t + overshoot) + 2) 208 | ); 209 | }, 210 | }; 211 | 212 | const outBounce = Easings.OutBounce; 213 | 214 | export { Easings }; 215 | -------------------------------------------------------------------------------- /modules/fbo.js: -------------------------------------------------------------------------------- 1 | import { 2 | WebGLRenderTarget, 3 | ClampToEdgeWrapping, 4 | LinearFilter, 5 | RGBAFormat, 6 | UnsignedByteType, 7 | } from "../third_party/three.module.js"; 8 | 9 | function getFBO(w, h, options = {}) { 10 | return new WebGLRenderTarget(w, h, { 11 | wrapS: options.wrapS || ClampToEdgeWrapping, 12 | wrapT: options.wrapT || ClampToEdgeWrapping, 13 | minFilter: options.minFilter || LinearFilter, 14 | magFilter: options.magFilter || LinearFilter, 15 | format: options.format || RGBAFormat, 16 | type: options.type || UnsignedByteType, 17 | stencilBuffer: options.stencilBuffer || false, 18 | depthBuffer: options.depthBuffer || true, 19 | samples: options.samples || 0, 20 | }); 21 | } 22 | 23 | export { getFBO }; 24 | -------------------------------------------------------------------------------- /modules/geometry-index.js: -------------------------------------------------------------------------------- 1 | import { Vector3 } from "../third_party/three.module.js"; 2 | 3 | function hash(p) { 4 | return `${p.x}:${p.y}:${p.z}`; 5 | } 6 | 7 | function toIndexed(geo) { 8 | const map = new Map(); 9 | 10 | const position = geo.attributes.position.array; 11 | const indices = []; 12 | 13 | for (let i = 0; i < geo.attributes.position.count; i++) { 14 | const p = new Vector3(); 15 | p.set(position[i * 3], position[i * 3 + 1], position[i * 3 + 2]); 16 | const h = hash(p); 17 | let cached = map.get(h); 18 | if (cached) { 19 | indices.push(cached.id); 20 | } else { 21 | map.set(h, { point: p, id: i }); 22 | indices.push(i); 23 | } 24 | } 25 | 26 | geo.setIndex(indices); 27 | 28 | return geo; 29 | } 30 | 31 | function weld(geo) { 32 | const map = new Map(); 33 | 34 | const position = geo.attributes.position.array; 35 | 36 | for (let i = 0; i < geo.attributes.position.count; i++) { 37 | const p = new Vector3(); 38 | p.set(position[i * 3], position[i * 3 + 1], position[i * 3 + 2]); 39 | const h = hash(p); 40 | let cached = map.get(h); 41 | if (cached) { 42 | } else { 43 | map.set(h, { point: p, id: i }); 44 | } 45 | } 46 | 47 | const indices = geo.index.array; 48 | for (let i = 0; i < indices.length; i++) { 49 | const p = new Vector3(); 50 | const id = indices[i]; 51 | p.set(position[id * 3], position[id * 3 + 1], position[id * 3 + 2]); 52 | const h = hash(p); 53 | let cached = map.get(h); 54 | indices[i] = cached.id; 55 | } 56 | 57 | return geo; 58 | } 59 | 60 | export { toIndexed, weld }; 61 | -------------------------------------------------------------------------------- /modules/gradient-linear.js: -------------------------------------------------------------------------------- 1 | import { Color } from "../third_party/three.module.js"; 2 | import { mix, clamp } from "./Maf.js"; 3 | 4 | class GradientLinear { 5 | constructor(colors) { 6 | this.colors = colors.map((c) => new Color(c)); 7 | } 8 | getAt(t) { 9 | t = clamp(t, 0, 1); 10 | const from = Math.floor(t * this.colors.length * 0.9999); 11 | const to = clamp(from + 1, 0, this.colors.length - 1); 12 | const fc = this.colors[from]; 13 | const ft = this.colors[to]; 14 | const p = (t - from / this.colors.length) / (1 / this.colors.length); 15 | const res = new Color(); 16 | res.r = mix(fc.r, ft.r, p); 17 | res.g = mix(fc.g, ft.g, p); 18 | res.b = mix(fc.b, ft.b, p); 19 | return res; 20 | } 21 | } 22 | 23 | export { GradientLinear }; 24 | -------------------------------------------------------------------------------- /modules/hdri.js: -------------------------------------------------------------------------------- 1 | import { 2 | FloatType, 3 | PMREMGenerator, 4 | sRGBEncoding, 5 | } from "../third_party/three.module.js"; 6 | import { RGBELoader } from "../third_party/RGBELoader.js"; 7 | 8 | async function initHdrEnv(filename, renderer) { 9 | return new Promise((resolve, reject) => { 10 | const pmremGenerator = new PMREMGenerator(renderer); 11 | pmremGenerator.compileEquirectangularShader(); 12 | new RGBELoader() 13 | .setDataType(FloatType) 14 | .setPath("../assets/") 15 | .load(filename, (texture) => { 16 | const radianceMap = pmremGenerator.fromEquirectangular(texture).texture; 17 | pmremGenerator.dispose(); 18 | // radianceMap.magFilter = LinearFilter; 19 | // radianceMap.minFilter = LinearMipmapLinearFilter; 20 | // radianceMap.encoding = sRGBEncoding; 21 | resolve(radianceMap); 22 | }); 23 | }); 24 | } 25 | 26 | export { initHdrEnv }; 27 | -------------------------------------------------------------------------------- /modules/models.js: -------------------------------------------------------------------------------- 1 | import { GLTFLoader } from "../third_party/GLTFLoader.js"; 2 | import { OBJLoader } from "../third_party/OBJLoader.js"; 3 | import { 4 | IcosahedronGeometry, 5 | Matrix4, 6 | Vector3, 7 | } from "../third_party/three.module.js"; 8 | import { simplex3 } from "../third_party/perlin.js"; 9 | import { weld, toIndexed } from "../modules/geometry-index.js"; 10 | 11 | async function loadOBJ(file) { 12 | return new Promise((resolve, reject) => { 13 | const loader = new OBJLoader(); 14 | loader.load(file, resolve, null, reject); 15 | }); 16 | } 17 | 18 | async function loadGLTF(file) { 19 | return new Promise((resolve, reject) => { 20 | const loader = new GLTFLoader(); 21 | loader.load(file, resolve, null, reject); 22 | }); 23 | } 24 | 25 | async function loadSuzanne() { 26 | const scene = await loadGLTF("../assets/suzanne.glb"); 27 | const geometry = scene.scenes[0].children[0].geometry; 28 | const mat = new Matrix4().makeRotationX(-Math.PI / 2); 29 | geometry.applyMatrix4(mat); 30 | geometry.scale(0.75, 0.75, 0.75); 31 | return geometry; 32 | } 33 | 34 | async function loadSuzanneHard() { 35 | const scene = await loadGLTF("../assets/suzanne-hard.glb"); 36 | const geometry = scene.scenes[0].children[0].geometry; 37 | const mat = new Matrix4().makeRotationX(-Math.PI / 2); 38 | geometry.applyMatrix4(mat); 39 | geometry.scale(0.75, 0.75, 0.75); 40 | return geometry; 41 | } 42 | 43 | async function loadTorusHard() { 44 | const scene = await loadGLTF("../assets/torus-hard.glb"); 45 | const geometry = scene.scenes[0].children[0].geometry; 46 | const mat = new Matrix4().makeRotationX(-Math.PI / 2); 47 | geometry.applyMatrix4(mat); 48 | geometry.scale(0.75, 0.75, 0.75); 49 | return geometry; 50 | } 51 | 52 | async function loadBunny() { 53 | const scene = await loadGLTF("../assets/stanford_bunny.glb"); 54 | const geometry = scene.scenes[0].children[0].geometry; 55 | geometry.center(); 56 | const mat = new Matrix4().makeRotationX(Math.PI / 2); 57 | geometry.applyMatrix4(mat); 58 | return geometry; 59 | } 60 | 61 | async function loadDragon() { 62 | const scene = await loadGLTF("../assets/dragon.glb"); 63 | const geometry = scene.scenes[0].children[0].geometry; 64 | geometry.center(); 65 | const mat = new Matrix4().makeRotationX(Math.PI / 2); 66 | geometry.applyMatrix4(mat); 67 | geometry.scale(2, 2, 2); 68 | return geometry; 69 | } 70 | async function loadIcosahedron() { 71 | const model = await loadOBJ("../assets/icosahedron.obj"); 72 | const geometry = model.children[0].geometry; 73 | geometry.center(); 74 | const scale = 0.5; 75 | geometry.applyMatrix4(new Matrix4().makeScale(scale, scale, scale)); 76 | return geometry; 77 | } 78 | 79 | async function loadDodecahedron() { 80 | const model = await loadOBJ("../assets/dodecahedron.obj"); 81 | const geometry = model.children[0].geometry; 82 | geometry.center(); 83 | const scale = 0.6; 84 | geometry.applyMatrix4(new Matrix4().makeScale(scale, scale, scale)); 85 | return geometry; 86 | } 87 | 88 | async function loadBox() { 89 | const model = await loadOBJ("../assets/box.obj"); 90 | const geometry = model.children[0].geometry; 91 | geometry.center(); 92 | const scale = 0.6; 93 | geometry.applyMatrix4(new Matrix4().makeScale(scale, scale, scale)); 94 | return geometry; 95 | } 96 | 97 | function generateBlob() { 98 | const geometry = weld(toIndexed(new IcosahedronGeometry(1, 30))); 99 | const vertices = geometry.attributes.position.array; 100 | const v = new Vector3(); 101 | const scale = 0.1; 102 | const noise = 1; 103 | for (let i = 0; i < vertices.length; i += 3) { 104 | v.set(vertices[i], vertices[i + 1], vertices[i + 2]); 105 | v.normalize().multiplyScalar(noise); 106 | const n = 107 | scale * (simplex3(v.x, v.y, v.z) + simplex3(2 * v.x, 2 * v.y, 2 * v.z)); 108 | v.multiplyScalar(1 + n); 109 | vertices[i] = v.x; 110 | vertices[i + 1] = v.y; 111 | vertices[i + 2] = v.z; 112 | } 113 | geometry.scale(0.75, 0.75, 0.75); 114 | geometry.computeVertexNormals(); 115 | return geometry; 116 | } 117 | 118 | export { 119 | loadSuzanne, 120 | loadIcosahedron, 121 | loadDodecahedron, 122 | loadBox, 123 | loadDragon, 124 | loadBunny, 125 | generateBlob, 126 | loadTorusHard, 127 | loadSuzanneHard, 128 | }; 129 | -------------------------------------------------------------------------------- /modules/orient2d.js: -------------------------------------------------------------------------------- 1 | import { 2 | epsilon, 3 | splitter, 4 | resulterrbound, 5 | estimate, 6 | vec, 7 | sum, 8 | } from "../third_party/util.js"; 9 | 10 | const ccwerrboundA = (3 + 16 * epsilon) * epsilon; 11 | const ccwerrboundB = (2 + 12 * epsilon) * epsilon; 12 | const ccwerrboundC = (9 + 64 * epsilon) * epsilon * epsilon; 13 | 14 | const B = vec(4); 15 | const C1 = vec(8); 16 | const C2 = vec(12); 17 | const D = vec(16); 18 | const u = vec(4); 19 | 20 | function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) { 21 | let acxtail, acytail, bcxtail, bcytail; 22 | let bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3; 23 | 24 | const acx = ax - cx; 25 | const bcx = bx - cx; 26 | const acy = ay - cy; 27 | const bcy = by - cy; 28 | 29 | $Cross_Product(acx, bcx, acy, bcy, B); 30 | 31 | let det = estimate(4, B); 32 | let errbound = ccwerrboundB * detsum; 33 | if (det >= errbound || -det >= errbound) { 34 | return det; 35 | } 36 | 37 | $Two_Diff_Tail(ax, cx, acx, acxtail); 38 | $Two_Diff_Tail(bx, cx, bcx, bcxtail); 39 | $Two_Diff_Tail(ay, cy, acy, acytail); 40 | $Two_Diff_Tail(by, cy, bcy, bcytail); 41 | 42 | if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) { 43 | return det; 44 | } 45 | 46 | errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det); 47 | det += acx * bcytail + bcy * acxtail - (acy * bcxtail + bcx * acytail); 48 | if (det >= errbound || -det >= errbound) return det; 49 | 50 | $Cross_Product(acxtail, bcx, acytail, bcy, u); 51 | const C1len = sum(4, B, 4, u, C1); 52 | 53 | $Cross_Product(acx, bcxtail, acy, bcytail, u); 54 | const C2len = sum(C1len, C1, 4, u, C2); 55 | 56 | $Cross_Product(acxtail, bcxtail, acytail, bcytail, u); 57 | const Dlen = sum(C2len, C2, 4, u, D); 58 | 59 | return D[Dlen - 1]; 60 | } 61 | 62 | export function orient2d(ax, ay, bx, by, cx, cy) { 63 | const detleft = (ay - cy) * (bx - cx); 64 | const detright = (ax - cx) * (by - cy); 65 | const det = detleft - detright; 66 | 67 | if (detleft === 0 || detright === 0 || detleft > 0 !== detright > 0) 68 | return det; 69 | 70 | const detsum = Math.abs(detleft + detright); 71 | if (Math.abs(det) >= ccwerrboundA * detsum) return det; 72 | 73 | return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum); 74 | } 75 | 76 | export function orient2dfast(ax, ay, bx, by, cx, cy) { 77 | return (ay - cy) * (bx - cx) - (ax - cx) * (by - cy); 78 | } 79 | -------------------------------------------------------------------------------- /modules/palettes.js: -------------------------------------------------------------------------------- 1 | const warm = ["#FF2000", "#FF5900", "#FE9100", "#FEFDFC", "#FEC194", "#FE9F5B"]; 2 | const natural = [ 3 | "#FF6D00", 4 | "#FBF8EB", 5 | "#008B99", 6 | "#F8E1A6", 7 | "#FDA81F", 8 | "#B80A01", 9 | "#480D07", 10 | ]; 11 | const natural2 = [ 12 | "#EF2006", 13 | "#350000", 14 | "#A11104", 15 | "#ED5910", 16 | "#F1B52E", 17 | "#7B5614", 18 | "#F7F1AC", 19 | ]; 20 | const circus = [ 21 | "#F62D62", 22 | "#FFFFFF", 23 | "#FDB600", 24 | "#F42D2D", 25 | "#544C98", 26 | "#ECACBC", 27 | ]; 28 | const seaside = [ 29 | "#FEB019", 30 | "#F46002", 31 | "#E1E7F1", 32 | "#0A1D69", 33 | "#138FE2", 34 | "#0652C4", 35 | "#D23401", 36 | "#B0A12F", 37 | ]; 38 | const warm2 = [ 39 | "#FFFEFE", 40 | "#0D0211", 41 | "#FBCEA0", 42 | "#FFAD5D", 43 | "#530E1D", 44 | "#FE9232", 45 | "#B93810", 46 | "#907996", 47 | ]; 48 | const warm3 = [ 49 | "#EDEBE7", 50 | "#13595A", 51 | "#DE1408", 52 | "#161814", 53 | "#E1610A", 54 | "#B7BDB3", 55 | "#9F9772", 56 | ]; 57 | const circus2 = [ 58 | "#F62D62", 59 | "#FFFFFF", 60 | "#FDB600", 61 | "#F42D2D", 62 | "#544C98", 63 | "#ECACBC", 64 | ]; 65 | 66 | export { warm, natural, natural2, circus, seaside, warm2, warm3, circus2 }; 67 | -------------------------------------------------------------------------------- /modules/renderer.js: -------------------------------------------------------------------------------- 1 | import { 2 | sRGBEncoding, 3 | PerspectiveCamera, 4 | Scene, 5 | WebGLRenderer, 6 | } from "../third_party/three.module.js"; 7 | import { OrbitControls } from "../third_party/OrbitControls.js"; 8 | 9 | const renderer = new WebGLRenderer({ 10 | antialias: true, 11 | preserveDrawingBuffer: true, 12 | powerPreference: "high-performance", 13 | }); 14 | 15 | document.body.append(renderer.domElement); 16 | renderer.outputEncoding = sRGBEncoding; 17 | renderer.setPixelRatio(window.devicePixelRatio); 18 | 19 | const scene = new Scene(); 20 | 21 | const camera = new PerspectiveCamera(45, 1, 0.1, 100); 22 | camera.position.set(2, 2, 2); 23 | camera.lookAt(scene.position); 24 | 25 | function resize() { 26 | const w = window.innerWidth; 27 | const h = window.innerHeight; 28 | renderer.setSize(w, h); 29 | camera.aspect = w / h; 30 | camera.updateProjectionMatrix(); 31 | renderer.setPixelRatio(window.devicePixelRatio); 32 | const dPR = renderer.getPixelRatio(); 33 | 34 | for (const fn of resizes) { 35 | fn(w, h, dPR); 36 | } 37 | } 38 | 39 | async function render() { 40 | for (const fn of updates) { 41 | await fn(); 42 | } 43 | renderer.render(scene, camera); 44 | renderer.setAnimationLoop(render); 45 | } 46 | 47 | const updates = []; 48 | 49 | function addUpdate(fn) { 50 | updates.push(fn); 51 | } 52 | 53 | const resizes = []; 54 | 55 | function addResize(fn) { 56 | resizes.push(fn); 57 | } 58 | 59 | window.addEventListener("resize", () => resize()); 60 | 61 | resize(); 62 | 63 | function getControls(cam = camera) { 64 | const controls = new OrbitControls(cam, renderer.domElement); 65 | return controls; 66 | } 67 | 68 | export { 69 | renderer, 70 | scene, 71 | addUpdate, 72 | addResize, 73 | getControls, 74 | camera, 75 | resize, 76 | render, 77 | }; 78 | -------------------------------------------------------------------------------- /modules/tweet-button.js: -------------------------------------------------------------------------------- 1 | const styles = ` 2 | :host{ 3 | all: initial; 4 | } 5 | 6 | .button { 7 | background-color: #1d9bf0; 8 | color: white; 9 | text-decoration: none; 10 | line-height: 0; 11 | padding: 8px 16px; 12 | border-radius: 3px; 13 | font-weight: bold; 14 | cursor: pointer; 15 | display: inline-flex; 16 | font-size: 12px; 17 | align-items: center; 18 | justify-content: center; 19 | border: 1px solid #1d9bf0; 20 | font-family: 'roboto', sans-serif; 21 | font-size: 13px; 22 | } 23 | 24 | .button svg { 25 | fill: white; 26 | margin-right: .25em; 27 | height: 1.25em; 28 | } 29 | 30 | .button:hover { 31 | background: white; 32 | color: #1d9bf0; 33 | } 34 | 35 | .button:hover svg { 36 | fill: #1d9bf0; 37 | } 38 | 39 | @media (max-width: 512px) { 40 | .button span.text { 41 | display: none; 42 | } 43 | .button span svg { 44 | margin: 0; 45 | } 46 | }`; 47 | 48 | class TweetButton extends HTMLElement { 49 | constructor() { 50 | super(); 51 | this.attachShadow({ mode: "open" }); 52 | 53 | const style = document.createElement("style"); 54 | style.textContent = styles; 55 | this.shadowRoot.append(style); 56 | 57 | const div = document.createElement("div"); 58 | div.className = "button"; 59 | 60 | const logo = document.createElement("span"); 61 | logo.innerHTML = ``; 62 | div.append(logo); 63 | this.shadowRoot.append(div); 64 | 65 | const text = document.createElement("span"); 66 | text.textContent = "Share"; 67 | text.className = "text"; 68 | div.append(text); 69 | 70 | const url = `https://twitter.com/intent/tweet?url=${window.location}`; 71 | this.addEventListener("click", (e) => { 72 | window.open(url, "_blank").focus(); 73 | e.preventDefault(); 74 | }); 75 | 76 | this.title = "Share on twitter"; 77 | } 78 | } 79 | 80 | customElements.define("tweet-button", TweetButton); 81 | 82 | export { TweetButton }; 83 | -------------------------------------------------------------------------------- /screenshots/codevember-2022-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-1.jpg -------------------------------------------------------------------------------- /screenshots/codevember-2022-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-1.png -------------------------------------------------------------------------------- /screenshots/codevember-2022-12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-12.jpg -------------------------------------------------------------------------------- /screenshots/codevember-2022-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-12.png -------------------------------------------------------------------------------- /screenshots/codevember-2022-13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-13.jpg -------------------------------------------------------------------------------- /screenshots/codevember-2022-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-13.png -------------------------------------------------------------------------------- /screenshots/codevember-2022-17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-17.jpg -------------------------------------------------------------------------------- /screenshots/codevember-2022-17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-17.png -------------------------------------------------------------------------------- /screenshots/codevember-2022-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-2.jpg -------------------------------------------------------------------------------- /screenshots/codevember-2022-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-2.png -------------------------------------------------------------------------------- /screenshots/codevember-2022-23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-23.jpg -------------------------------------------------------------------------------- /screenshots/codevember-2022-23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-23.png -------------------------------------------------------------------------------- /screenshots/codevember-2022-27.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-27.jpg -------------------------------------------------------------------------------- /screenshots/codevember-2022-27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-27.png -------------------------------------------------------------------------------- /screenshots/codevember-2022-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-3.jpg -------------------------------------------------------------------------------- /screenshots/codevember-2022-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-3.png -------------------------------------------------------------------------------- /screenshots/codevember-2022-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-4.jpg -------------------------------------------------------------------------------- /screenshots/codevember-2022-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-4.png -------------------------------------------------------------------------------- /screenshots/codevember-2022-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-5.jpg -------------------------------------------------------------------------------- /screenshots/codevember-2022-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-5.png -------------------------------------------------------------------------------- /screenshots/codevember-2022-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-6.jpg -------------------------------------------------------------------------------- /screenshots/codevember-2022-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-6.png -------------------------------------------------------------------------------- /screenshots/codevember-2022-7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-7.jpg -------------------------------------------------------------------------------- /screenshots/codevember-2022-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-7.png -------------------------------------------------------------------------------- /screenshots/codevember-2022-8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-8.jpg -------------------------------------------------------------------------------- /screenshots/codevember-2022-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/codevember-2022/8f972f2fae8c6f41a9ea62a6c7d15808cdcfd3c0/screenshots/codevember-2022-8.png -------------------------------------------------------------------------------- /shaders/basic.js: -------------------------------------------------------------------------------- 1 | const shader = ` 2 | precision highp float; 3 | 4 | in vec3 position; 5 | in vec2 uv; 6 | 7 | uniform mat4 modelViewMatrix; 8 | uniform mat4 projectionMatrix; 9 | 10 | out vec2 vUv; 11 | 12 | void main() { 13 | vUv = uv; 14 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1. ); 15 | }`; 16 | 17 | export { shader }; 18 | -------------------------------------------------------------------------------- /shaders/blur.js: -------------------------------------------------------------------------------- 1 | import { blur5, blur9, blur13 } from "./fast-separable-gaussian-blur.js"; 2 | // import { settings } from "../js/settings.js"; 3 | 4 | // let blurFn = blur13; 5 | // let blurCall = "blur13"; 6 | // if (settings.blur === 5) { 7 | // blurFn = blur5; 8 | // blurCall = "blur5"; 9 | // } 10 | // if (settings.blur === 9) { 11 | // blurFn = blur9; 12 | // blurCall = "blur9"; 13 | // } 14 | 15 | const shader = ` 16 | precision highp float; 17 | 18 | uniform sampler2D inputTexture; 19 | uniform vec2 direction; 20 | 21 | in vec2 vUv; 22 | 23 | out vec4 color; 24 | 25 | ${blur13} 26 | 27 | void main() { 28 | color = blur13(inputTexture, vUv, direction); 29 | }`; 30 | 31 | export { shader }; 32 | -------------------------------------------------------------------------------- /shaders/chromatic-aberration.js: -------------------------------------------------------------------------------- 1 | //import { settings } from "../js/settings.js"; 2 | 3 | const shader = ` 4 | vec2 barrelDistortion(vec2 coord, float amt) { 5 | vec2 cc = coord - 0.5; 6 | float dist = dot(cc, cc); 7 | return coord + cc * dist * amt; 8 | } 9 | 10 | float sat( float t ){ 11 | return clamp( t, 0.0, 1.0 ); 12 | } 13 | 14 | float linterp( float t ) { 15 | return sat( 1.0 - abs( 2.0*t - 1.0 ) ); 16 | } 17 | 18 | float remap( float t, float a, float b ) { 19 | return sat( (t - a) / (b - a) ); 20 | } 21 | 22 | vec4 spectrum_offset( float t ) { 23 | vec4 ret; 24 | float lo = step(t,0.5); 25 | float hi = 1.0-lo; 26 | float w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) ); 27 | ret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.); 28 | return pow( ret, vec4(1.0/2.2) ); 29 | } 30 | 31 | const float max_distort = 2.2; 32 | const int num_iter = 8; 33 | const float reci_num_iter_f = 1.0 / float(num_iter); 34 | 35 | vec4 chromaticAberration(sampler2D inputTexture, vec2 uv, float amount, vec2 dir) { 36 | vec4 sumcol = vec4(0.0); 37 | vec4 sumw = vec4(0.0); 38 | for ( int i=0; i lumaMax ) ) { 47 | return rgbA; 48 | } else { 49 | return rgbB; 50 | } 51 | 52 | } 53 | `; 54 | 55 | export { shader }; 56 | -------------------------------------------------------------------------------- /shaders/hsl.js: -------------------------------------------------------------------------------- 1 | const shader = ` 2 | 3 | vec3 rgb2hsv(vec3 c) 4 | { 5 | vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); 6 | vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); 7 | vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); 8 | 9 | float d = q.x - min(q.w, q.y); 10 | float e = 1.0e-10; 11 | return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); 12 | } 13 | 14 | // All components are in the range [0…1], including hue. 15 | vec3 hsv2rgb(vec3 c) 16 | { 17 | vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); 18 | vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); 19 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); 20 | } 21 | 22 | `; 23 | 24 | export { shader }; 25 | -------------------------------------------------------------------------------- /shaders/levels.js: -------------------------------------------------------------------------------- 1 | const shader = ` 2 | vec3 gammaCorrect(vec3 color, vec3 gamma){ 3 | return pow(color, 1.0/gamma); 4 | } 5 | 6 | vec3 levelRange(vec3 color, vec3 minInput, vec3 maxInput){ 7 | return min(max(color - minInput, vec3(0.0)) / (maxInput - minInput), vec3(1.0)); 8 | } 9 | 10 | vec3 finalLevels(vec3 color, vec3 minInput, vec3 gamma, vec3 maxInput){ 11 | return gammaCorrect(levelRange(color, minInput, maxInput), gamma); 12 | }`; 13 | 14 | export { shader }; 15 | -------------------------------------------------------------------------------- /shaders/noise-common.js: -------------------------------------------------------------------------------- 1 | const shader = ` 2 | vec4 mod289(vec4 x) { 3 | return x - floor(x * (1.0 / 289.0)) * 289.0; 4 | } 5 | 6 | vec3 mod289(vec3 x) { 7 | return x - floor(x * (1.0 / 289.0)) * 289.0; 8 | } 9 | 10 | vec2 mod289(vec2 x) { 11 | return x - floor(x * (1.0 / 289.0)) * 289.0; 12 | } 13 | 14 | float mod289(float x) { 15 | return x - floor(x * (1.0 / 289.0)) * 289.0; 16 | } 17 | 18 | vec4 permute(vec4 x) { 19 | return mod289(((x*34.0)+1.0)*x); 20 | } 21 | 22 | vec3 permute(vec3 x) { 23 | return mod289(((x*34.0)+1.0)*x); 24 | } 25 | 26 | vec2 permute(vec2 x) { 27 | return mod289(((x*34.0)+1.0)*x); 28 | } 29 | 30 | float permute(float x) { 31 | return mod289(((x*34.0)+1.0)*x); 32 | } 33 | 34 | vec4 taylorInvSqrt(vec4 r) { 35 | return 1.79284291400159 - 0.85373472095314 * r; 36 | } 37 | 38 | float taylorInvSqrt(float r) { 39 | return 1.79284291400159 - 0.85373472095314 * r; 40 | } 41 | `; 42 | 43 | export { shader }; 44 | -------------------------------------------------------------------------------- /shaders/noise.js: -------------------------------------------------------------------------------- 1 | // https://www.shadertoy.com/view/llGSzw 2 | 3 | const shader = ` 4 | float hash1( uint n ) 5 | { 6 | // integer hash copied from Hugo Elias 7 | n = (n << 13U) ^ n; 8 | n = n * (n * n * 15731U + 789221U) + 1376312589U; 9 | return float( n & uint(0x7fffffffU))/float(0x7fffffff); 10 | } 11 | 12 | float noise(in vec2 uv, in float time) { 13 | uvec2 p = uvec2(uv); 14 | return hash1( p.x + 1920U*p.y + (1920U*1080U)*uint(time) ); 15 | } 16 | `; 17 | 18 | export { shader }; 19 | -------------------------------------------------------------------------------- /shaders/noise2d.js: -------------------------------------------------------------------------------- 1 | const shader = `// 2 | // Description : Array and textureless GLSL 2D simplex noise function. 3 | // Author : Ian McEwan, Ashima Arts. 4 | // Maintainer : stegu 5 | // Lastmod : 20110822 (ijm) 6 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 7 | // Distributed under the MIT License. See LICENSE file. 8 | // https://github.com/ashima/webgl-noise 9 | // https://github.com/stegu/webgl-noise 10 | // 11 | 12 | float snoise(vec2 v) 13 | { 14 | const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0 15 | 0.366025403784439, // 0.5*(sqrt(3.0)-1.0) 16 | -0.577350269189626, // -1.0 + 2.0 * C.x 17 | 0.024390243902439); // 1.0 / 41.0 18 | // First corner 19 | vec2 i = floor(v + dot(v, C.yy) ); 20 | vec2 x0 = v - i + dot(i, C.xx); 21 | 22 | // Other corners 23 | vec2 i1; 24 | //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0 25 | //i1.y = 1.0 - i1.x; 26 | i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); 27 | // x0 = x0 - 0.0 + 0.0 * C.xx ; 28 | // x1 = x0 - i1 + 1.0 * C.xx ; 29 | // x2 = x0 - 1.0 + 2.0 * C.xx ; 30 | vec4 x12 = x0.xyxy + C.xxzz; 31 | x12.xy -= i1; 32 | 33 | // Permutations 34 | i = mod289(i); // Avoid truncation effects in permutation 35 | vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) 36 | + i.x + vec3(0.0, i1.x, 1.0 )); 37 | 38 | vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); 39 | m = m*m ; 40 | m = m*m ; 41 | 42 | // Gradients: 41 points uniformly over a line, mapped onto a diamond. 43 | // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287) 44 | 45 | vec3 x = 2.0 * fract(p * C.www) - 1.0; 46 | vec3 h = abs(x) - 0.5; 47 | vec3 ox = floor(x + 0.5); 48 | vec3 a0 = x - ox; 49 | 50 | // Normalise gradients implicitly by scaling m 51 | // Approximation of: m *= inversesqrt( a0*a0 + h*h ); 52 | m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h ); 53 | 54 | // Compute final noise value at P 55 | vec3 g; 56 | g.x = a0.x * x0.x + h.x * x0.y; 57 | g.yz = a0.yz * x12.xz + h.yz * x12.yw; 58 | return 130.0 * dot(m, g); 59 | }`; 60 | 61 | export { shader }; 62 | -------------------------------------------------------------------------------- /shaders/noise3d.js: -------------------------------------------------------------------------------- 1 | const shader = ` 2 | // 3 | // Description : Array and textureless GLSL 2D/3D/4D simplex 4 | // noise functions. 5 | // Author : Ian McEwan, Ashima Arts. 6 | // Maintainer : stegu 7 | // Lastmod : 20201014 (stegu) 8 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 9 | // Distributed under the MIT License. See LICENSE file. 10 | // https://github.com/ashima/webgl-noise 11 | // https://github.com/stegu/webgl-noise 12 | // 13 | 14 | float noise3d(vec3 v) { 15 | const vec2 C = vec2(1.0/6.0, 1.0/3.0) ; 16 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); 17 | 18 | // First corner 19 | vec3 i = floor(v + dot(v, C.yyy) ); 20 | vec3 x0 = v - i + dot(i, C.xxx) ; 21 | 22 | // Other corners 23 | vec3 g = step(x0.yzx, x0.xyz); 24 | vec3 l = 1.0 - g; 25 | vec3 i1 = min( g.xyz, l.zxy ); 26 | vec3 i2 = max( g.xyz, l.zxy ); 27 | 28 | // x0 = x0 - 0.0 + 0.0 * C.xxx; 29 | // x1 = x0 - i1 + 1.0 * C.xxx; 30 | // x2 = x0 - i2 + 2.0 * C.xxx; 31 | // x3 = x0 - 1.0 + 3.0 * C.xxx; 32 | vec3 x1 = x0 - i1 + C.xxx; 33 | vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y 34 | vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y 35 | 36 | // Permutations 37 | i = mod289(i); 38 | vec4 p = permute( permute( permute( 39 | i.z + vec4(0.0, i1.z, i2.z, 1.0 )) 40 | + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) 41 | + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); 42 | 43 | // Gradients: 7x7 points over a square, mapped onto an octahedron. 44 | // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) 45 | float n_ = 0.142857142857; // 1.0/7.0 46 | vec3 ns = n_ * D.wyz - D.xzx; 47 | 48 | vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) 49 | 50 | vec4 x_ = floor(j * ns.z); 51 | vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N) 52 | 53 | vec4 x = x_ *ns.x + ns.yyyy; 54 | vec4 y = y_ *ns.x + ns.yyyy; 55 | vec4 h = 1.0 - abs(x) - abs(y); 56 | 57 | vec4 b0 = vec4( x.xy, y.xy ); 58 | vec4 b1 = vec4( x.zw, y.zw ); 59 | 60 | //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; 61 | //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; 62 | vec4 s0 = floor(b0)*2.0 + 1.0; 63 | vec4 s1 = floor(b1)*2.0 + 1.0; 64 | vec4 sh = -step(h, vec4(0.0)); 65 | 66 | vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; 67 | vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; 68 | 69 | vec3 p0 = vec3(a0.xy,h.x); 70 | vec3 p1 = vec3(a0.zw,h.y); 71 | vec3 p2 = vec3(a1.xy,h.z); 72 | vec3 p3 = vec3(a1.zw,h.w); 73 | 74 | //Normalise gradients 75 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 76 | p0 *= norm.x; 77 | p1 *= norm.y; 78 | p2 *= norm.z; 79 | p3 *= norm.w; 80 | 81 | // Mix final noise value 82 | vec4 m = max(0.5 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); 83 | m = m * m; 84 | return 105.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), 85 | dot(p2,x2), dot(p3,x3) ) ); 86 | }`; 87 | 88 | export { shader }; 89 | -------------------------------------------------------------------------------- /shaders/ortho.js: -------------------------------------------------------------------------------- 1 | const shader = ` 2 | precision highp float; 3 | 4 | in vec3 position; 5 | in vec2 uv; 6 | 7 | uniform mat4 modelViewMatrix; 8 | uniform mat4 projectionMatrix; 9 | 10 | out vec2 vUv; 11 | 12 | void main() { 13 | vUv = uv; 14 | gl_Position = vec4( position, 1. ); 15 | }`; 16 | 17 | export { shader }; 18 | -------------------------------------------------------------------------------- /shaders/parabola.js: -------------------------------------------------------------------------------- 1 | const shader = ` 2 | float parabola(float x, float k) { 3 | return pow(4. * x * (1. - x), k); 4 | } 5 | `; 6 | 7 | export { shader }; 8 | -------------------------------------------------------------------------------- /shaders/screen.js: -------------------------------------------------------------------------------- 1 | const shader = ` 2 | vec4 screen(vec4 base, vec4 blend, float opacity) { 3 | vec4 color = 1. - (1.-base) * (1.-blend); 4 | color = color * opacity + base * ( 1. - opacity ); 5 | return color; 6 | } 7 | 8 | vec3 screen(vec3 base, vec3 blend, float opacity) { 9 | vec3 color = 1. - (1.-base) * (1.-blend); 10 | color = color * opacity + base * ( 1. - opacity ); 11 | return color; 12 | }`; 13 | 14 | export { shader }; 15 | -------------------------------------------------------------------------------- /shaders/vignette.js: -------------------------------------------------------------------------------- 1 | const shader = ` 2 | float vignette(vec2 uv, float boost, float reduction) { 3 | vec2 position = vUv - .5; 4 | return boost - length(position) * reduction; 5 | } 6 | `; 7 | 8 | export { shader }; 9 | -------------------------------------------------------------------------------- /shaders/worley.js: -------------------------------------------------------------------------------- 1 | const shader = ` 2 | #define UI0 1597334673U 3 | #define UI1 3812015801U 4 | #define UI2 uvec2(UI0, UI1) 5 | #define UI3 uvec3(UI0, UI1, 2798796415U) 6 | #define UIF (1.0 / float(0xffffffffU)) 7 | 8 | vec3 hash33(vec3 p) { 9 | uvec3 q = uvec3(ivec3(p)) * UI3; 10 | q = (q.x ^ q.y ^ q.z)*UI3; 11 | return -1. + 2. * vec3(q) * UIF; 12 | } 13 | 14 | float remap(float x, float a, float b, float c, float d) { 15 | return (((x - a) / (b - a)) * (d - c)) + c; 16 | } 17 | 18 | // Gradient noise by iq (modified to be tileable) 19 | float gradientNoise(vec3 x, float freq) { 20 | 21 | // grid 22 | vec3 p = floor(x); 23 | vec3 w = fract(x); 24 | 25 | // quintic interpolant 26 | vec3 u = w * w * w * (w * (w * 6. - 15.) + 10.); 27 | 28 | // gradients 29 | vec3 ga = hash33(mod(p + vec3(0., 0., 0.), freq)); 30 | vec3 gb = hash33(mod(p + vec3(1., 0., 0.), freq)); 31 | vec3 gc = hash33(mod(p + vec3(0., 1., 0.), freq)); 32 | vec3 gd = hash33(mod(p + vec3(1., 1., 0.), freq)); 33 | vec3 ge = hash33(mod(p + vec3(0., 0., 1.), freq)); 34 | vec3 gf = hash33(mod(p + vec3(1., 0., 1.), freq)); 35 | vec3 gg = hash33(mod(p + vec3(0., 1., 1.), freq)); 36 | vec3 gh = hash33(mod(p + vec3(1., 1., 1.), freq)); 37 | 38 | // projections 39 | float va = dot(ga, w - vec3(0., 0., 0.)); 40 | float vb = dot(gb, w - vec3(1., 0., 0.)); 41 | float vc = dot(gc, w - vec3(0., 1., 0.)); 42 | float vd = dot(gd, w - vec3(1., 1., 0.)); 43 | float ve = dot(ge, w - vec3(0., 0., 1.)); 44 | float vf = dot(gf, w - vec3(1., 0., 1.)); 45 | float vg = dot(gg, w - vec3(0., 1., 1.)); 46 | float vh = dot(gh, w - vec3(1., 1., 1.)); 47 | 48 | // interpolation 49 | return va + 50 | u.x * (vb - va) + 51 | u.y * (vc - va) + 52 | u.z * (ve - va) + 53 | u.x * u.y * (va - vb - vc + vd) + 54 | u.y * u.z * (va - vc - ve + vg) + 55 | u.z * u.x * (va - vb - ve + vf) + 56 | u.x * u.y * u.z * (-va + vb + vc - vd + ve - vf - vg + vh); 57 | } 58 | 59 | // Tileable 3D worley noise 60 | float worleyNoise(vec3 uv, float freq) { 61 | vec3 id = floor(uv); 62 | vec3 p = fract(uv); 63 | 64 | float minDist = 10000.; 65 | for (float x = -1.; x <= 1.; ++x) { 66 | for(float y = -1.; y <= 1.; ++y) { 67 | for(float z = -1.; z <= 1.; ++z) { 68 | vec3 offset = vec3(x, y, z); 69 | vec3 h = hash33(mod(id + offset, vec3(freq))) * .5 + .5; 70 | h += offset; 71 | vec3 d = p - h; 72 | minDist = min(minDist, dot(d, d)); 73 | } 74 | } 75 | } 76 | 77 | // inverted worley noise 78 | return 1. - minDist; 79 | } 80 | 81 | // Fbm for Perlin noise based on iq's blog 82 | float perlinfbm(vec3 p, float freq, int octaves) { 83 | float G = exp2(-.85); 84 | float amp = 1.; 85 | float noise = 0.; 86 | for (int i = 0; i < octaves; ++i) { 87 | noise += amp * gradientNoise(p * freq, freq); 88 | freq *= 2.; 89 | amp *= G; 90 | } 91 | 92 | return noise; 93 | } 94 | 95 | // Tileable Worley fbm inspired by Andrew Schneider's Real-Time Volumetric Cloudscapes 96 | // chapter in GPU Pro 7. 97 | float worleyFbm(vec3 p, float freq) { 98 | return worleyNoise(p*freq, freq) * .625 + 99 | worleyNoise(p*freq*2., freq*2.) * .25 + 100 | worleyNoise(p*freq*4., freq*4.) * .125; 101 | } 102 | 103 | float edge(vec3 p, float freq) { 104 | float d= 0.; 105 | float up = worleyFbm(p - vec3(0.,d,0.), freq); 106 | float down = worleyFbm(p- vec3(0.,d,0.), freq); 107 | float right = worleyFbm(p- vec3(d,0.,0.), freq); 108 | float left = worleyFbm(p+ vec3(d,0.,0.), freq); 109 | return (up+down+right+left)/4.; 110 | } 111 | `; 112 | 113 | export { shader }; 114 | -------------------------------------------------------------------------------- /styles/styles.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@500&family=Open+Sans:wght@300;600&display=swap"); 2 | 3 | body { 4 | overflow: hidden; 5 | } 6 | 7 | * { 8 | box-sizing: border-box; 9 | margin: 0; 10 | padding: 0; 11 | font-family: "Open Sans", sans-serif; 12 | } 13 | 14 | html { 15 | font-size: 14px; 16 | } 17 | 18 | #index { 19 | color: #202020; 20 | font-size: 1rem; 21 | } 22 | 23 | #index ul { 24 | list-style-type: none; 25 | } 26 | 27 | #index h1 { 28 | padding: 1em; 29 | } 30 | 31 | #index ul { 32 | margin-bottom: 1em; 33 | } 34 | 35 | a { 36 | font-weight: bold; 37 | text-decoration: none; 38 | color: inherit; 39 | } 40 | 41 | a:hover { 42 | text-decoration: underline; 43 | } 44 | 45 | a:hover { 46 | font-weight: bold; 47 | } 48 | 49 | canvas { 50 | position: absolute; 51 | left: 0; 52 | top: 0; 53 | right: 0; 54 | bottom: 0; 55 | width: 100%; 56 | height: 100%; 57 | } 58 | 59 | #ui { 60 | display: flex; 61 | pointer-events: none; 62 | position: absolute; 63 | left: 0; 64 | top: 0; 65 | right: 0; 66 | bottom: 0; 67 | flex-direction: column; 68 | padding: 1.5em; 69 | gap: 1.5em; 70 | z-index: 10; 71 | } 72 | 73 | #info { 74 | color: white; 75 | text-shadow: 1px 1px black; 76 | border-radius: 1em; 77 | margin-right: 0.5em; 78 | } 79 | 80 | #info a { 81 | color: inherit; 82 | } 83 | 84 | #info p span { 85 | margin-right: 0.5em; 86 | } 87 | 88 | #info p:last-child { 89 | margin-bottom: 0; 90 | } 91 | 92 | .contrast #info *, 93 | .contrast footer * { 94 | text-shadow: none; 95 | color: black; 96 | } 97 | 98 | .contrast #info button { 99 | border-color: black; 100 | } 101 | 102 | .contrast #info button:hover { 103 | background-color: rgb(80, 80, 80); 104 | color: white; 105 | } 106 | 107 | .ultrahigh #info { 108 | background-color: rgba(255, 255, 255, 0.9); 109 | } 110 | 111 | #info h1 { 112 | font-family: "Montserrat", sans-serif; 113 | margin-bottom: 0.5em; 114 | font-size: 1.5rem; 115 | } 116 | 117 | #info h2 { 118 | font-family: "Montserrat", sans-serif; 119 | font-size: 1rem; 120 | } 121 | 122 | #info p { 123 | margin-bottom: 1em; 124 | } 125 | 126 | #info button { 127 | color: inherit; 128 | cursor: pointer; 129 | } 130 | 131 | button { 132 | border: 1px solid white; 133 | outline: none; 134 | background: none; 135 | border-radius: 3px; 136 | padding: 0.5em 1em; 137 | margin-right: 0.5em; 138 | pointer-events: auto; 139 | text-shadow: 1px 1px black; 140 | font-weight: bold; 141 | } 142 | 143 | button:hover, 144 | #info button:hover { 145 | background-color: rgb(180, 180, 180); 146 | color: black; 147 | text-shadow: none; 148 | } 149 | 150 | #info a { 151 | pointer-events: auto; 152 | } 153 | 154 | footer { 155 | color: white; 156 | text-shadow: 1px 1px black; 157 | display: flex; 158 | flex: 1; 159 | align-items: end; 160 | } 161 | 162 | footer a { 163 | pointer-events: auto; 164 | } 165 | 166 | .contact { 167 | padding: 1em; 168 | font-size: 2em; 169 | } 170 | 171 | .github-corner:hover .octo-arm { 172 | animation: octocat-wave 560ms ease-in-out; 173 | } 174 | @keyframes octocat-wave { 175 | 0%, 176 | 100% { 177 | transform: rotate(0); 178 | } 179 | 20%, 180 | 60% { 181 | transform: rotate(-25deg); 182 | } 183 | 40%, 184 | 80% { 185 | transform: rotate(10deg); 186 | } 187 | } 188 | @media (max-width: 500px) { 189 | .github-corner:hover .octo-arm { 190 | animation: none; 191 | } 192 | .github-corner .octo-arm { 193 | animation: octocat-wave 560ms ease-in-out; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /third_party/DeviceOrientationControls.js: -------------------------------------------------------------------------------- 1 | import { 2 | Euler, 3 | EventDispatcher, 4 | MathUtils, 5 | Quaternion, 6 | Vector3, 7 | } from "../third_party/three.module.js"; 8 | 9 | const _zee = new Vector3(0, 0, 1); 10 | const _euler = new Euler(); 11 | const _q0 = new Quaternion(); 12 | const _q1 = new Quaternion(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5)); // - PI/2 around the x-axis 13 | 14 | const _changeEvent = { type: "change" }; 15 | 16 | class DeviceOrientationControls extends EventDispatcher { 17 | constructor(object) { 18 | super(); 19 | 20 | if (window.isSecureContext === false) { 21 | console.error( 22 | "THREE.DeviceOrientationControls: DeviceOrientationEvent is only available in secure contexts (https)" 23 | ); 24 | } 25 | 26 | const scope = this; 27 | 28 | const EPS = 0.000001; 29 | const lastQuaternion = new Quaternion(); 30 | 31 | this.object = object; 32 | this.object.rotation.reorder("YXZ"); 33 | 34 | this.enabled = true; 35 | 36 | this.deviceOrientation = {}; 37 | this.screenOrientation = 0; 38 | 39 | this.alphaOffset = 0; // radians 40 | 41 | const onDeviceOrientationChangeEvent = function (event) { 42 | scope.deviceOrientation = event; 43 | }; 44 | 45 | const onScreenOrientationChangeEvent = function () { 46 | scope.screenOrientation = window.orientation || 0; 47 | }; 48 | 49 | // The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y'' 50 | 51 | const setObjectQuaternion = function ( 52 | quaternion, 53 | alpha, 54 | beta, 55 | gamma, 56 | orient 57 | ) { 58 | _euler.set(beta, alpha, -gamma, "YXZ"); // 'ZXY' for the device, but 'YXZ' for us 59 | 60 | quaternion.setFromEuler(_euler); // orient the device 61 | 62 | quaternion.multiply(_q1); // camera looks out the back of the device, not the top 63 | 64 | quaternion.multiply(_q0.setFromAxisAngle(_zee, -orient)); // adjust for screen orientation 65 | }; 66 | 67 | this.connect = function () { 68 | onScreenOrientationChangeEvent(); // run once on load 69 | 70 | // iOS 13+ 71 | 72 | if ( 73 | window.DeviceOrientationEvent !== undefined && 74 | typeof window.DeviceOrientationEvent.requestPermission === "function" 75 | ) { 76 | window.DeviceOrientationEvent.requestPermission() 77 | .then(function (response) { 78 | if (response == "granted") { 79 | window.addEventListener( 80 | "orientationchange", 81 | onScreenOrientationChangeEvent 82 | ); 83 | window.addEventListener( 84 | "deviceorientation", 85 | onDeviceOrientationChangeEvent 86 | ); 87 | } 88 | }) 89 | .catch(function (error) { 90 | console.error( 91 | "THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:", 92 | error 93 | ); 94 | }); 95 | } else { 96 | window.addEventListener( 97 | "orientationchange", 98 | onScreenOrientationChangeEvent 99 | ); 100 | window.addEventListener( 101 | "deviceorientation", 102 | onDeviceOrientationChangeEvent 103 | ); 104 | } 105 | 106 | scope.enabled = true; 107 | }; 108 | 109 | this.disconnect = function () { 110 | window.removeEventListener( 111 | "orientationchange", 112 | onScreenOrientationChangeEvent 113 | ); 114 | window.removeEventListener( 115 | "deviceorientation", 116 | onDeviceOrientationChangeEvent 117 | ); 118 | 119 | scope.enabled = false; 120 | }; 121 | 122 | this.update = function () { 123 | if (scope.enabled === false) return; 124 | 125 | const device = scope.deviceOrientation; 126 | 127 | if (device) { 128 | const alpha = device.alpha 129 | ? MathUtils.degToRad(device.alpha) + scope.alphaOffset 130 | : 0; // Z 131 | 132 | const beta = device.beta ? MathUtils.degToRad(device.beta) : 0; // X' 133 | 134 | const gamma = device.gamma ? MathUtils.degToRad(device.gamma) : 0; // Y'' 135 | 136 | const orient = scope.screenOrientation 137 | ? MathUtils.degToRad(scope.screenOrientation) 138 | : 0; // O 139 | 140 | setObjectQuaternion( 141 | scope.object.quaternion, 142 | alpha, 143 | beta, 144 | gamma, 145 | orient 146 | ); 147 | 148 | if (8 * (1 - lastQuaternion.dot(scope.object.quaternion)) > EPS) { 149 | lastQuaternion.copy(scope.object.quaternion); 150 | scope.dispatchEvent(_changeEvent); 151 | } 152 | } 153 | }; 154 | 155 | this.dispose = function () { 156 | scope.disconnect(); 157 | }; 158 | 159 | this.connect(); 160 | } 161 | } 162 | 163 | export { DeviceOrientationControls }; 164 | -------------------------------------------------------------------------------- /third_party/util.js: -------------------------------------------------------------------------------- 1 | export const epsilon = 1.1102230246251565e-16; 2 | export const splitter = 134217729; 3 | export const resulterrbound = (3 + 8 * epsilon) * epsilon; 4 | 5 | // fast_expansion_sum_zeroelim routine from oritinal code 6 | export function sum(elen, e, flen, f, h) { 7 | let Q, Qnew, hh, bvirt; 8 | let enow = e[0]; 9 | let fnow = f[0]; 10 | let eindex = 0; 11 | let findex = 0; 12 | if (fnow > enow === fnow > -enow) { 13 | Q = enow; 14 | enow = e[++eindex]; 15 | } else { 16 | Q = fnow; 17 | fnow = f[++findex]; 18 | } 19 | let hindex = 0; 20 | if (eindex < elen && findex < flen) { 21 | if (fnow > enow === fnow > -enow) { 22 | $Fast_Two_Sum(enow, Q, Qnew, hh); 23 | enow = e[++eindex]; 24 | } else { 25 | $Fast_Two_Sum(fnow, Q, Qnew, hh); 26 | fnow = f[++findex]; 27 | } 28 | Q = Qnew; 29 | if (hh !== 0) { 30 | h[hindex++] = hh; 31 | } 32 | while (eindex < elen && findex < flen) { 33 | if (fnow > enow === fnow > -enow) { 34 | $Two_Sum(Q, enow, Qnew, hh); 35 | enow = e[++eindex]; 36 | } else { 37 | $Two_Sum(Q, fnow, Qnew, hh); 38 | fnow = f[++findex]; 39 | } 40 | Q = Qnew; 41 | if (hh !== 0) { 42 | h[hindex++] = hh; 43 | } 44 | } 45 | } 46 | while (eindex < elen) { 47 | $Two_Sum(Q, enow, Qnew, hh); 48 | enow = e[++eindex]; 49 | Q = Qnew; 50 | if (hh !== 0) { 51 | h[hindex++] = hh; 52 | } 53 | } 54 | while (findex < flen) { 55 | $Two_Sum(Q, fnow, Qnew, hh); 56 | fnow = f[++findex]; 57 | Q = Qnew; 58 | if (hh !== 0) { 59 | h[hindex++] = hh; 60 | } 61 | } 62 | if (Q !== 0 || hindex === 0) { 63 | h[hindex++] = Q; 64 | } 65 | return hindex; 66 | } 67 | 68 | export function sum_three(alen, a, blen, b, clen, c, tmp, out) { 69 | return sum(sum(alen, a, blen, b, tmp), tmp, clen, c, out); 70 | } 71 | 72 | // scale_expansion_zeroelim routine from oritinal code 73 | export function scale(elen, e, b, h) { 74 | let Q, sum, hh, product1, product0; 75 | let bvirt, c, ahi, alo, bhi, blo; 76 | 77 | $Split(b, bhi, blo); 78 | let enow = e[0]; 79 | $Two_Product_Presplit(enow, b, bhi, blo, Q, hh); 80 | let hindex = 0; 81 | if (hh !== 0) { 82 | h[hindex++] = hh; 83 | } 84 | for (let i = 1; i < elen; i++) { 85 | enow = e[i]; 86 | $Two_Product_Presplit(enow, b, bhi, blo, product1, product0); 87 | $Two_Sum(Q, product0, sum, hh); 88 | if (hh !== 0) { 89 | h[hindex++] = hh; 90 | } 91 | $Fast_Two_Sum(product1, sum, Q, hh); 92 | if (hh !== 0) { 93 | h[hindex++] = hh; 94 | } 95 | } 96 | if (Q !== 0 || hindex === 0) { 97 | h[hindex++] = Q; 98 | } 99 | return hindex; 100 | } 101 | 102 | export function negate(elen, e) { 103 | for (let i = 0; i < elen; i++) e[i] = -e[i]; 104 | return elen; 105 | } 106 | 107 | export function estimate(elen, e) { 108 | let Q = e[0]; 109 | for (let i = 1; i < elen; i++) Q += e[i]; 110 | return Q; 111 | } 112 | 113 | export function vec(n) { 114 | return new Float64Array(n); 115 | } 116 | --------------------------------------------------------------------------------