mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-17 13:59:30 -07:00
- Added "slant" param to readme
- getParam now supports synonyms - Renamed "dropLength" URL param to "raindropLength", keeping around support for dropLength URL param - Renamed "texture" to "fontTexture" - Changed "isSlanted" boolean to "slant" angle, adding slant/angle URL param, converted from degrees to radians - Renamed "numGlyphColumns" to "numFontColumns" - Renamed "showRTT" to "showComputationTexture" - Random glyphs are now based on a simple sine scramble - Renaming "now" and "delta" to "time" and "deltaTime" - glyphCycleSpeed is no longer premultiplied; it is now a number between 0 and 1, much easier to visualize
This commit is contained in:
@@ -54,7 +54,8 @@ Now you know link fu. Here's a list of customization options:
|
||||
|
||||
- **version** - the version of the Matrix to simulate. Can be "paradise", "nightmare" or "1999" (default).
|
||||
- **width** - the number of columns (and rows) to draw. Default is 80.
|
||||
- **dropLength** - the vertical scale of "raindrops" in the columns. Can be any number, even negative! Default is 1.0.
|
||||
- **slant** - which angle is up, in degrees. Default is 0.
|
||||
- **raindropLength** - the vertical scale of "raindrops" in the columns. Can be any number, even negative! Default is 1.0.
|
||||
- **animationSpeed** - the overall speed of the animation. Can be any number, even negative! Default is 1.0.
|
||||
- **fallSpeed** - the speed of the rain. Can be any number, even negative! Default is 1.0.
|
||||
- **cycleSpeed** - the speed that the glyphs change their symbol. Can be any number, even negative! Default is 1.0.
|
||||
|
||||
46
index.html
46
index.html
@@ -26,8 +26,8 @@
|
||||
|
||||
const versions = {
|
||||
paradise: {
|
||||
texture: './coptic_msdf.png',
|
||||
dropLength: 0.5,
|
||||
fontTexture: './coptic_msdf.png',
|
||||
raindropLength: 0.5,
|
||||
glyphSequenceLength: 32,
|
||||
bloom: {
|
||||
strength: 4,
|
||||
@@ -45,13 +45,13 @@
|
||||
cycleSpeed: 0.05,
|
||||
hasThunder: false,
|
||||
hasSun: true,
|
||||
isSlanted: false,
|
||||
slant: 0,
|
||||
isPolar: true,
|
||||
numColumns: 50
|
||||
},
|
||||
nightmare: {
|
||||
texture: './gothic_msdf.png',
|
||||
dropLength: 0.6,
|
||||
fontTexture: './gothic_msdf.png',
|
||||
raindropLength: 0.6,
|
||||
glyphSequenceLength: 27,
|
||||
bloom: {
|
||||
strength: 2,
|
||||
@@ -69,13 +69,13 @@
|
||||
cycleSpeed: 0.02,
|
||||
hasThunder: true,
|
||||
hasSun: false,
|
||||
isSlanted: true,
|
||||
slant: 360 / 16,
|
||||
isPolar: false,
|
||||
numColumns: 60
|
||||
},
|
||||
["1999"]: {
|
||||
texture: './matrixcode_msdf.png',
|
||||
dropLength: 1,
|
||||
fontTexture: './matrixcode_msdf.png',
|
||||
raindropLength: 1,
|
||||
glyphSequenceLength: 57,
|
||||
bloom: {
|
||||
strength: 2,
|
||||
@@ -110,14 +110,23 @@
|
||||
cycleSpeed: 1,
|
||||
hasThunder: false,
|
||||
hasSun: false,
|
||||
isSlanted: false,
|
||||
slant: 0,
|
||||
isPolar: false,
|
||||
numColumns: 80
|
||||
}
|
||||
};
|
||||
|
||||
const urlParams = new Map(window.location.href.replace(/^[^\?]+\?/, "").split("&").map(pair => pair.split("=")));
|
||||
const getParam = (key, defaultValue) => urlParams.has(key) ? urlParams.get(key) : defaultValue;
|
||||
const getParam = (keyOrKeys, defaultValue) => {
|
||||
if (Array.isArray(keyOrKeys)) {
|
||||
const keys = keyOrKeys;
|
||||
const key = keys.find(key => urlParams.has(key));
|
||||
return key != null ? urlParams.get(key) : defaultValue;
|
||||
} else {
|
||||
const key = keyOrKeys;
|
||||
return urlParams.has(key) ? urlParams.get(key) : defaultValue;
|
||||
}
|
||||
};
|
||||
|
||||
const version = versions[getParam("version", "1999")] || versions["1999"];
|
||||
|
||||
@@ -126,9 +135,10 @@
|
||||
const fallSpeed = parseFloat(getParam("fallSpeed", 1)) * version.fallSpeed;
|
||||
const cycleSpeed = parseFloat(getParam("cycleSpeed", 1)) * version.cycleSpeed;
|
||||
const numColumns = parseInt(getParam("width", version.numColumns));
|
||||
const dropLength = parseFloat(getParam("dropLength", version.dropLength));
|
||||
const numGlyphColumns = 8;
|
||||
const raindropLength = parseFloat(getParam(["raindropLength", "dropLength"], version.raindropLength));
|
||||
const numFontColumns = 8;
|
||||
const glyphSequenceLength = version.glyphSequenceLength;
|
||||
const slant = parseFloat(getParam(["slant", "angle"], version.slant)) * Math.PI / 180;
|
||||
|
||||
const effect = getParam("effect", "plain");
|
||||
|
||||
@@ -142,20 +152,20 @@
|
||||
element.appendChild(renderer.domElement);
|
||||
const composer = new THREE.EffectComposer( renderer );
|
||||
|
||||
const texture = new THREE.TextureLoader().load( version.texture );
|
||||
const fontTexture = new THREE.TextureLoader().load( version.fontTexture );
|
||||
|
||||
const matrixRenderer = makeMatrixRenderer(renderer, texture, {
|
||||
const matrixRenderer = makeMatrixRenderer(renderer, fontTexture, {
|
||||
sharpness,
|
||||
numColumns,
|
||||
animationSpeed, fallSpeed, cycleSpeed,
|
||||
glyphSequenceLength,
|
||||
numGlyphColumns,
|
||||
numFontColumns,
|
||||
hasThunder: version.hasThunder,
|
||||
hasSun: version.hasSun,
|
||||
isPolar: version.isPolar,
|
||||
isSlanted: version.isSlanted,
|
||||
showRTT: effect === "none",
|
||||
dropLength
|
||||
slant,
|
||||
showComputationTexture: effect === "none",
|
||||
raindropLength
|
||||
});
|
||||
|
||||
matrixRenderer.pass.renderToScreen = false;
|
||||
|
||||
@@ -3,13 +3,13 @@ const makeMatrixRenderer = (renderer, texture, {
|
||||
numColumns,
|
||||
animationSpeed, fallSpeed, cycleSpeed,
|
||||
glyphSequenceLength,
|
||||
numGlyphColumns,
|
||||
numFontColumns,
|
||||
hasThunder,
|
||||
hasSun,
|
||||
isPolar,
|
||||
isSlanted,
|
||||
showRTT,
|
||||
dropLength
|
||||
slant,
|
||||
showComputationTexture,
|
||||
raindropLength
|
||||
}) => {
|
||||
const matrixRenderer = {};
|
||||
const camera = new THREE.OrthographicCamera( -0.5, 0.5, 0.5, -0.5, 0.0001, 10000 );
|
||||
@@ -17,9 +17,12 @@ const makeMatrixRenderer = (renderer, texture, {
|
||||
const gpuCompute = new GPUComputationRenderer( numColumns, numColumns, renderer );
|
||||
const glyphValue = gpuCompute.createTexture();
|
||||
const pixels = glyphValue.image.data;
|
||||
|
||||
const scramble = i => Math.sin(i) * 0.5 + 0.5;
|
||||
|
||||
for (let i = 0; i < numColumns * numColumns; i++) {
|
||||
pixels[i * 4 + 0] = 0;
|
||||
pixels[i * 4 + 1] = Math.random();
|
||||
pixels[i * 4 + 1] = showComputationTexture ? 0 : scramble(i);
|
||||
pixels[i * 4 + 2] = 0;
|
||||
pixels[i * 4 + 3] = 0;
|
||||
}
|
||||
@@ -31,15 +34,15 @@ const glyphVariable = gpuCompute.addVariable(
|
||||
#define PI 3.14159265359
|
||||
#define SQRT_2 1.4142135623730951
|
||||
#define SQRT_5 2.23606797749979
|
||||
uniform float now;
|
||||
uniform float delta;
|
||||
uniform float time;
|
||||
uniform float deltaTime;
|
||||
uniform float animationSpeed;
|
||||
uniform float fallSpeed;
|
||||
uniform float cycleSpeed;
|
||||
uniform float brightnessChangeBias;
|
||||
uniform float glyphSequenceLength;
|
||||
uniform float numGlyphColumns;
|
||||
uniform float dropLength;
|
||||
uniform float numFontColumns;
|
||||
uniform float raindropLength;
|
||||
|
||||
highp float rand( const in vec2 uv ) {
|
||||
const highp float a = 12.9898, b = 78.233, c = 43758.5453;
|
||||
@@ -62,11 +65,11 @@ const glyphVariable = gpuCompute.addVariable(
|
||||
vec4 data = texture2D( glyph, uv );
|
||||
|
||||
float brightness = data.r;
|
||||
float cycle = data.g;
|
||||
float glyphCycle = data.g;
|
||||
|
||||
float simTime = now * 0.0005 * animationSpeed;
|
||||
float simTime = time * 0.0005 * animationSpeed;
|
||||
float columnTime = (columnTimeOffset * 1000.0 + simTime * fallSpeed) * (0.5 + columnSpeedOffset * 0.5) + (sin(simTime * fallSpeed * 2.0 * columnSpeedOffset) * 0.2);
|
||||
float glyphTime = (gl_FragCoord.y * 0.01 + columnTime) / dropLength;
|
||||
float glyphTime = (gl_FragCoord.y * 0.01 + columnTime) / raindropLength;
|
||||
|
||||
float value = 1.0 - fract((glyphTime + 0.3 * sin(SQRT_2 * glyphTime) + 0.2 * sin(SQRT_5 * glyphTime)));
|
||||
|
||||
@@ -92,23 +95,23 @@ const glyphVariable = gpuCompute.addVariable(
|
||||
brightness = mix(brightness, newBrightness, brightnessChangeBias);
|
||||
#endif
|
||||
|
||||
float glyphCycleSpeed = (brightness < 0.0) ? 0.0 : delta * cycleSpeed * 0.2 * pow(1.0 - brightness, 4.0);
|
||||
cycle = fract(cycle + glyphCycleSpeed);
|
||||
float symbol = floor(glyphSequenceLength * cycle);
|
||||
float symbolX = mod(symbol, numGlyphColumns);
|
||||
float symbolY = ((numGlyphColumns - 1.0) - (symbol - symbolX) / numGlyphColumns);
|
||||
float glyphCycleSpeed = (brightness <= 0.0) ? 0.0 : pow(1.0 - brightness, 4.0);
|
||||
glyphCycle = fract(glyphCycle + deltaTime * cycleSpeed * 0.2 * glyphCycleSpeed);
|
||||
float symbol = floor(glyphSequenceLength * glyphCycle);
|
||||
float symbolX = mod(symbol, numFontColumns);
|
||||
float symbolY = ((numFontColumns - 1.0) - (symbol - symbolX) / numFontColumns);
|
||||
|
||||
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
gl_FragColor.r = brightness;
|
||||
gl_FragColor.g = cycle;
|
||||
gl_FragColor.g = glyphCycle;
|
||||
|
||||
#ifdef showRTT
|
||||
#ifdef showComputationTexture
|
||||
// Better use of the blue channel, for show and tell
|
||||
gl_FragColor.b = min(1.0, glyphCycleSpeed * 500.0);
|
||||
gl_FragColor.b = min(1.0, glyphCycleSpeed);
|
||||
gl_FragColor.a = 1.0;
|
||||
#else
|
||||
gl_FragColor.b = symbolX / numGlyphColumns;
|
||||
gl_FragColor.a = symbolY / numGlyphColumns;
|
||||
gl_FragColor.b = symbolX / numFontColumns;
|
||||
gl_FragColor.a = symbolY / numFontColumns;
|
||||
#endif
|
||||
}
|
||||
`
|
||||
@@ -119,14 +122,14 @@ const glyphVariable = gpuCompute.addVariable(
|
||||
|
||||
const brightnessChangeBias = (animationSpeed * fallSpeed) == 0 ? 1 : Math.min(1, Math.abs(animationSpeed * fallSpeed));
|
||||
Object.assign(glyphVariable.material.uniforms, {
|
||||
now: { type: "f", value: 0 },
|
||||
delta: { type: "f", value: 0.01 },
|
||||
time: { type: "f", value: 0 },
|
||||
deltaTime: { type: "f", value: 0.01 },
|
||||
animationSpeed: { type: "f", value: animationSpeed },
|
||||
fallSpeed: { type: "f", value: fallSpeed },
|
||||
cycleSpeed: {type: "f", value: cycleSpeed },
|
||||
glyphSequenceLength: { type: "f", value: glyphSequenceLength },
|
||||
numGlyphColumns: {type: "f", value: numGlyphColumns },
|
||||
dropLength: {type: "f", value: dropLength },
|
||||
numFontColumns: {type: "f", value: numFontColumns },
|
||||
raindropLength: {type: "f", value: raindropLength },
|
||||
brightnessChangeBias: { type: "f", value: brightnessChangeBias },
|
||||
});
|
||||
if (hasThunder) {
|
||||
@@ -135,8 +138,8 @@ const glyphVariable = gpuCompute.addVariable(
|
||||
if (hasSun) {
|
||||
glyphVariable.material.defines.hasSun = 1.0;
|
||||
}
|
||||
if (showRTT) {
|
||||
glyphVariable.material.defines.showRTT = 1.0;
|
||||
if (showComputationTexture) {
|
||||
glyphVariable.material.defines.showComputationTexture = 1.0;
|
||||
}
|
||||
|
||||
const error = gpuCompute.init();
|
||||
@@ -154,8 +157,9 @@ const glyphVariable = gpuCompute.addVariable(
|
||||
msdf: { type: "t", value: texture },
|
||||
numColumns: {type: "f", value: numColumns},
|
||||
sharpness: { type: "f", value: sharpness },
|
||||
numGlyphColumns: {type: "f", value: numGlyphColumns},
|
||||
numFontColumns: {type: "f", value: numFontColumns},
|
||||
resolution: {type: "v2", value: new THREE.Vector2() },
|
||||
slant: {type: "v2", value: new THREE.Vector2(Math.cos(slant), Math.sin(slant)) }
|
||||
},
|
||||
vertexShader: `
|
||||
attribute vec2 uv;
|
||||
@@ -179,7 +183,8 @@ const glyphVariable = gpuCompute.addVariable(
|
||||
uniform sampler2D msdf;
|
||||
uniform sampler2D glyphs;
|
||||
uniform float numColumns;
|
||||
uniform float numGlyphColumns;
|
||||
uniform float numFontColumns;
|
||||
uniform vec2 slant;
|
||||
varying vec2 vUV;
|
||||
|
||||
float median(float r, float g, float b) {
|
||||
@@ -189,23 +194,22 @@ const glyphVariable = gpuCompute.addVariable(
|
||||
void main() {
|
||||
|
||||
vec2 uv = vUV;
|
||||
|
||||
uv = vec2(
|
||||
(uv.x - 0.5) * slant.x + (uv.y - 0.5) * slant.y,
|
||||
(uv.y - 0.5) * slant.x - (uv.x - 0.5) * slant.y
|
||||
) * 0.75 + 0.5;
|
||||
|
||||
#ifdef isPolar
|
||||
vec2 diff = vUV - vec2(0.5, 1.25);
|
||||
vec2 diff = uv - vec2(0.5, 1.25);
|
||||
float radius = length(diff);
|
||||
float angle = atan(diff.y, diff.x) + PI;
|
||||
uv = vec2(angle / PI, 1.0 - pow(radius * 0.75, 0.6));
|
||||
#endif
|
||||
#ifdef isSlanted
|
||||
float angle = PI * 0.125;
|
||||
vec2 rotation = vec2(cos(angle), sin(angle));
|
||||
uv = vec2(
|
||||
(vUV.x - 0.5) * rotation.x + (vUV.y - 0.5) * rotation.y,
|
||||
(vUV.y - 0.5) * rotation.x - (vUV.x - 0.5) * rotation.y) * 0.75 + 0.5;
|
||||
#endif
|
||||
|
||||
vec4 glyph = texture2D(glyphs, uv);
|
||||
|
||||
#ifdef showRTT
|
||||
#ifdef showComputationTexture
|
||||
gl_FragColor = glyph;
|
||||
return;
|
||||
#endif
|
||||
@@ -213,7 +217,7 @@ const glyphVariable = gpuCompute.addVariable(
|
||||
// Unpack the values from the glyph texture
|
||||
float brightness = glyph.r;
|
||||
vec2 symbolUV = glyph.ba;
|
||||
vec4 sample = texture2D(msdf, fract(uv * numColumns) / numGlyphColumns + symbolUV);
|
||||
vec4 sample = texture2D(msdf, fract(uv * numColumns) / numFontColumns + symbolUV);
|
||||
|
||||
// The rest is straight up MSDF
|
||||
float sigDist = median(sample.r, sample.g, sample.b) - 0.5;
|
||||
@@ -239,12 +243,8 @@ const glyphVariable = gpuCompute.addVariable(
|
||||
mesh.material.defines.isPolar = 1.0;
|
||||
}
|
||||
|
||||
if (isSlanted) {
|
||||
mesh.material.defines.isSlanted = 1.0;
|
||||
}
|
||||
|
||||
if (showRTT) {
|
||||
mesh.material.defines.showRTT = 1.0;
|
||||
if (showComputationTexture) {
|
||||
mesh.material.defines.showComputationTexture = 1.0;
|
||||
}
|
||||
|
||||
scene.add( mesh );
|
||||
@@ -266,11 +266,11 @@ const glyphVariable = gpuCompute.addVariable(
|
||||
return;
|
||||
}
|
||||
|
||||
const delta = ((now - last > 1000) ? 0 : now - last) / 1000 * animationSpeed;
|
||||
const deltaTime = ((now - last > 1000) ? 0 : now - last) / 1000 * animationSpeed;
|
||||
last = now;
|
||||
|
||||
glyphVariable.material.uniforms.now.value = now;
|
||||
glyphVariable.material.uniforms.delta.value = delta;
|
||||
glyphVariable.material.uniforms.time.value = now;
|
||||
glyphVariable.material.uniforms.deltaTime.value = deltaTime;
|
||||
|
||||
gpuCompute.compute();
|
||||
renderer.render( scene, camera );
|
||||
|
||||
Reference in New Issue
Block a user