From 38f29ad21bd86aed18605363677971ba7ba84aed Mon Sep 17 00:00:00 2001 From: Rezmason Date: Tue, 21 Dec 2021 02:22:20 -0800 Subject: [PATCH] You can now loop the effect. It's not super great, but it ought to make it easier to create repeating backgrounds or something. --- README.md | 2 +- js/config.js | 2 ++ js/regl/rainPass.js | 1 + shaders/glsl/rainPass.compute.frag.glsl | 27 ++++++++++++++++++------- shaders/wgsl/rainPass.wgsl | 26 ++++++++++++++++++------ 5 files changed, 44 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 6025891..a5d023f 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ Now you know link fu. Here's a list of customization options: - ("none" displays the texture whose RGBA values represent the glyph shape and brightness data. _epilepsy warning_: lots of flickering) - **colors** - if you set the effect to "customStripes", you can specify the colors of vertical stripes as alternating *R,G,B* numeric values, like so: [https://rezmason.github.io/matrix/?effect=customStripes&colors=1,0,0,1,1,0,0,1,0](https://rezmason.github.io/matrix/?effect=customStripes&colors=1,0,0,1,1,0,0,1,0) - **url** - if you set the effect to "image", this is how you specify which image to load. It doesn't work with any URL; I suggest grabbing them from Wikipedia: [https://rezmason.github.io/matrix/?effect=image&url=https://upload.wikimedia.org/wikipedia/commons/f/f5/EagleRock.jpg](https://rezmason.github.io/matrix/?effect=image&url=https://upload.wikimedia.org/wikipedia/commons/f/f5/EagleRock.jpg) - +- **loops** - (WIP) if set to "true", this causes the effect to loop, so that it can be converted into a looping video. --- ### other details diff --git a/js/config.js b/js/config.js index fcc896c..18cf96b 100644 --- a/js/config.js +++ b/js/config.js @@ -77,6 +77,7 @@ const defaults = { useHalfFloat: false, renderer: "webgpu", // The preferred web graphics API useHoloplay: false, + loops: false, }; const versions = { @@ -274,6 +275,7 @@ const paramMapping = { stripeColors: { key: "stripeColors", parser: (s) => s }, backgroundColor: { key: "backgroundColor", parser: (s) => s.split(",").map(parseFloat) }, volumetric: { key: "volumetric", parser: (s) => s.toLowerCase().includes("true") }, + loops: { key: "loops", parser: (s) => s.toLowerCase().includes("true") }, renderer: { key: "renderer", parser: (s) => s }, }; paramMapping.dropLength = paramMapping.raindropLength; diff --git a/js/regl/rainPass.js b/js/regl/rainPass.js index 9dd2f74..6825c5f 100644 --- a/js/regl/rainPass.js +++ b/js/regl/rainPass.js @@ -76,6 +76,7 @@ export default ({ regl, config, lkg }) => { "rippleScale", "rippleSpeed", "rippleThickness", + "loops", ]), cycleStyle, rippleType, diff --git a/shaders/glsl/rainPass.compute.frag.glsl b/shaders/glsl/rainPass.compute.frag.glsl index 405905f..13f8f93 100644 --- a/shaders/glsl/rainPass.compute.frag.glsl +++ b/shaders/glsl/rainPass.compute.frag.glsl @@ -20,7 +20,7 @@ uniform float numColumns, numRows; uniform float time, tick, cycleFrameSkip; uniform float animationSpeed, fallSpeed, cycleSpeed; -uniform bool hasSun, hasThunder; +uniform bool hasSun, hasThunder, loops; uniform bool showComputationTexture; uniform float brightnessOverride, brightnessThreshold, brightnessDecay; uniform float raindropLength, glyphHeightToWidth; @@ -51,14 +51,18 @@ float wobble(float x) { float getRainTime(float simTime, vec2 glyphPos) { float columnTimeOffset = randomFloat(vec2(glyphPos.x, 0.)) * 1000.; float columnSpeedOffset = randomFloat(vec2(glyphPos.x + 0.1, 0.)) * 0.5 + 0.5; - // columnSpeedOffset = 0.; // TODO: loop + if (loops) { + columnSpeedOffset = 0.5; + } float columnTime = columnTimeOffset + simTime * fallSpeed * columnSpeedOffset; return (glyphPos.y * 0.01 + columnTime) / raindropLength; } float getBrightness(float rainTime) { float value = 1. - fract(wobble(rainTime)); - // value = 1. - fract(rainTime); // TODO: loop + if (loops) { + value = 1. - fract(rainTime); + } return log(value * 1.25) * 3.; } @@ -84,7 +88,9 @@ float applySunShowerBrightness(float brightness, vec2 screenPos) { float applyThunderBrightness(float brightness, float simTime, vec2 screenPos) { simTime *= 0.5; float thunder = 1. - fract(wobble(simTime)); - // thunder = 1. - fract(simTime + 0.3); // TODO: loop + if (loops) { + thunder = 1. - fract(simTime + 0.3); + } thunder = log(thunder * 1.5) * 4.; thunder = clamp(thunder, 0., 1.); @@ -98,10 +104,14 @@ float applyRippleEffect(float effect, float simTime, vec2 screenPos) { } float rippleTime = (simTime * 0.5 + sin(simTime) * 0.2) * rippleSpeed + 1.; // TODO: clarify - // rippleTime = (simTime * 0.5) * rippleSpeed + 1.; // TODO: loop + if (loops) { + rippleTime = (simTime * 0.5) * rippleSpeed + 1.; + } vec2 offset = randomVec2(vec2(floor(rippleTime), 0.)) - 0.5; - // offset = vec2(0.); // TODO: loop + if (loops) { + offset = vec2(0.); + } vec2 ripplePos = screenPos * 2. - 1. + offset; float rippleDistance; if (rippleType == 0) { @@ -149,7 +159,10 @@ vec4 computeResult(bool isFirstFrame, vec4 previousResult, vec2 glyphPos, vec2 s // Determine the glyph's cycle— the percent this glyph has progressed through the glyph sequence float previousCycle = previousResult.g; - bool resetGlyph = isFirstFrame; // || previousBrightness <= 0.; // TODO: loop + bool resetGlyph = isFirstFrame; + if (loops) { + resetGlyph = resetGlyph || previousBrightness <= 0.; + } if (resetGlyph) { previousCycle = showComputationTexture ? 0. : randomFloat(screenPos); } diff --git a/shaders/wgsl/rainPass.wgsl b/shaders/wgsl/rainPass.wgsl index 8538d4a..eeb5542 100644 --- a/shaders/wgsl/rainPass.wgsl +++ b/shaders/wgsl/rainPass.wgsl @@ -37,6 +37,7 @@ struct Config { slantScale : f32; slantVec : vec2; volumetric : i32; + loops : i32; highPassThreshold : f32; }; @@ -127,14 +128,18 @@ fn wobble(x : f32) -> f32 { fn getRainTime(simTime : f32, glyphPos : vec2) -> f32 { var columnTimeOffset = randomFloat(vec2(glyphPos.x, 0.0)) * 1000.0; var columnSpeedOffset = randomFloat(vec2(glyphPos.x + 0.1, 0.0)) * 0.5 + 0.5; - // columnSpeedOffset = 0.0; // TODO: loop + if (bool(config.loops)) { + columnSpeedOffset = 0.5; + } var columnTime = columnTimeOffset + simTime * config.fallSpeed * columnSpeedOffset; return (glyphPos.y * 0.01 + columnTime) / config.raindropLength; } fn getBrightness(rainTime : f32) -> f32 { var value = 1.0 - fract(wobble(rainTime)); - // value = 1.0 - fract(rainTime); // TODO: loop + if (bool(config.loops)) { + value = 1.0 - fract(rainTime); + } return log(value * 1.25) * 3.0; } @@ -160,7 +165,9 @@ fn applySunShowerBrightness(brightness : f32, screenPos : vec2) -> f32 { fn applyThunderBrightness(brightness : f32, simTime : f32, screenPos : vec2) -> f32 { var thunderTime = simTime * 0.5; var thunder = 1.0 - fract(wobble(thunderTime)); - // thunder = 1.0 - fract(thunderTime + 0.3); // TODO: loop + if (bool(config.loops)) { + thunder = 1.0 - fract(thunderTime + 0.3); + } thunder = log(thunder * 1.5) * 4.0; thunder = clamp(thunder, 0.0, 1.0); @@ -174,10 +181,14 @@ fn applyRippleEffect(effect : f32, simTime : f32, screenPos : vec2) -> f32 } var rippleTime = (simTime * 0.5 + sin(simTime) * 0.2) * config.rippleSpeed + 1.0; // TODO: clarify - // rippleTime = (simTime * 0.5) * config.rippleSpeed + 1.0; // TODO: loop + if (bool(config.loops)) { + rippleTime = (simTime * 0.5) * config.rippleSpeed + 1.0; + } var offset = randomVec2(vec2(floor(rippleTime), 0.0)) - 0.5; - // offset = vec2(0.0); // TODO: loop + if (bool(config.loops)) { + offset = vec2(0.0); + } var ripplePos = screenPos * 2.0 - 1.0 + offset; var rippleDistance : f32; if (config.rippleType == 0) { @@ -225,7 +236,10 @@ fn computeResult (isFirstFrame : bool, previousResult : vec4, glyphPos : ve // Determine the glyph's cycle— the percent this glyph has progressed through the glyph sequence var previousCycle = previousResult.g; - var resetGlyph = isFirstFrame; // || previousBrightness <= 0.0; // TODO: loop + var resetGlyph = isFirstFrame; + if (bool(config.loops)) { + resetGlyph = resetGlyph || previousBrightness < 0.0; + } if (resetGlyph) { previousCycle = select(randomFloat(screenPos), 0.0, bool(config.showComputationTexture)); }