Cursors are now much more robustly detected, and the debug view— previously called the computation texture— now resembles BUF's behind-the-scenes VFX footage. Isolated the isometric camera option from the debug view

This commit is contained in:
Rezmason
2022-09-08 23:12:58 -07:00
parent 77d6176fd5
commit ed49105c69
8 changed files with 55 additions and 43 deletions

View File

@@ -1,9 +1,13 @@
TODO: TODO:
Create a Resurrections font
Icomoon
Unicode PUA
Reformulate the basis Reformulate the basis
https://buf.com/films/the-matrix-resurrections https://buf.com/films/the-matrix-resurrections
Base cursors and other colors on BUF clip Base cursors and other colors on BUF clip
Show this stuff for showComputationTexture Show this stuff for showDebugView
Pixel grill? Pixel grill?
Tune the colors Tune the colors
Maybe glow can be an SDF-derived effect instead, look into it Maybe glow can be an SDF-derived effect instead, look into it
@@ -29,10 +33,6 @@ Playdate version
https://sdk.play.date/1.12.3/Inside%20Playdate.html#pdxinfo https://sdk.play.date/1.12.3/Inside%20Playdate.html#pdxinfo
Menu later? Menu later?
Create a Resurrections font
Icomoon
Unicode PUA
Resurrections Resurrections
Support anomaly streaks Support anomaly streaks
MSDF MSDF

View File

@@ -96,6 +96,7 @@ const defaults = {
resolution: 0.75, // An overall scale multiplier resolution: 0.75, // An overall scale multiplier
useHalfFloat: false, useHalfFloat: false,
renderer: "webgpu", // The preferred web graphics API renderer: "webgpu", // The preferred web graphics API
isometric: false,
useHoloplay: false, useHoloplay: false,
loops: false, loops: false,
}; };
@@ -308,6 +309,7 @@ const paramMapping = {
loops: { key: "loops", parser: (s) => s.toLowerCase().includes("true") }, loops: { key: "loops", parser: (s) => s.toLowerCase().includes("true") },
renderer: { key: "renderer", parser: (s) => s }, renderer: { key: "renderer", parser: (s) => s },
once: { key: "once", parser: (s) => s.toLowerCase().includes("true") }, once: { key: "once", parser: (s) => s.toLowerCase().includes("true") },
isometric: { key: "isometric", parser: (s) => s.toLowerCase().includes("true") },
}; };
paramMapping.dropLength = paramMapping.raindropLength; paramMapping.dropLength = paramMapping.raindropLength;
paramMapping.angle = paramMapping.slant; paramMapping.angle = paramMapping.slant;

View File

@@ -37,13 +37,13 @@ export default ({ regl, config, lkg }) => {
const cycleStyle = config.cycleStyleName in cycleStyles ? cycleStyles[config.cycleStyleName] : 0; const cycleStyle = config.cycleStyleName in cycleStyles ? cycleStyles[config.cycleStyleName] : 0;
const slantVec = [Math.cos(config.slant), Math.sin(config.slant)]; const slantVec = [Math.cos(config.slant), Math.sin(config.slant)];
const slantScale = 1 / (Math.abs(Math.sin(2 * config.slant)) * (Math.sqrt(2) - 1) + 1); const slantScale = 1 / (Math.abs(Math.sin(2 * config.slant)) * (Math.sqrt(2) - 1) + 1);
const showComputationTexture = config.effect === "none"; const showDebugView = config.effect === "none";
const commonUniforms = { const commonUniforms = {
...extractEntries(config, ["animationSpeed", "glyphHeightToWidth", "glyphSequenceLength", "glyphTextureGridSize"]), ...extractEntries(config, ["animationSpeed", "glyphHeightToWidth", "glyphSequenceLength", "glyphTextureGridSize"]),
numColumns, numColumns,
numRows, numRows,
showComputationTexture, showDebugView,
}; };
// These two framebuffers are used to compute the raining code. // These two framebuffers are used to compute the raining code.
@@ -182,7 +182,7 @@ export default ({ regl, config, lkg }) => {
const screenSize = [1, 1]; const screenSize = [1, 1];
const { mat4, vec3 } = glMatrix; const { mat4, vec3 } = glMatrix;
const transform = mat4.create(); const transform = mat4.create();
if (config.effect === "none") { if (volumetric && config.isometric) {
mat4.rotateX(transform, transform, (Math.PI * 1) / 8); mat4.rotateX(transform, transform, (Math.PI * 1) / 8);
mat4.rotateY(transform, transform, (Math.PI * 1) / 4); mat4.rotateY(transform, transform, (Math.PI * 1) / 4);
mat4.translate(transform, transform, vec3.fromValues(0, 0, -1)); mat4.translate(transform, transform, vec3.fromValues(0, 0, -1));
@@ -217,7 +217,7 @@ export default ({ regl, config, lkg }) => {
const index = column + row * numTileColumns; const index = column + row * numTileColumns;
const camera = mat4.create(); const camera = mat4.create();
if (config.effect === "none") { if (volumetric && config.isometric) {
if (aspectRatio > 1) { if (aspectRatio > 1) {
mat4.ortho(camera, -1.5 * aspectRatio, 1.5 * aspectRatio, -1.5, 1.5, -1000, 1000); mat4.ortho(camera, -1.5 * aspectRatio, 1.5 * aspectRatio, -1.5, 1.5, -1000, 1000);
} else { } else {

View File

@@ -18,7 +18,7 @@ const makeConfigBuffer = (device, configUniforms, config, density, gridSize) =>
...config, ...config,
gridSize, gridSize,
density, density,
showComputationTexture: config.effect === "none", showDebugView: config.effect === "none",
cycleStyle: config.cycleStyleName in cycleStyles ? cycleStyles[config.cycleStyleName] : 0, cycleStyle: config.cycleStyleName in cycleStyles ? cycleStyles[config.cycleStyleName] : 0,
rippleType: config.rippleTypeName in rippleTypes ? rippleTypes[config.rippleTypeName] : -1, rippleType: config.rippleTypeName in rippleTypes ? rippleTypes[config.rippleTypeName] : -1,
slantScale: 1 / (Math.abs(Math.sin(2 * config.slant)) * (Math.sqrt(2) - 1) + 1), slantScale: 1 / (Math.abs(Math.sin(2 * config.slant)) * (Math.sqrt(2) - 1) + 1),
@@ -45,7 +45,7 @@ export default ({ config, device, timeBuffer }) => {
const numQuads = config.volumetric ? numCells : 1; const numQuads = config.volumetric ? numCells : 1;
const transform = mat4.create(); const transform = mat4.create();
if (config.effect === "none") { if (config.volumetric && config.isometric) {
mat4.rotateX(transform, transform, (Math.PI * 1) / 8); mat4.rotateX(transform, transform, (Math.PI * 1) / 8);
mat4.rotateY(transform, transform, (Math.PI * 1) / 4); mat4.rotateY(transform, transform, (Math.PI * 1) / 4);
mat4.translate(transform, transform, vec3.fromValues(0, 0, -1)); mat4.translate(transform, transform, vec3.fromValues(0, 0, -1));
@@ -153,7 +153,7 @@ export default ({ config, device, timeBuffer }) => {
const build = (size) => { const build = (size) => {
// Update scene buffer: camera and transform math for the volumetric mode // Update scene buffer: camera and transform math for the volumetric mode
const aspectRatio = size[0] / size[1]; const aspectRatio = size[0] / size[1];
if (config.effect === "none") { if (config.volumetric && config.isometric) {
if (aspectRatio > 1) { if (aspectRatio > 1) {
mat4.orthoZO(camera, -1.5 * aspectRatio, 1.5 * aspectRatio, -1.5, 1.5, -1000, 1000); mat4.orthoZO(camera, -1.5 * aspectRatio, 1.5 * aspectRatio, -1.5, 1.5, -1000, 1000);
} else { } else {

View File

@@ -13,7 +13,7 @@ uniform vec2 glyphTextureGridSize;
uniform vec2 slantVec; uniform vec2 slantVec;
uniform float slantScale; uniform float slantScale;
uniform bool isPolar; uniform bool isPolar;
uniform bool showComputationTexture; uniform bool showDebugView;
uniform bool volumetric; uniform bool volumetric;
varying vec2 vUV; varying vec2 vUV;
@@ -76,7 +76,7 @@ void main() {
brightness = max(shine.b * cursorBrightness, brightness); brightness = max(shine.b * cursorBrightness, brightness);
brightness = max(shine.a, brightness); brightness = max(shine.a, brightness);
// In volumetric mode, distant glyphs are dimmer // In volumetric mode, distant glyphs are dimmer
if (volumetric) { if (volumetric && !showDebugView) {
brightness = brightness * min(1., vDepth); brightness = brightness * min(1., vDepth);
} }
@@ -92,12 +92,18 @@ void main() {
float sigDist = median3(dist) - 0.5; float sigDist = median3(dist) - 0.5;
float alpha = clamp(sigDist/fwidth(sigDist) + 0.5, 0., 1.); float alpha = clamp(sigDist/fwidth(sigDist) + 0.5, 0., 1.);
if (showComputationTexture) { if (showDebugView) {
vec4 debugColor = vec4(shine.r - alpha, shine.g * alpha, shine.a - alpha, 1.); brightness *= 2.;
if (volumetric) { gl_FragColor = vec4(
debugColor.g = debugColor.g * 0.9 + 0.1; vec3(
} shine.b,
gl_FragColor = debugColor; vec2(
brightness,
clamp(0., 1., pow(brightness * 0.9, 6.0))
) * (1.0 - shine.b)
) * alpha,
1.
);
} else { } else {
gl_FragColor = vec4(vChannel * brightness * alpha, 1.); gl_FragColor = vec4(vChannel * brightness * alpha, 1.);
} }

View File

@@ -55,11 +55,11 @@ float getRainTime(float simTime, vec2 glyphPos) {
columnSpeedOffset = 0.5; columnSpeedOffset = 0.5;
} }
float columnTime = columnTimeOffset + simTime * fallSpeed * columnSpeedOffset; float columnTime = columnTimeOffset + simTime * fallSpeed * columnSpeedOffset;
return (glyphPos.y * 0.01 + columnTime) / raindropLength; return wobble((glyphPos.y * 0.01 + columnTime) / raindropLength);
} }
float getBrightness(float rainTime) { float getBrightness(float rainTime) {
float value = 1. - fract(wobble(rainTime)); float value = 1. - fract(rainTime);
if (loops) { if (loops) {
value = 1. - fract(rainTime); value = 1. - fract(rainTime);
} }
@@ -122,10 +122,12 @@ float applyRippleEffect(float effect, float simTime, vec2 screenPos) {
// Main function // Main function
vec4 computeResult(float simTime, bool isFirstFrame, vec2 glyphPos, vec2 screenPos, vec4 previous, vec4 previousBelow) { vec4 computeResult(float simTime, bool isFirstFrame, vec2 glyphPos, vec2 screenPos, vec4 previous) {
// Determine the glyph's local time. // 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 cursor = fract(rainTime) < fract(rainTimeBelow) ? 1.0 : 0.0;
// Rain time is the backbone of this effect. // Rain time is the backbone of this effect.
@@ -139,9 +141,6 @@ vec4 computeResult(float simTime, bool isFirstFrame, vec2 glyphPos, vec2 screenP
float effect = 0.; float effect = 0.;
effect = applyRippleEffect(effect, simTime, screenPos); // Round or square ripples across the grid effect = applyRippleEffect(effect, simTime, screenPos); // Round or square ripples across the grid
float previousBrightnessBelow = previousBelow.r;
float cursor = brightness > previousBrightnessBelow ? 1.0 : 0.0;
// 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;
@@ -158,6 +157,5 @@ void main() {
vec2 glyphPos = gl_FragCoord.xy; vec2 glyphPos = gl_FragCoord.xy;
vec2 screenPos = glyphPos / vec2(numColumns, numRows); vec2 screenPos = glyphPos / vec2(numColumns, numRows);
vec4 previous = texture2D( previousShineState, screenPos ); vec4 previous = texture2D( previousShineState, screenPos );
vec4 previousBelow = texture2D( previousShineState, screenPos + vec2(0., -1. / numRows)); gl_FragColor = computeResult(simTime, isFirstFrame, glyphPos, screenPos, previous);
gl_FragColor = computeResult(simTime, isFirstFrame, glyphPos, screenPos, previous, previousBelow);
} }

View File

@@ -13,7 +13,7 @@ uniform sampler2D previousSymbolState, shineState;
uniform float numColumns, numRows; uniform float numColumns, numRows;
uniform float time, tick, cycleFrameSkip; uniform float time, tick, cycleFrameSkip;
uniform float animationSpeed, cycleSpeed; uniform float animationSpeed, cycleSpeed;
uniform bool loops, showComputationTexture; uniform bool loops, showDebugView;
uniform float glyphSequenceLength; uniform float glyphSequenceLength;
uniform int cycleStyle; uniform int cycleStyle;

View File

@@ -8,7 +8,7 @@ struct Config {
glyphTextureGridSize : vec2<i32>, glyphTextureGridSize : vec2<i32>,
glyphHeightToWidth : f32, glyphHeightToWidth : f32,
gridSize : vec2<f32>, gridSize : vec2<f32>,
showComputationTexture : i32, showDebugView : i32,
// compute-specific properties // compute-specific properties
brightnessThreshold : f32, brightnessThreshold : f32,
@@ -138,11 +138,11 @@ fn getRainTime(simTime : f32, glyphPos : vec2<f32>) -> f32 {
columnSpeedOffset = 0.5; columnSpeedOffset = 0.5;
} }
var columnTime = columnTimeOffset + simTime * config.fallSpeed * columnSpeedOffset; var columnTime = columnTimeOffset + simTime * config.fallSpeed * columnSpeedOffset;
return (glyphPos.y * 0.01 + columnTime) / config.raindropLength; return wobble((glyphPos.y * 0.01 + columnTime) / config.raindropLength);
} }
fn getBrightness(rainTime : f32) -> f32 { fn getBrightness(rainTime : f32) -> f32 {
var value = 1.0 - fract(wobble(rainTime)); var value = 1.0 - fract(rainTime);
if (bool(config.loops)) { if (bool(config.loops)) {
value = 1.0 - fract(rainTime); value = 1.0 - fract(rainTime);
} }
@@ -213,10 +213,12 @@ fn applyRippleEffect(effect : f32, simTime : f32, screenPos : vec2<f32>) -> f32
// Compute shader main functions // Compute shader main functions
fn computeShine (simTime : f32, isFirstFrame : bool, glyphPos : vec2<f32>, screenPos : vec2<f32>, previous : vec4<f32>, previousBelow : vec4<f32>) -> vec4<f32> { fn computeShine (simTime : f32, isFirstFrame : bool, glyphPos : vec2<f32>, screenPos : vec2<f32>, previous : vec4<f32>) -> vec4<f32> {
// Determine the glyph's local time. // Determine the glyph's local time.
var rainTime = getRainTime(simTime, glyphPos); var rainTime = getRainTime(simTime, glyphPos);
var rainTimeBelow = getRainTime(simTime, glyphPos + vec2<f32>(0., -1.));
var cursor = select(0.0, 1.0, fract(rainTime) < fract(rainTimeBelow));
// Rain time is the backbone of this effect. // Rain time is the backbone of this effect.
@@ -234,8 +236,6 @@ fn computeShine (simTime : f32, isFirstFrame : bool, glyphPos : vec2<f32>, scree
var effect = 0.0; var effect = 0.0;
effect = applyRippleEffect(effect, simTime, screenPos); // Round or square ripples across the grid effect = applyRippleEffect(effect, simTime, screenPos); // Round or square ripples across the grid
var previousBrightnessBelow = previousBelow.r;
var cursor = select(0.0, 1.0, brightness > previousBrightnessBelow);
// 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) {
@@ -290,17 +290,16 @@ fn computeSymbol (simTime : f32, isFirstFrame : bool, glyphPos : vec2<f32>, scre
} }
var i = row * i32(config.gridSize.x) + column; var i = row * i32(config.gridSize.x) + column;
var below = (row - 1) * i32(config.gridSize.x) + column;
var simTime = time.seconds * config.animationSpeed; var simTime = time.seconds * config.animationSpeed;
var isFirstFrame = time.frames == 0;
// Update the cell // Update the cell
var isFirstFrame = time.frames == 0;
var glyphPos = vec2<f32>(f32(column), f32(row)); var glyphPos = vec2<f32>(f32(column), f32(row));
var screenPos = glyphPos / config.gridSize; var screenPos = glyphPos / config.gridSize;
var cell = cells_RW.cells[i]; var cell = cells_RW.cells[i];
cell.shine = computeShine(simTime, isFirstFrame, glyphPos, screenPos, cell.shine, cells_RW.cells[below].shine); cell.shine = computeShine(simTime, isFirstFrame, glyphPos, screenPos, cell.shine);
cell.symbol = computeSymbol(simTime, isFirstFrame, glyphPos, screenPos, cell.symbol, cell.shine); cell.symbol = computeSymbol(simTime, isFirstFrame, glyphPos, screenPos, cell.symbol, cell.shine);
cells_RW.cells[i] = cell; cells_RW.cells[i] = cell;
} }
@@ -442,11 +441,18 @@ fn getSymbolUV(symbol : i32) -> vec2<f32> {
var output : FragOutput; var output : FragOutput;
if (bool(config.showComputationTexture)) { if (bool(config.showDebugView)) {
output.color = vec4<f32>(cell.shine.r - alpha, cell.shine.g * alpha, cell.shine.a - alpha, 1.0); brightness *= 2.0;
if (volumetric) { output.color = vec4<f32>(
output.color.g *= 0.9 + 0.1; vec3<f32>(
} cell.shine.b,
vec2<f32>(
brightness,
clamp(0.0, 1.0, pow(brightness * 0.9, 6.0))
) * (1.0 - cell.shine.b)
) * alpha,
1.0
);
} else { } else {
output.color = vec4<f32>(input.channel * brightness * alpha, 1.0); output.color = vec4<f32>(input.channel * brightness * alpha, 1.0);
} }