mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-18 14:19:30 -07:00
Removed sun shower. Thunder and ripples are now handled by a third compute shader.
This commit is contained in:
13
TODO.txt
13
TODO.txt
@@ -1,15 +1,20 @@
|
|||||||
TODO:
|
TODO:
|
||||||
|
|
||||||
|
Test all the versions!
|
||||||
|
|
||||||
Reformulate the basis
|
Reformulate the basis
|
||||||
https://buf.com/films/the-matrix-resurrections
|
https://buf.com/films/the-matrix-resurrections
|
||||||
Rain pass frag's output should match its debug view output
|
Rain pass frag's output should match its debug view output
|
||||||
|
Move brightness and contrast logic to the later passes
|
||||||
The channels get individually blurred
|
The channels get individually blurred
|
||||||
Then the other passes use the red, green and blue channels for separate things
|
Then the other passes use the red, green and blue channels for separate things
|
||||||
|
red: cursor
|
||||||
|
green: long tail
|
||||||
|
blue: short tail
|
||||||
Tune the colors
|
Tune the colors
|
||||||
Maybe glow can be an SDF-derived effect instead, look into it
|
New config properties
|
||||||
|
Find a way to support the old stuff?
|
||||||
Migrate the rest of the project over
|
Migrate to WebGPU
|
||||||
Migrate WebGPU
|
|
||||||
|
|
||||||
Audio system
|
Audio system
|
||||||
Toggle (or number representing frequency)
|
Toggle (or number representing frequency)
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ const defaults = {
|
|||||||
glyphEdgeCrop: 0.0, // The border around a glyph in a font texture that should be cropped out
|
glyphEdgeCrop: 0.0, // The border around a glyph in a font texture that should be cropped out
|
||||||
glyphHeightToWidth: 1, // The aspect ratio of glyphs
|
glyphHeightToWidth: 1, // The aspect ratio of glyphs
|
||||||
glyphVerticalSpacing: 1, // The ratio of the vertical distance between glyphs to their height
|
glyphVerticalSpacing: 1, // The ratio of the vertical distance between glyphs to their height
|
||||||
hasSun: false, // Makes the glyphs more radiant. Admittedly not very technical.
|
|
||||||
hasThunder: false, // An effect that adds dramatic lightning flashes
|
hasThunder: false, // An effect that adds dramatic lightning flashes
|
||||||
isPolar: false, // Whether the glyphs arc across the screen or sit in a standard grid
|
isPolar: false, // Whether the glyphs arc across the screen or sit in a standard grid
|
||||||
rippleTypeName: null, // The variety of the ripple effect
|
rippleTypeName: null, // The variety of the ripple effect
|
||||||
@@ -154,10 +153,10 @@ const versions = {
|
|||||||
bloomStrength: 1,
|
bloomStrength: 1,
|
||||||
highPassThreshold: 0,
|
highPassThreshold: 0,
|
||||||
cycleSpeed: 0.05,
|
cycleSpeed: 0.05,
|
||||||
baseBrightness: -0.1,
|
baseBrightness: -1.3,
|
||||||
|
baseContrast: 2,
|
||||||
brightnessDecay: 0.05,
|
brightnessDecay: 0.05,
|
||||||
fallSpeed: 0.04,
|
fallSpeed: 0.04,
|
||||||
hasSun: true,
|
|
||||||
isPolar: true,
|
isPolar: true,
|
||||||
rippleTypeName: "circle",
|
rippleTypeName: "circle",
|
||||||
rippleSpeed: 0.1,
|
rippleSpeed: 0.1,
|
||||||
@@ -211,7 +210,6 @@ const versions = {
|
|||||||
fallSpeed: 0.1,
|
fallSpeed: 0.1,
|
||||||
cycleStyleName: "cycleRandomly",
|
cycleStyleName: "cycleRandomly",
|
||||||
highPassThreshold: 0.0,
|
highPassThreshold: 0.0,
|
||||||
hasSun: true,
|
|
||||||
paletteEntries: [
|
paletteEntries: [
|
||||||
{ hsl: [0.6, 1.0, 0.05], at: 0.0 },
|
{ hsl: [0.6, 1.0, 0.05], at: 0.0 },
|
||||||
{ hsl: [0.6, 0.8, 0.1], at: 0.1 },
|
{ hsl: [0.6, 0.8, 0.1], at: 0.1 },
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const loadJS = (src) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default async (canvas, config) => {
|
export default async (canvas, config) => {
|
||||||
await Promise.all([loadJS("lib/regl.min.js"), loadJS("lib/gl-matrix.js")]);
|
await Promise.all([loadJS("lib/regl.js"), loadJS("lib/gl-matrix.js")]);
|
||||||
|
|
||||||
const resize = () => {
|
const resize = () => {
|
||||||
canvas.width = Math.ceil(canvas.clientWidth * config.resolution);
|
canvas.width = Math.ceil(canvas.clientWidth * config.resolution);
|
||||||
|
|||||||
@@ -12,6 +12,21 @@ const cycleStyles = {
|
|||||||
cycleRandomly: 1,
|
cycleRandomly: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// These compute buffers are used to compute the properties of cells in the grid.
|
||||||
|
// They take turns being the source and destination of a "compute" shader.
|
||||||
|
// The half float data type is crucial! It lets us store almost any real number,
|
||||||
|
// whereas the default type limits us to integers between 0 and 255.
|
||||||
|
|
||||||
|
// These double buffers are smaller than the screen, because their pixels correspond
|
||||||
|
// with cells in the grid, and the cells' glyphs are much larger than a pixel.
|
||||||
|
const makeComputeDoubleBuffer = (regl, height, width) =>
|
||||||
|
makeDoubleBuffer(regl, {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
wrapT: "clamp",
|
||||||
|
type: "half float",
|
||||||
|
});
|
||||||
|
|
||||||
const numVerticesPerQuad = 2 * 3;
|
const numVerticesPerQuad = 2 * 3;
|
||||||
const tlVert = [0, 0];
|
const tlVert = [0, 0];
|
||||||
const trVert = [0, 1];
|
const trVert = [0, 1];
|
||||||
@@ -46,36 +61,11 @@ export default ({ regl, config, lkg }) => {
|
|||||||
showDebugView,
|
showDebugView,
|
||||||
};
|
};
|
||||||
|
|
||||||
// These two framebuffers are used to compute the raining code.
|
const shineDoubleBuffer = makeComputeDoubleBuffer(regl, numRows, numColumns);
|
||||||
// they take turns being the source and destination of the "compute" shader.
|
|
||||||
// The half float data type is crucial! It lets us store almost any real number,
|
|
||||||
// whereas the default type limits us to integers between 0 and 255.
|
|
||||||
|
|
||||||
// This double buffer is smaller than the screen, because its pixels correspond
|
|
||||||
// with glyphs in the final image, and the glyphs are much larger than a pixel.
|
|
||||||
const shineDoubleBuffer = makeDoubleBuffer(regl, {
|
|
||||||
width: numColumns,
|
|
||||||
height: numRows,
|
|
||||||
wrapT: "clamp",
|
|
||||||
type: "half float",
|
|
||||||
});
|
|
||||||
const rainPassShine = loadText("shaders/glsl/rainPass.shine.frag.glsl");
|
const rainPassShine = loadText("shaders/glsl/rainPass.shine.frag.glsl");
|
||||||
const shineUniforms = {
|
const shineUniforms = {
|
||||||
...commonUniforms,
|
...commonUniforms,
|
||||||
...extractEntries(config, [
|
...extractEntries(config, ["baseBrightness", "baseContrast", "brightnessDecay", "fallSpeed", "raindropLength", "loops"]),
|
||||||
"baseBrightness",
|
|
||||||
"baseContrast",
|
|
||||||
"brightnessDecay",
|
|
||||||
"fallSpeed",
|
|
||||||
"hasSun",
|
|
||||||
"hasThunder",
|
|
||||||
"raindropLength",
|
|
||||||
"rippleScale",
|
|
||||||
"rippleSpeed",
|
|
||||||
"rippleThickness",
|
|
||||||
"loops",
|
|
||||||
]),
|
|
||||||
rippleType,
|
|
||||||
};
|
};
|
||||||
const shine = regl({
|
const shine = regl({
|
||||||
frag: regl.prop("frag"),
|
frag: regl.prop("frag"),
|
||||||
@@ -87,12 +77,7 @@ export default ({ regl, config, lkg }) => {
|
|||||||
framebuffer: shineDoubleBuffer.front,
|
framebuffer: shineDoubleBuffer.front,
|
||||||
});
|
});
|
||||||
|
|
||||||
const symbolDoubleBuffer = makeDoubleBuffer(regl, {
|
const symbolDoubleBuffer = makeComputeDoubleBuffer(regl, numRows, numColumns);
|
||||||
width: numColumns,
|
|
||||||
height: numRows,
|
|
||||||
wrapT: "clamp",
|
|
||||||
type: "half float",
|
|
||||||
});
|
|
||||||
const rainPassSymbol = loadText("shaders/glsl/rainPass.symbol.frag.glsl");
|
const rainPassSymbol = loadText("shaders/glsl/rainPass.symbol.frag.glsl");
|
||||||
const symbolUniforms = {
|
const symbolUniforms = {
|
||||||
...commonUniforms,
|
...commonUniforms,
|
||||||
@@ -110,6 +95,24 @@ export default ({ regl, config, lkg }) => {
|
|||||||
framebuffer: symbolDoubleBuffer.front,
|
framebuffer: symbolDoubleBuffer.front,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const effectDoubleBuffer = makeComputeDoubleBuffer(regl, numRows, numColumns);
|
||||||
|
const rainPassEffect = loadText("shaders/glsl/rainPass.effect.frag.glsl");
|
||||||
|
const effectUniforms = {
|
||||||
|
...commonUniforms,
|
||||||
|
...extractEntries(config, ["hasThunder", "rippleScale", "rippleSpeed", "rippleThickness", "loops"]),
|
||||||
|
rippleType,
|
||||||
|
};
|
||||||
|
const effect = regl({
|
||||||
|
frag: regl.prop("frag"),
|
||||||
|
uniforms: {
|
||||||
|
...effectUniforms,
|
||||||
|
shineState: shineDoubleBuffer.front,
|
||||||
|
previousEffectState: effectDoubleBuffer.back,
|
||||||
|
},
|
||||||
|
|
||||||
|
framebuffer: effectDoubleBuffer.front,
|
||||||
|
});
|
||||||
|
|
||||||
const quadPositions = Array(numQuadRows)
|
const quadPositions = Array(numQuadRows)
|
||||||
.fill()
|
.fill()
|
||||||
.map((_, y) =>
|
.map((_, y) =>
|
||||||
@@ -160,6 +163,7 @@ export default ({ regl, config, lkg }) => {
|
|||||||
|
|
||||||
shineState: shineDoubleBuffer.front,
|
shineState: shineDoubleBuffer.front,
|
||||||
symbolState: symbolDoubleBuffer.front,
|
symbolState: symbolDoubleBuffer.front,
|
||||||
|
effectState: effectDoubleBuffer.front,
|
||||||
glyphTex: msdf.texture,
|
glyphTex: msdf.texture,
|
||||||
|
|
||||||
camera: regl.prop("camera"),
|
camera: regl.prop("camera"),
|
||||||
@@ -254,6 +258,7 @@ export default ({ regl, config, lkg }) => {
|
|||||||
() => {
|
() => {
|
||||||
shine({ frag: rainPassShine.text() });
|
shine({ frag: rainPassShine.text() });
|
||||||
symbol({ frag: rainPassSymbol.text() });
|
symbol({ frag: rainPassSymbol.text() });
|
||||||
|
effect({ frag: rainPassEffect.text() });
|
||||||
regl.clear({
|
regl.clear({
|
||||||
depth: 1,
|
depth: 1,
|
||||||
color: [0, 0, 0, 1],
|
color: [0, 0, 0, 1],
|
||||||
|
|||||||
99
shaders/glsl/rainPass.effect.frag.glsl
Normal file
99
shaders/glsl/rainPass.effect.frag.glsl
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
precision highp float;
|
||||||
|
|
||||||
|
// These effects are used to spice up the non-canon versions of the code rain.
|
||||||
|
// The shader writes them to the channels of a data texture:
|
||||||
|
// R: multiplied effects— magnify the cell's brightness
|
||||||
|
// G: added effects— offset the cell's brightness
|
||||||
|
// B: unused
|
||||||
|
// A: unused
|
||||||
|
|
||||||
|
#define SQRT_2 1.4142135623730951
|
||||||
|
#define SQRT_5 2.23606797749979
|
||||||
|
|
||||||
|
uniform sampler2D previousEffectState;
|
||||||
|
uniform float numColumns, numRows;
|
||||||
|
uniform float time, tick;
|
||||||
|
uniform float animationSpeed;
|
||||||
|
|
||||||
|
uniform bool hasThunder, loops;
|
||||||
|
uniform float glyphHeightToWidth;
|
||||||
|
uniform int rippleType;
|
||||||
|
uniform float rippleScale, rippleSpeed, rippleThickness;
|
||||||
|
|
||||||
|
// Helper functions for generating randomness, borrowed from elsewhere
|
||||||
|
|
||||||
|
vec2 randomVec2( const in vec2 uv ) {
|
||||||
|
return fract(vec2(sin(uv.x * 591.32 + uv.y * 154.077), cos(uv.x * 391.32 + uv.y * 49.077)));
|
||||||
|
}
|
||||||
|
|
||||||
|
float wobble(float x) {
|
||||||
|
return x + 0.3 * sin(SQRT_2 * x) + 0.2 * sin(SQRT_5 * x);
|
||||||
|
}
|
||||||
|
|
||||||
|
float getThunder(float simTime, vec2 screenPos) {
|
||||||
|
if (!hasThunder) {
|
||||||
|
return 0.;
|
||||||
|
}
|
||||||
|
|
||||||
|
simTime *= 0.5;
|
||||||
|
float thunder = 1. - fract(wobble(simTime));
|
||||||
|
if (loops) {
|
||||||
|
thunder = 1. - fract(simTime + 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
thunder = log(thunder * 1.5) * 4.;
|
||||||
|
thunder = clamp(thunder, 0., 1.) * 10. * pow(screenPos.y, 2.);
|
||||||
|
return thunder;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getRipple(float simTime, vec2 screenPos) {
|
||||||
|
if (rippleType == -1) {
|
||||||
|
return 0.;
|
||||||
|
}
|
||||||
|
|
||||||
|
float rippleTime = (simTime * 0.5 + sin(simTime) * 0.2) * rippleSpeed + 1.; // TODO: clarify
|
||||||
|
if (loops) {
|
||||||
|
rippleTime = (simTime * 0.5) * rippleSpeed + 1.;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 offset = randomVec2(vec2(floor(rippleTime), 0.)) - 0.5;
|
||||||
|
if (loops) {
|
||||||
|
offset = vec2(0.);
|
||||||
|
}
|
||||||
|
vec2 ripplePos = screenPos * 2. - 1. + offset;
|
||||||
|
float rippleDistance;
|
||||||
|
if (rippleType == 0) {
|
||||||
|
vec2 boxDistance = abs(ripplePos) * vec2(1., glyphHeightToWidth);
|
||||||
|
rippleDistance = max(boxDistance.x, boxDistance.y);
|
||||||
|
} else if (rippleType == 1) {
|
||||||
|
rippleDistance = length(ripplePos);
|
||||||
|
}
|
||||||
|
|
||||||
|
float rippleValue = fract(rippleTime) * rippleScale - rippleDistance;
|
||||||
|
|
||||||
|
if (rippleValue > 0. && rippleValue < rippleThickness) {
|
||||||
|
return 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main function
|
||||||
|
|
||||||
|
vec4 computeResult(float simTime, bool isFirstFrame, vec2 glyphPos, vec2 screenPos, vec4 previous) {
|
||||||
|
|
||||||
|
float multipliedEffects = 1.0 + getThunder(simTime, screenPos);
|
||||||
|
float addedEffects = getRipple(simTime, screenPos); // Round or square ripples across the grid
|
||||||
|
|
||||||
|
vec4 result = vec4(multipliedEffects, addedEffects, 0., 0.);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float simTime = time * animationSpeed;
|
||||||
|
bool isFirstFrame = tick <= 1.;
|
||||||
|
vec2 glyphPos = gl_FragCoord.xy;
|
||||||
|
vec2 screenPos = glyphPos / vec2(numColumns, numRows);
|
||||||
|
vec4 previous = texture2D( previousEffectState, screenPos );
|
||||||
|
gl_FragColor = computeResult(simTime, isFirstFrame, glyphPos, screenPos, previous);
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
#endif
|
#endif
|
||||||
precision lowp float;
|
precision lowp float;
|
||||||
|
|
||||||
uniform sampler2D shineState, symbolState;
|
uniform sampler2D shineState, symbolState, effectState;
|
||||||
uniform float numColumns, numRows;
|
uniform float numColumns, numRows;
|
||||||
uniform sampler2D glyphTex;
|
uniform sampler2D glyphTex;
|
||||||
uniform float glyphHeightToWidth, glyphSequenceLength, glyphEdgeCrop;
|
uniform float glyphHeightToWidth, glyphSequenceLength, glyphEdgeCrop;
|
||||||
@@ -17,7 +17,7 @@ uniform bool showDebugView;
|
|||||||
uniform bool volumetric;
|
uniform bool volumetric;
|
||||||
|
|
||||||
varying vec2 vUV;
|
varying vec2 vUV;
|
||||||
varying vec4 vShine, vSymbol;
|
varying vec4 vShine, vSymbol, vEffect;
|
||||||
varying float vDepth;
|
varying float vDepth;
|
||||||
|
|
||||||
float median3(vec3 i) {
|
float median3(vec3 i) {
|
||||||
@@ -63,6 +63,7 @@ void main() {
|
|||||||
// Unpack the values from the data textures
|
// Unpack the values from the data textures
|
||||||
vec4 shine = volumetric ? vShine : texture2D(shineState, uv);
|
vec4 shine = volumetric ? vShine : texture2D(shineState, uv);
|
||||||
vec4 symbol = volumetric ? vSymbol : texture2D(symbolState, uv);
|
vec4 symbol = volumetric ? vSymbol : texture2D(symbolState, uv);
|
||||||
|
vec4 effect = volumetric ? vEffect : texture2D(effectState, uv);
|
||||||
vec2 symbolUV = getSymbolUV(symbol.r);
|
vec2 symbolUV = getSymbolUV(symbol.r);
|
||||||
|
|
||||||
float brightness = shine.r;
|
float brightness = shine.r;
|
||||||
@@ -72,8 +73,10 @@ void main() {
|
|||||||
brightness = brightnessOverride;
|
brightness = brightnessOverride;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
brightness *= effect.r; // multiplied effects
|
||||||
|
brightness += effect.g; // added effects
|
||||||
brightness = max(shine.b * cursorBrightness, brightness);
|
brightness = max(shine.b * cursorBrightness, brightness);
|
||||||
brightness = max(shine.a, brightness);
|
|
||||||
// In volumetric mode, distant glyphs are dimmer
|
// In volumetric mode, distant glyphs are dimmer
|
||||||
if (volumetric && !showDebugView) {
|
if (volumetric && !showDebugView) {
|
||||||
brightness = brightness * min(1., vDepth);
|
brightness = brightness * min(1., vDepth);
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
precision highp float;
|
precision highp float;
|
||||||
|
|
||||||
// This shader is the star of the show.
|
// This shader is the star of the show.
|
||||||
// It writes falling rain to four channels of a data texture:
|
// It writes falling rain to the channels of a data texture:
|
||||||
// R: brightness
|
// R: brightness
|
||||||
// G: unused
|
// G: unused
|
||||||
// B: whether the cell is a "cursor"
|
// B: whether the cell is a "cursor"
|
||||||
// A: some other effect, such as a ripple
|
// A: unused
|
||||||
|
|
||||||
// Listen.
|
// Listen.
|
||||||
// I understand if this shader looks confusing. Please don't be discouraged!
|
// I understand if this shader looks confusing. Please don't be discouraged!
|
||||||
@@ -21,12 +21,10 @@ uniform float numColumns, numRows;
|
|||||||
uniform float time, tick;
|
uniform float time, tick;
|
||||||
uniform float animationSpeed, fallSpeed;
|
uniform float animationSpeed, fallSpeed;
|
||||||
|
|
||||||
uniform bool hasSun, hasThunder, loops;
|
uniform bool loops;
|
||||||
uniform float brightnessDecay;
|
uniform float brightnessDecay;
|
||||||
uniform float baseContrast, baseBrightness;
|
uniform float baseContrast, baseBrightness;
|
||||||
uniform float raindropLength, glyphHeightToWidth;
|
uniform float raindropLength, glyphHeightToWidth;
|
||||||
uniform int rippleType;
|
|
||||||
uniform float rippleScale, rippleSpeed, rippleThickness;
|
|
||||||
|
|
||||||
// Helper functions for generating randomness, borrowed from elsewhere
|
// Helper functions for generating randomness, borrowed from elsewhere
|
||||||
|
|
||||||
@@ -66,88 +64,21 @@ float getBrightness(float rainTime) {
|
|||||||
return (1. - fract(rainTime)) * baseContrast + baseBrightness;
|
return (1. - fract(rainTime)) * baseContrast + baseBrightness;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Additional effects
|
|
||||||
|
|
||||||
float applySunShowerBrightness(float brightness, vec2 screenPos) {
|
|
||||||
if (brightness >= -4.) {
|
|
||||||
brightness = pow(fract(brightness * 0.5), 3.) * screenPos.y * 1.5;
|
|
||||||
}
|
|
||||||
return brightness;
|
|
||||||
}
|
|
||||||
|
|
||||||
float applyThunderBrightness(float brightness, float simTime, vec2 screenPos) {
|
|
||||||
simTime *= 0.5;
|
|
||||||
float thunder = 1. - fract(wobble(simTime));
|
|
||||||
if (loops) {
|
|
||||||
thunder = 1. - fract(simTime + 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
thunder = log(thunder * 1.5) * 4.;
|
|
||||||
thunder = clamp(thunder, 0., 1.);
|
|
||||||
thunder = thunder * pow(screenPos.y, 2.) * 3.;
|
|
||||||
return brightness + thunder;
|
|
||||||
}
|
|
||||||
|
|
||||||
float applyRippleEffect(float effect, float simTime, vec2 screenPos) {
|
|
||||||
if (rippleType == -1) {
|
|
||||||
return effect;
|
|
||||||
}
|
|
||||||
|
|
||||||
float rippleTime = (simTime * 0.5 + sin(simTime) * 0.2) * rippleSpeed + 1.; // TODO: clarify
|
|
||||||
if (loops) {
|
|
||||||
rippleTime = (simTime * 0.5) * rippleSpeed + 1.;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec2 offset = randomVec2(vec2(floor(rippleTime), 0.)) - 0.5;
|
|
||||||
if (loops) {
|
|
||||||
offset = vec2(0.);
|
|
||||||
}
|
|
||||||
vec2 ripplePos = screenPos * 2. - 1. + offset;
|
|
||||||
float rippleDistance;
|
|
||||||
if (rippleType == 0) {
|
|
||||||
vec2 boxDistance = abs(ripplePos) * vec2(1., glyphHeightToWidth);
|
|
||||||
rippleDistance = max(boxDistance.x, boxDistance.y);
|
|
||||||
} else if (rippleType == 1) {
|
|
||||||
rippleDistance = length(ripplePos);
|
|
||||||
}
|
|
||||||
|
|
||||||
float rippleValue = fract(rippleTime) * rippleScale - rippleDistance;
|
|
||||||
|
|
||||||
if (rippleValue > 0. && rippleValue < rippleThickness) {
|
|
||||||
effect += 0.75;
|
|
||||||
}
|
|
||||||
|
|
||||||
return effect;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main function
|
// Main function
|
||||||
|
|
||||||
vec4 computeResult(float simTime, bool isFirstFrame, vec2 glyphPos, vec2 screenPos, vec4 previous) {
|
vec4 computeResult(float simTime, bool isFirstFrame, vec2 glyphPos, vec2 screenPos, vec4 previous) {
|
||||||
|
|
||||||
// Determine the glyph's local time.
|
|
||||||
float rainTime = getRainTime(simTime, glyphPos);
|
float rainTime = getRainTime(simTime, glyphPos);
|
||||||
float rainTimeBelow = getRainTime(simTime, glyphPos + vec2(0., -1.));
|
float rainTimeBelow = getRainTime(simTime, glyphPos + vec2(0., -1.));
|
||||||
float cursor = fract(rainTime) < fract(rainTimeBelow) ? 1.0 : 0.0;
|
float cursor = fract(rainTime) < fract(rainTimeBelow) ? 1.0 : 0.0;
|
||||||
|
|
||||||
// Rain time is the backbone of this effect.
|
|
||||||
|
|
||||||
// Determine the glyph's brightness.
|
|
||||||
float brightness = getBrightness(rainTime);
|
float brightness = getBrightness(rainTime);
|
||||||
|
|
||||||
if (hasSun) brightness = applySunShowerBrightness(brightness, screenPos);
|
|
||||||
if (hasThunder) brightness = applyThunderBrightness(brightness, simTime, screenPos);
|
|
||||||
|
|
||||||
// Determine the glyph's effect— the amount the glyph lights up for other reasons
|
|
||||||
float effect = 0.;
|
|
||||||
effect = applyRippleEffect(effect, simTime, screenPos); // Round or square ripples across the grid
|
|
||||||
|
|
||||||
// Blend the glyph's brightness with its previous brightness, so it winks on and off organically
|
// Blend the glyph's brightness with its previous brightness, so it winks on and off organically
|
||||||
if (!isFirstFrame) {
|
if (!isFirstFrame) {
|
||||||
float previousBrightness = previous.r;
|
float previousBrightness = previous.r;
|
||||||
brightness = mix(previousBrightness, brightness, brightnessDecay);
|
brightness = mix(previousBrightness, brightness, brightnessDecay);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 result = vec4(brightness, fract(rainTime), cursor, effect);
|
vec4 result = vec4(brightness, fract(rainTime), cursor, 0.0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
precision highp float;
|
precision highp float;
|
||||||
|
|
||||||
// This shader governs the glyphs appearing in the rain.
|
// This shader governs the glyphs appearing in the rain.
|
||||||
// It writes each glyph's state to four channels of a data texture:
|
// It writes each glyph's state to the channels of a data texture:
|
||||||
// R: symbol
|
// R: symbol
|
||||||
// G: age
|
// G: age
|
||||||
// B: unused
|
// B: unused
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#define PI 3.14159265359
|
#define PI 3.14159265359
|
||||||
precision lowp float;
|
precision lowp float;
|
||||||
attribute vec2 aPosition, aCorner;
|
attribute vec2 aPosition, aCorner;
|
||||||
uniform sampler2D shineState, symbolState;
|
uniform sampler2D shineState, symbolState, effectState;
|
||||||
uniform float density;
|
uniform float density;
|
||||||
uniform vec2 quadSize;
|
uniform vec2 quadSize;
|
||||||
uniform float glyphHeightToWidth, glyphVerticalSpacing;
|
uniform float glyphHeightToWidth, glyphVerticalSpacing;
|
||||||
@@ -10,7 +10,7 @@ uniform vec2 screenSize;
|
|||||||
uniform float time, animationSpeed, forwardSpeed;
|
uniform float time, animationSpeed, forwardSpeed;
|
||||||
uniform bool volumetric;
|
uniform bool volumetric;
|
||||||
varying vec2 vUV;
|
varying vec2 vUV;
|
||||||
varying vec4 vShine, vSymbol;
|
varying vec4 vShine, vSymbol, vEffect;
|
||||||
varying float vDepth;
|
varying float vDepth;
|
||||||
|
|
||||||
highp float rand( const in vec2 uv ) {
|
highp float rand( const in vec2 uv ) {
|
||||||
@@ -24,6 +24,7 @@ void main() {
|
|||||||
vUV = (aPosition + aCorner) * quadSize;
|
vUV = (aPosition + aCorner) * quadSize;
|
||||||
vShine = texture2D(shineState, aPosition * quadSize);
|
vShine = texture2D(shineState, aPosition * quadSize);
|
||||||
vSymbol = texture2D(symbolState, aPosition * quadSize);
|
vSymbol = texture2D(symbolState, aPosition * quadSize);
|
||||||
|
vEffect = texture2D(effectState, aPosition * quadSize);
|
||||||
|
|
||||||
// Calculate the world space position
|
// Calculate the world space position
|
||||||
float quadDepth = 0.0;
|
float quadDepth = 0.0;
|
||||||
|
|||||||
Reference in New Issue
Block a user