diff --git a/TODO.txt b/TODO.txt index 5e716de..3e8d12d 100644 --- a/TODO.txt +++ b/TODO.txt @@ -5,10 +5,16 @@ Migrate rain logic to shaders that operate on double buffer RTTs Reach out to Ashley's partner about producing sounds Much later: - Optimization: calculate texture derivatives on CPU, pass in as a uniform - Optimization: simpler bloom replacement + Optimizations + calculate texture derivatives on CPU, pass in as a uniform + simpler bloom replacement Dissolve threejs project into webgl project Maybe webgl2 project? - Flashing row effect? + Deluxe + Flashing row effect? + https://youtu.be/z_KmNZNT5xw?t=16 + Square event? + https://youtu.be/ngnlBZNuVb8?t=200 + https://youtu.be/721sG2D_9-U?t=67 More patterns? Symbol duplication is common diff --git a/index.html b/index.html index 9926333..558b4c4 100644 --- a/index.html +++ b/index.html @@ -9,9 +9,9 @@ - + @@ -33,6 +33,8 @@ const b = parseFloat(getParam("b", 1.125)); const c = parseFloat(getParam("c", 1.25)); + const effect = getParam("effect", "plain"); + document.ontouchmove = (e) => e.preventDefault(); const element = document.createElement("matrixcode"); document.body.appendChild(element); @@ -56,46 +58,53 @@ glyphSequenceLength, }); - // const stupidMaterial = new THREE.MeshBasicMaterial( { map: texture, flatShading: false, transparent:false } ); - const mesh = new THREE.Mesh( geometry, material ); scene.add(mesh); composer.addPass( new THREE.RenderPass( scene, camera ) ); - const bloomPass = new THREE.UnrealBloomPass( new THREE.Vector2( window.innerWidth, window.innerHeight ), 1.5, 0.15, 0.3 ); + const bloomPass = new THREE.UnrealBloomPass( new THREE.Vector2( window.innerWidth, window.innerHeight ), 2, 0.5, 0.3 ); composer.addPass( bloomPass ); - const colorMap = new THREE.ColorMapPass([ - {color: new THREE.Vector3(0.00, 0.00, 0.00), at: 0.0}, - {color: new THREE.Vector3(0.05, 0.52, 0.17), at: 0.4}, - {color: new THREE.Vector3(0.12, 0.82, 0.37), at: 0.8}, - {color: new THREE.Vector3(0.29, 1.00, 0.64), at: 1.0}, - ]); - composer.addPass( colorMap ); + switch (effect) { + case "plain": + composer.addPass(new THREE.ColorMapPass([ + {color: new THREE.Vector3(0.00, 0.00, 0.00), at: 0.0}, + {color: new THREE.Vector3(0.05, 0.52, 0.17), at: 0.4}, + {color: new THREE.Vector3(0.12, 0.82, 0.37), at: 0.8}, + {color: new THREE.Vector3(0.29, 1.00, 0.64), at: 1.0}, + ], 0.1)); + break; + case "pride": + composer.addPass(new THREE.HorizontalColorationPass([ + new THREE.Vector3(1, 0, 0), + new THREE.Vector3(1, 0.5, 0), + new THREE.Vector3(1, 1, 0), + new THREE.Vector3(0, 1, 0), + new THREE.Vector3(0, 0, 1), + new THREE.Vector3(0.8, 0, 1), + ], 0.1)); + break; + case "customStripes": + const flagColorData = getParam("colors", "0.4,0.15,0.1,0.4,0.15,0.1,0.8,0.8,0.6,0.8,0.8,0.6,1.0,0.7,0.8,1.0,0.7,0.8,").split(",").map(parseFloat); + const numFlagColors = Math.floor(flagColorData.length / 3); + const colors = []; + for (let i = 0; i < numFlagColors; i++) { + colors.push(new THREE.Vector3(flagColorData[i * 3 + 0], flagColorData[i * 3 + 1], flagColorData[i * 3 + 2])); + } + composer.addPass(new THREE.HorizontalColorationPass(colors, 0.1)); + break; + case "none": + break; + case "image": + const imageURL = getParam("url", "https://upload.wikimedia.org/wikipedia/commons/0/0a/Flammarion_Colored.jpg"); + window.texture2 = new THREE.TextureLoader().load( imageURL ); + composer.addPass(new THREE.ImageOverlayPass(texture2)); + break; + } - const horizontalColoration = new THREE.HorizontalColorationPass([ - new THREE.Vector3(1, 0, 0), - new THREE.Vector3(1, 1, 0), - new THREE.Vector3(0, 1, 0), - new THREE.Vector3(0, 1, 1), - new THREE.Vector3(0, 0, 1), - new THREE.Vector3(1, 0, 1), - - - // new THREE.Vector3(1, 0, 0.5), - // new THREE.Vector3(1, 0, 0.5), - // new THREE.Vector3(0.5, 0, 1), - // new THREE.Vector3(0.5, 0, 1), - // new THREE.Vector3(0.25, 0.25, 1), - // new THREE.Vector3(0.25, 0.25, 1), - ]); - // composer.addPass(horizontalColoration); - - const blurMag = 0.0001; - const ditherMag = 0.075; - const filmGrainPass = new THREE.FilmGrainPass(blurMag, ditherMag); - composer.addPass(filmGrainPass); + composer.passes.filter(pass => !pass.enabled).renderToScreen = false; + composer.passes.filter(pass => pass.enabled).pop().renderToScreen = true; const windowResize = () => { const [width, height] = [window.innerWidth, window.innerHeight]; @@ -122,8 +131,7 @@ const render = () => { requestAnimationFrame(render); - const now = Date.now(); - update(now); + update(Date.now()); composer.render(); } render(); diff --git a/js/ColorMapPass.js b/js/ColorMapPass.js index e8429e7..722d8be 100644 --- a/js/ColorMapPass.js +++ b/js/ColorMapPass.js @@ -11,7 +11,7 @@ const easeInOutQuad = input => { return 1 - 2 * input * input; } -THREE.ColorMapPass = function (entries) { +THREE.ColorMapPass = function (entries, ditherMagnitude = 1) { const colors = Array(256).fill().map(_ => new THREE.Vector3()); const sortedEntries = entries.slice().sort((e1, e2) => e1.at - e2.at).map(entry => ({ color: entry.color, @@ -44,7 +44,8 @@ THREE.ColorMapPass = function (entries) { this.shader = { uniforms: { tDiffuse: { value: null }, - tColorData: { value: this.dataTexture } + tColorData: { value: this.dataTexture }, + ditherMagnitude: { value: ditherMagnitude } }, vertexShader: ` @@ -56,15 +57,21 @@ THREE.ColorMapPass = function (entries) { `, fragmentShader: ` + #define PI 3.14159265359 + uniform sampler2D tDiffuse; uniform sampler2D tColorData; + uniform float ditherMagnitude; varying vec2 vUv; + highp float rand( const in vec2 uv ) { + const highp float a = 12.9898, b = 78.233, c = 43758.5453; + highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); + return fract(sin(sn) * c); + } + void main() { - gl_FragColor = vec4( - texture2D( tColorData, vec2( texture2D( tDiffuse, vUv ).r, 0.0 ) ).rgb, - 1.0 - ); + gl_FragColor = texture2D( tColorData, vec2( texture2D( tDiffuse, vUv ).r - rand( gl_FragCoord.xy ) * ditherMagnitude, 0.0 ) ); } ` }; diff --git a/js/FilmGrainPass.js b/js/FilmGrainPass.js deleted file mode 100644 index a3b5445..0000000 --- a/js/FilmGrainPass.js +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @author rezmason - */ - -THREE.FilmGrainPass = function (blurMagnitude, ditherMagnitude) { - this.shader = { - uniforms: { - tDiffuse: { value: null }, - blurMagnitude: { value: blurMagnitude }, - ditherMagnitude: { value: ditherMagnitude } - }, - - vertexShader: ` - varying vec2 vUv; - void main() { - vUv = uv; - gl_Position = vec4( position, 1.0 ); - } - `, - - fragmentShader: ` - #define PI 3.14159265359 - - uniform sampler2D tDiffuse; - uniform float blurMagnitude; - uniform float ditherMagnitude; - varying vec2 vUv; - - highp float rand( const in vec2 uv ) { - const highp float a = 12.9898, b = 78.233, c = 43758.5453; - highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); - return fract(sin(sn) * c); - } - - vec3 dithering( vec3 color1, vec3 color2 ) { - float difference = pow(length(color1 - color2), 0.1); - return color1 + vec3( -1, 0.5, 0.5 ) * ditherMagnitude * difference * mix( -1.0, 1.0, rand( gl_FragCoord.xy ) ); - } - - void main() { - vec4 sample = texture2D( tDiffuse, vUv ); - vec4 blurSum = vec4( 0.0 ); - - blurSum += texture2D( tDiffuse, vUv + vec2(-blurMagnitude, -blurMagnitude) ) * 0.25; - blurSum += texture2D( tDiffuse, vUv + vec2( blurMagnitude, -blurMagnitude) ) * 0.25; - blurSum += texture2D( tDiffuse, vUv + vec2(-blurMagnitude, blurMagnitude) ) * 0.25; - blurSum += texture2D( tDiffuse, vUv + vec2( blurMagnitude, blurMagnitude) ) * 0.25; - - gl_FragColor = vec4(dithering(blurSum.rgb, sample.rgb), 1.0); - } - ` - }; - - THREE.ShaderPass.call(this, this.shader); -}; - -THREE.FilmGrainPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ), { - constructor: THREE.FilmGrainPass, - render: THREE.ShaderPass.prototype.render -}); diff --git a/js/HorizontalColorationPass.js b/js/HorizontalColorationPass.js index db17603..77939f1 100644 --- a/js/HorizontalColorationPass.js +++ b/js/HorizontalColorationPass.js @@ -2,7 +2,7 @@ * @author rezmason */ -THREE.HorizontalColorationPass = function (colors) { +THREE.HorizontalColorationPass = function (colors, ditherMagnitude = 1) { const values = new Uint8Array([].concat(...colors.map(color => color.toArray().map(component => Math.floor(component * 255))))); this.dataTexture = new THREE.DataTexture( @@ -19,6 +19,7 @@ THREE.HorizontalColorationPass = function (colors) { uniforms: { tDiffuse: { value: null }, tColorData: { value: this.dataTexture }, + ditherMagnitude: { value: ditherMagnitude }, }, vertexShader: ` @@ -30,13 +31,22 @@ THREE.HorizontalColorationPass = function (colors) { `, fragmentShader: ` + #define PI 3.14159265359 + uniform sampler2D tDiffuse; uniform sampler2D tColorData; + uniform float ditherMagnitude; varying vec2 vUv; + highp float rand( const in vec2 uv ) { + const highp float a = 12.9898, b = 78.233, c = 43758.5453; + highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); + return fract(sin(sn) * c); + } + void main() { float value = texture2D(tDiffuse, vUv).r; - vec3 value2 = texture2D(tColorData, vUv).rgb; + vec3 value2 = texture2D(tColorData, vUv).rgb - rand( gl_FragCoord.xy ) * ditherMagnitude; gl_FragColor = vec4(value2 * value, 1.0); } ` diff --git a/js/ImageOverlayPass.js b/js/ImageOverlayPass.js new file mode 100644 index 0000000..624e74d --- /dev/null +++ b/js/ImageOverlayPass.js @@ -0,0 +1,41 @@ +/** + * @author rezmason + */ + +THREE.ImageOverlayPass = function (texture) { + this.texture = texture; + this.shader = { + uniforms: { + tDiffuse: { value: null }, + map: { value: this.texture }, + }, + + vertexShader: ` + varying vec2 vUv; + void main() { + vUv = uv; + gl_Position = vec4( position, 1.0 ); + } + `, + + fragmentShader: ` + uniform sampler2D tDiffuse; + uniform sampler2D map; + varying vec2 vUv; + + void main() { + gl_FragColor = vec4(texture2D(map, vUv).rgb * (pow(texture2D(tDiffuse, vUv).r, 1.5) * 0.995 + 0.005), 1.0); + } + ` + }; + + THREE.ShaderPass.call(this, this.shader); +}; + +THREE.ImageOverlayPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ), { + constructor: THREE.ImageOverlayPass, + render: function() { + this.uniforms[ "map" ].value = this.texture; + THREE.ShaderPass.prototype.render.call(this, ...arguments); + } +}); diff --git a/js/MatrixGeometry.js b/js/MatrixGeometry.js index 8c5e421..27f2100 100644 --- a/js/MatrixGeometry.js +++ b/js/MatrixGeometry.js @@ -89,12 +89,6 @@ const makeMatrixGeometry = ({ const delta = ((isNaN(last) || now - last > 1000) ? 0 : now - last) / 1000 * animationSpeed; last = now; - bloomPass.enabled = delta < minimumPostProcessingFrameTime; - filmGrainPass.enabled = delta < minimumPostProcessingFrameTime; - - composer.passes.filter(pass => !pass.enabled).renderToScreen = false; - composer.passes.filter(pass => pass.enabled).pop().renderToScreen = true; - const simTime = now * animationSpeed * fallSpeed * 0.0005; for (const column of columns) {