From 80f5aea2a306173483c69823f594da98e40984a0 Mon Sep 17 00:00:00 2001 From: Rezmason Date: Sun, 19 Sep 2021 21:48:17 -0700 Subject: [PATCH] Adding support for "resurrecting" volumetric code, which is rendered to a separate channel and can be given separate colors. --- README.md | 1 + js/bloomPass.js | 12 +++---- js/config.js | 45 +++++++++++++++--------- js/main.js | 5 ++- js/palettePass.js | 14 ++++++-- js/renderer.js | 22 +++++++++++- js/resurrectionPass.js | 78 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 149 insertions(+), 28 deletions(-) create mode 100644 js/resurrectionPass.js diff --git a/README.md b/README.md index 09ab02d..a3d91c0 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ - [Classic Matrix code](https://rezmason.github.io/matrix) - [3D mode](https://rezmason.github.io/matrix?volumetric=true) +- [Matrix Resurrections code (WIP)](https://rezmason.github.io/matrix?version=resurrections) - [Operator Matrix code (with ripple effects)](https://rezmason.github.io/matrix?version=operator) - [Code of the "Nightmare Matrix"](https://rezmason.github.io/matrix?version=nightmare) - [(you know, this stuff).](http://matrix.wikia.com/wiki/Nightmare_Matrix) diff --git a/js/bloomPass.js b/js/bloomPass.js index 10b04ce..bd91531 100644 --- a/js/bloomPass.js +++ b/js/bloomPass.js @@ -42,18 +42,16 @@ export default (regl, config, inputs) => { // The high pass restricts the blur to bright things in our input texture. const highPass = regl({ frag: ` - #define lumaMag vec3(0.2126, 0.7152, 0.0722) precision mediump float; varying vec2 vUV; uniform sampler2D tex; uniform float highPassThreshold; void main() { - vec3 lumaColor = texture2D(tex, vUV).rgb * lumaMag; - float luma = lumaColor.r + lumaColor.g + lumaColor.b; - if (luma < highPassThreshold) { - luma = 0.; - } - gl_FragColor = vec4(vec3(luma), 1.0); + vec3 lumaColor = texture2D(tex, vUV).rgb; + if (lumaColor.r < highPassThreshold) lumaColor.r = 0.0; + if (lumaColor.g < highPassThreshold) lumaColor.g = 0.0; + if (lumaColor.b < highPassThreshold) lumaColor.b = 0.0; + gl_FragColor = vec4(lumaColor, 1.0); } `, uniforms: { diff --git a/js/config.js b/js/config.js index 1bbb34c..ba91c59 100644 --- a/js/config.js +++ b/js/config.js @@ -19,6 +19,7 @@ const fonts = { const defaults = { backgroundColor: [0, 0, 0], volumetric: false, + resurrectingCodeRatio: 0, animationSpeed: 1, forwardSpeed: 0.25, bloomStrength: 1, @@ -44,10 +45,10 @@ const defaults = { numColumns: 80, density: 1, paletteEntries: [ - { rgb: [0.0, 0.0, 0.0], at: 0.0 }, - { rgb: [0.09, 0.33, 0.04], at: 0.25 }, - { rgb: [0.39, 0.98, 0.38], at: 0.7 }, - { rgb: [0.57, 0.97, 0.61], at: 1.0 } + { hsl: [0.3, 0.9, 0.0], at: 0.0 }, + { hsl: [0.3, 0.9, 0.2], at: 0.2 }, + { hsl: [0.3, 0.9, 0.7], at: 0.7 }, + { hsl: [0.3, 0.9, 0.8], at: 0.8 } ], raindropLength: 1, slant: 0, @@ -77,9 +78,9 @@ const versions = { rippleTypeName: "box", numColumns: 108, paletteEntries: [ - { rgb: [0.0, 0.0, 0.0], at: 0.0 }, - { rgb: [0.18, 0.9, 0.35], at: 0.6 }, - { rgb: [0.9, 1.0, 0.9], at: 1.0 } + { hsl: [0.4, 0.8, 0.0], at: 0.0 }, + { hsl: [0.4, 0.8, 0.5], at: 0.5 }, + { hsl: [0.4, 0.8, 1.0], at: 1.0 } ], raindropLength: 1.5 }, @@ -92,11 +93,11 @@ const versions = { hasThunder: true, numColumns: 60, paletteEntries: [ - { rgb: [0.0, 0.0, 0.0], at: 0.0 }, - { rgb: [0.32, 0.06, 0.0], at: 0.2 }, - { rgb: [0.82, 0.06, 0.05], at: 0.4 }, - { rgb: [1.0, 0.6, 0.3], at: 0.8 }, - { rgb: [1.0, 1.0, 0.9], at: 1.0 } + { hsl: [0.0, 1.0, 0.0], at: 0.0 }, + { hsl: [0.0, 1.0, 0.2], at: 0.2 }, + { hsl: [0.0, 1.0, 0.4], at: 0.4 }, + { hsl: [0.1, 1.0, 0.7], at: 0.7 }, + { hsl: [0.2, 1.0, 1.0], at: 1.0 } ], raindropLength: 0.6, slant: (22.5 * Math.PI) / 180 @@ -115,13 +116,23 @@ const versions = { rippleSpeed: 0.1, numColumns: 30, paletteEntries: [ - { rgb: [0.0, 0.0, 0.0], at: 0.0 }, - { rgb: [0.52, 0.17, 0.05], at: 0.4 }, - { rgb: [0.82, 0.37, 0.12], at: 0.7 }, - { rgb: [1.0, 0.74, 0.29], at: 0.9 }, - { rgb: [1.0, 0.9, 0.8], at: 1.0 } + { hsl: [0.0, 0.0, 0.0], at: 0.0 }, + { hsl: [0.0, 0.8, 0.3], at: 0.3 }, + { hsl: [0.1, 0.8, 0.5], at: 0.5 }, + { hsl: [0.1, 1.0, 0.6], at: 0.6 }, + { hsl: [0.1, 1.0, 0.9], at: 0.9 } ], raindropLength: 0.4 + }, + resurrections: { + ...defaults, + ...fonts.matrixcode, + resurrectingCodeRatio: 0.25, + effect:"resurrections", + width:100, + volumetric:true, + density:3, + fallSpeed:0.7 } }; versions.throwback = versions.operator; diff --git a/js/main.js b/js/main.js index 8838fe1..e3823e6 100644 --- a/js/main.js +++ b/js/main.js @@ -5,6 +5,7 @@ import makeBloomPass from "./bloomPass.js"; import makePalettePass from "./palettePass.js"; import makeStripePass from "./stripePass.js"; import makeImagePass from "./imagePass.js"; +import makeResurrectionPass from "./resurrectionPass.js"; const canvas = document.createElement("canvas"); document.body.appendChild(canvas); @@ -29,7 +30,9 @@ const effects = { customStripes: makeStripePass, stripes: makeStripePass, pride: makeStripePass, - image: makeImagePass + image: makeImagePass, + resurrection: makeResurrectionPass, + resurrections: makeResurrectionPass }; const config = makeConfig(window.location.search); diff --git a/js/palettePass.js b/js/palettePass.js index f4d1bf4..d7ce755 100644 --- a/js/palettePass.js +++ b/js/palettePass.js @@ -1,5 +1,14 @@ import { extractEntries, make1DTexture, makePassFBO, makePass } from "./utils.js"; +const colorToRGB = ([hue, saturation, lightness]) => { + const a = saturation * Math.min(lightness, 1 - lightness); + const f = (n) => { + const k = (n + hue * 12) % 12; + return lightness - a * Math.max(-1, Math.min(k - 3, 9 - k, 1)); + }; + return [f(0), f(8), f(4)]; +}; + const makePalette = (regl, entries) => { const PALETTE_SIZE = 2048; const paletteColors = Array(PALETTE_SIZE); @@ -7,7 +16,7 @@ const makePalette = (regl, entries) => { .slice() .sort((e1, e2) => e1.at - e2.at) .map(entry => ({ - rgb: entry.rgb, + rgb: colorToRGB(entry.hsl), arrayIndex: Math.floor( Math.max(Math.min(1, entry.at), 0) * (PALETTE_SIZE - 1) ) @@ -73,7 +82,8 @@ export default (regl, config, inputs) => { } void main() { - float brightness = texture2D( tex, vUV ).r + texture2D( bloomTex, vUV ).r; + vec4 brightnessRGB = texture2D( tex, vUV ) + texture2D( bloomTex, vUV ); + float brightness = brightnessRGB.r + brightnessRGB.g + brightnessRGB.b; float at = brightness - rand( gl_FragCoord.xy, time ) * ditherMagnitude; gl_FragColor = texture2D( palette, vec2(at, 0.0)) + vec4(backgroundColor, 0.0); } diff --git a/js/renderer.js b/js/renderer.js index d350861..e98bcfa 100644 --- a/js/renderer.js +++ b/js/renderer.js @@ -65,6 +65,7 @@ export default (regl, config) => { "rippleScale", "rippleSpeed", "rippleThickness", + "resurrectingCodeRatio", // rain vertex "forwardSpeed", // rain render @@ -293,6 +294,7 @@ export default (regl, config) => { } }, vert: ` + #define PI 3.14159265359 precision lowp float; attribute vec2 aPosition, aCorner; uniform sampler2D lastState; @@ -304,8 +306,17 @@ export default (regl, config) => { uniform float time, animationSpeed, forwardSpeed; uniform bool volumetric; uniform bool showComputationTexture; + uniform float resurrectingCodeRatio; varying vec2 vUV; + varying vec3 vChannel; varying vec4 vGlyph; + + 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() { vUV = (aPosition + aCorner) * quadSize; @@ -319,8 +330,16 @@ export default (regl, config) => { vec2 position = (aPosition + aCorner * vec2(density, 1.)) * quadSize; vec4 pos = vec4((position - 0.5) * 2.0, quadDepth, 1.0); + vChannel = vec3(1.0, 0.0, 0.0); + if (volumetric) { + if (rand(vec2(aPosition.x, 0)) < resurrectingCodeRatio) { + pos.y = -pos.y; + vChannel = vec3(0.0, 1.0, 0.0); + } + pos.x /= glyphHeightToWidth; + pos = camera * transform * pos; } else { pos.xy *= screenSize; @@ -348,6 +367,7 @@ export default (regl, config) => { uniform bool volumetric; varying vec2 vUV; + varying vec3 vChannel; varying vec4 vGlyph; float median3(vec3 i) { @@ -416,7 +436,7 @@ export default (regl, config) => { float sigDist = median3(dist) - 0.5; float alpha = clamp(sigDist/fwidth(sigDist) + 0.5, 0.0, 1.0); - gl_FragColor = vec4(vec3(brightness * alpha), 1.0); + gl_FragColor = vec4(vChannel * brightness * alpha, 1.0); } `, diff --git a/js/resurrectionPass.js b/js/resurrectionPass.js new file mode 100644 index 0000000..8bfb717 --- /dev/null +++ b/js/resurrectionPass.js @@ -0,0 +1,78 @@ +import { extractEntries, make1DTexture, makePassFBO, makePass } from "./utils.js"; + +const colorToRGB = ([hue, saturation, lightness]) => { + const a = saturation * Math.min(lightness, 1 - lightness); + const f = (n) => { + const k = (n + hue * 12) % 12; + return lightness - a * Math.max(-1, Math.min(k - 3, 9 - k, 1)); + }; + return [f(0), f(8), f(4)]; +}; + +export default (regl, config, inputs) => { + const output = makePassFBO(regl, config.useHalfFloat); + + return makePass( + { + primary: output + }, + regl({ + frag: ` + precision mediump float; + #define PI 3.14159265359 + + uniform sampler2D tex; + uniform sampler2D bloomTex; + uniform float ditherMagnitude; + uniform float time; + uniform vec3 backgroundColor; + varying vec2 vUV; + + highp float rand( const in vec2 uv, const in float t ) { + 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 + t); + } + + float rgbComponent(float p, float q, float t) { + if (t < 0.0) t += 1.0; + if (t > 1.0) t -= 1.0; + if (t < 1.0 / 6.0) return p + (q - p) * 6.0 * t; + if (t < 1.0 / 2.0) return q; + if (t < 2.0 / 3.0) return p + (q - p) * (2.0 / 3.0 - t) * 6.0; + return p; + } + + vec3 hslToRgb(float h, float s, float l){ + float q = l < 0.5 ? l * (1. + s) : l + s - l * s; + float p = 2.0 * l - q; + return vec3( + rgbComponent(p, q, h + 1.0 / 3.0), + rgbComponent(p, q, h), + rgbComponent(p, q, h - 1.0 / 3.0) + ); + } + + void main() { + + vec3 brightness = mix(texture2D( bloomTex, vUV ).rgb, texture2D( tex, vUV ).rgb, (0.7 - length(vUV - 0.5))) * 1.25 - rand( gl_FragCoord.xy, time ) * ditherMagnitude; + + float hue = 0.35 + (length(vUV - vec2(0.5, 1.0)) * -0.4 + 0.2); + vec3 rgb = hslToRgb(hue, 0.8, max(0., brightness.r)) * vec3(0.8, 1.0, 0.7); + vec3 resurrectionRGB = hslToRgb(0.13, 1.0, max(0., brightness.g) * 0.5); + gl_FragColor = vec4(rgb + resurrectionRGB + backgroundColor, 1.0); + } + `, + + uniforms: { + ...extractEntries(config, [ + "backgroundColor", + ]), + tex: inputs.primary, + bloomTex: inputs.bloom, + ditherMagnitude: 0.05 + }, + framebuffer: output + }) + ); +};