mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-14 12:29:30 -07:00
Updating the WebGPU code to match the REGL code
This commit is contained in:
3
TODO.txt
3
TODO.txt
@@ -1,8 +1,5 @@
|
||||
TODO:
|
||||
|
||||
Update WebGPU
|
||||
Compute shine and symbol in separate methods of compute shader
|
||||
|
||||
Reformulate the basis
|
||||
https://buf.com/films/the-matrix-resurrections
|
||||
Base cursors and other colors on BUF clip
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { structs, byteSizeOf } from "../../lib/gpu-buffer.js";
|
||||
import { structs } from "../../lib/gpu-buffer.js";
|
||||
import { makeRenderTarget, loadTexture, loadShader, makeUniformBuffer, makeBindGroup, makePass } from "./utils.js";
|
||||
|
||||
const rippleTypes = {
|
||||
@@ -44,11 +44,6 @@ export default ({ config, device, timeBuffer }) => {
|
||||
// rather than a single quad for our geometry
|
||||
const numQuads = config.volumetric ? numCells : 1;
|
||||
|
||||
const cellsBuffer = device.createBuffer({
|
||||
size: numCells * byteSizeOf("vec4<f32>"),
|
||||
usage: GPUBufferUsage.STORAGE,
|
||||
});
|
||||
|
||||
const transform = mat4.create();
|
||||
if (config.effect === "none") {
|
||||
mat4.rotateX(transform, transform, (Math.PI * 1) / 8);
|
||||
@@ -60,8 +55,9 @@ export default ({ config, device, timeBuffer }) => {
|
||||
}
|
||||
const camera = mat4.create();
|
||||
|
||||
// It's handy to have multiple channels, in case we have
|
||||
// multiple varieties of code, such as downward and upward flowing
|
||||
// TODO: vantage points, multiple renders
|
||||
|
||||
// We use the different channels for different parts of the raindrop
|
||||
const renderFormat = "rgba8unorm";
|
||||
|
||||
const linearSampler = device.createSampler({
|
||||
@@ -100,6 +96,11 @@ export default ({ config, device, timeBuffer }) => {
|
||||
const rainShaderUniforms = structs.from(rainShader.code);
|
||||
configBuffer = makeConfigBuffer(device, rainShaderUniforms.Config, config, density, gridSize);
|
||||
|
||||
const cellsBuffer = device.createBuffer({
|
||||
size: numCells * rainShaderUniforms.Cell.minSize,
|
||||
usage: GPUBufferUsage.STORAGE,
|
||||
});
|
||||
|
||||
sceneUniforms = rainShaderUniforms.Scene;
|
||||
sceneBuffer = makeUniformBuffer(device, sceneUniforms);
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ vec4 computeResult(float simTime, bool isFirstFrame, vec2 glyphPos, vec2 screenP
|
||||
resetGlyph = resetGlyph || brightness <= 0.;
|
||||
}
|
||||
if (resetGlyph) {
|
||||
previousAge = randomFloat(screenPos + vec2(0.5));
|
||||
previousAge = randomFloat(screenPos + 0.5);
|
||||
previousSymbol = floor(glyphSequenceLength * randomFloat(screenPos));
|
||||
}
|
||||
float cycleSpeed = getCycleSpeed(brightness);
|
||||
@@ -61,7 +61,7 @@ vec4 computeResult(float simTime, bool isFirstFrame, vec2 glyphPos, vec2 screenP
|
||||
if (cycleStyle == 0) {
|
||||
symbol = mod(symbol + advance, glyphSequenceLength);
|
||||
} else if (cycleStyle == 1 && advance > 0.) {
|
||||
symbol = floor(glyphSequenceLength * randomFloat(screenPos + vec2(simTime)));
|
||||
symbol = floor(glyphSequenceLength * randomFloat(screenPos + simTime));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
struct Config {
|
||||
// common properties used for compute and rendering
|
||||
animationSpeed : f32,
|
||||
glyphSequenceLength : i32,
|
||||
glyphSequenceLength : f32,
|
||||
glyphTextureGridSize : vec2<i32>,
|
||||
glyphHeightToWidth : f32,
|
||||
gridSize : vec2<f32>,
|
||||
@@ -55,9 +55,14 @@ struct Scene {
|
||||
transform : mat4x4<f32>,
|
||||
};
|
||||
|
||||
struct Cell {
|
||||
shine : vec4<f32>,
|
||||
symbol : vec4<f32>,
|
||||
};
|
||||
|
||||
// The array of cells that the compute shader updates, and the fragment shader draws.
|
||||
struct CellData {
|
||||
cells: array<vec4<f32>>,
|
||||
cells: array<Cell>,
|
||||
};
|
||||
|
||||
// Shared bindings
|
||||
@@ -87,7 +92,7 @@ struct VertOutput {
|
||||
@builtin(position) Position : vec4<f32>,
|
||||
@location(0) uv : vec2<f32>,
|
||||
@location(1) channel : vec3<f32>,
|
||||
@location(2) glyph : vec4<f32>,
|
||||
@location(2) quadDepth : f32,
|
||||
};
|
||||
|
||||
struct FragOutput {
|
||||
@@ -144,12 +149,10 @@ fn getBrightness(rainTime : f32) -> f32 {
|
||||
return value * config.baseContrast + config.baseBrightness;
|
||||
}
|
||||
|
||||
fn getCycleSpeed(rainTime : f32, brightness : f32) -> f32 {
|
||||
var localCycleSpeed = 0.0;
|
||||
fn getCycleSpeed(brightness : f32) -> f32 {
|
||||
var localCycleSpeed = 1.0;
|
||||
if (config.cycleStyle == 0 && brightness > 0.0) {
|
||||
localCycleSpeed = pow(1.0 - brightness, 4.0);
|
||||
} else if (config.cycleStyle == 1) {
|
||||
localCycleSpeed = fract(rainTime);
|
||||
}
|
||||
return config.animationSpeed * config.cycleSpeed * localCycleSpeed;
|
||||
}
|
||||
@@ -208,26 +211,18 @@ fn applyRippleEffect(effect : f32, simTime : f32, screenPos : vec2<f32>) -> f32
|
||||
return effect;
|
||||
}
|
||||
|
||||
fn applyCursorEffect(effect : f32, brightness : f32) -> f32 {
|
||||
if (brightness >= config.cursorBrightness) {
|
||||
return 1.0;
|
||||
}
|
||||
return effect;
|
||||
}
|
||||
|
||||
// Compute shader main functions
|
||||
|
||||
fn computeResult (isFirstFrame : bool, previousResult : vec4<f32>, glyphPos : vec2<f32>, screenPos : vec2<f32>) -> vec4<f32> {
|
||||
fn computeShine (simTime : f32, isFirstFrame : bool, glyphPos : vec2<f32>, screenPos : vec2<f32>, previous : vec4<f32>, previousBelow : vec4<f32>) -> vec4<f32> {
|
||||
|
||||
// Determine the glyph's local time.
|
||||
var simTime = time.seconds * config.animationSpeed;
|
||||
var rainTime = getRainTime(simTime, glyphPos);
|
||||
|
||||
// Rain time is the backbone of this effect.
|
||||
|
||||
// Determine the glyph's brightness.
|
||||
var previousBrightness = previousResult.r;
|
||||
var brightness = getBrightness(rainTime);
|
||||
|
||||
if (bool(config.hasSun)) {
|
||||
brightness = applySunShowerBrightness(brightness, screenPos);
|
||||
}
|
||||
@@ -235,46 +230,52 @@ fn computeResult (isFirstFrame : bool, previousResult : vec4<f32>, glyphPos : ve
|
||||
brightness = applyThunderBrightness(brightness, simTime, screenPos);
|
||||
}
|
||||
|
||||
// Determine the glyph's cycle— the percent this glyph has progressed through the glyph sequence
|
||||
var previousCycle = previousResult.g;
|
||||
var resetGlyph = isFirstFrame;
|
||||
if (bool(config.loops)) {
|
||||
resetGlyph = resetGlyph || previousBrightness < 0.0;
|
||||
}
|
||||
if (resetGlyph) {
|
||||
previousCycle = select(randomFloat(screenPos), 0.0, bool(config.showComputationTexture));
|
||||
}
|
||||
var localCycleSpeed = getCycleSpeed(rainTime, brightness);
|
||||
var cycle = previousCycle;
|
||||
if (time.frames % config.cycleFrameSkip == 0) {
|
||||
cycle = fract(previousCycle + 0.005 * localCycleSpeed * f32(config.cycleFrameSkip));
|
||||
}
|
||||
|
||||
// Determine the glyph's effect— the amount the glyph lights up for other reasons
|
||||
var effect = 0.0;
|
||||
effect = applyRippleEffect(effect, simTime, screenPos); // Round or square ripples across the grid
|
||||
effect = applyCursorEffect(effect, brightness); // The bright glyphs at the "bottom" of raindrops
|
||||
|
||||
// Modes that don't fade glyphs set their actual brightness here
|
||||
if (config.brightnessOverride > 0.0 && brightness > config.brightnessThreshold) {
|
||||
brightness = config.brightnessOverride;
|
||||
}
|
||||
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
|
||||
if (!isFirstFrame) {
|
||||
var previousBrightness = previous.r;
|
||||
brightness = mix(previousBrightness, brightness, config.brightnessDecay);
|
||||
}
|
||||
|
||||
// Determine the glyph depth. This is a static value for each column.
|
||||
var depth = randomFloat(vec2<f32>(screenPos.x, 0.0));
|
||||
|
||||
var result = vec4<f32>(brightness, cycle, depth, effect);
|
||||
|
||||
// Better use of the alpha channel, for demonstrating how the glyph cycle works
|
||||
if (bool(config.showComputationTexture)) {
|
||||
result.a = min(1.0, localCycleSpeed);
|
||||
var result = vec4<f32>(brightness, 0.0, cursor, effect);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn computeSymbol (simTime : f32, isFirstFrame : bool, glyphPos : vec2<f32>, screenPos : vec2<f32>, previous : vec4<f32>, shine : vec4<f32>) -> vec4<f32> {
|
||||
|
||||
var brightness = shine.r;
|
||||
|
||||
var previousSymbol = previous.r;
|
||||
var previousAge = previous.g;
|
||||
var resetGlyph = isFirstFrame;
|
||||
if (bool(config.loops)) {
|
||||
resetGlyph = resetGlyph || brightness < 0.0;
|
||||
}
|
||||
if (resetGlyph) {
|
||||
previousAge = randomFloat(screenPos + 0.5);
|
||||
previousSymbol = floor(config.glyphSequenceLength * randomFloat(screenPos));
|
||||
}
|
||||
var cycleSpeed = getCycleSpeed(brightness);
|
||||
var age = previousAge;
|
||||
var symbol = previousSymbol;
|
||||
if (time.frames % config.cycleFrameSkip == 0) {
|
||||
age += cycleSpeed * f32(config.cycleFrameSkip);
|
||||
var advance = floor(age);
|
||||
age = fract(age);
|
||||
if (config.cycleStyle == 0) {
|
||||
symbol = (symbol + advance) % config.glyphSequenceLength;
|
||||
} else if (config.cycleStyle == 1 && advance > 0.) {
|
||||
symbol = floor(config.glyphSequenceLength * randomFloat(screenPos + simTime));
|
||||
}
|
||||
}
|
||||
|
||||
var result = vec4<f32>(symbol, age, 0.0, 0.0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -289,13 +290,19 @@ fn computeResult (isFirstFrame : bool, previousResult : vec4<f32>, glyphPos : ve
|
||||
}
|
||||
|
||||
var i = row * i32(config.gridSize.x) + column;
|
||||
var below = (row - 1) * i32(config.gridSize.x) + column;
|
||||
|
||||
var simTime = time.seconds * config.animationSpeed;
|
||||
|
||||
// Update the cell
|
||||
var isFirstFrame = time.frames == 0;
|
||||
var glyphPos = vec2<f32>(f32(column), f32(row));
|
||||
var screenPos = glyphPos / config.gridSize;
|
||||
var previousResult = cells_RW.cells[i];
|
||||
cells_RW.cells[i] = computeResult(isFirstFrame, previousResult, glyphPos, screenPos);
|
||||
|
||||
var cell = cells_RW.cells[i];
|
||||
cell.shine = computeShine(simTime, isFirstFrame, glyphPos, screenPos, cell.shine, cells_RW.cells[below].shine);
|
||||
cell.symbol = computeSymbol(simTime, isFirstFrame, glyphPos, screenPos, cell.symbol, cell.shine);
|
||||
cells_RW.cells[i] = cell;
|
||||
}
|
||||
|
||||
// Vertex shader
|
||||
@@ -327,14 +334,11 @@ fn computeResult (isFirstFrame : bool, previousResult : vec4<f32>, glyphPos : ve
|
||||
// Calculate the vertex's uv
|
||||
var uv = (quadPosition + quadCorner) / quadGridSize;
|
||||
|
||||
// Retrieve the quad's glyph data
|
||||
var glyph = cells_RO.cells[quadIndex];
|
||||
|
||||
// Calculate the quad's depth
|
||||
// Determine the quad's depth. This is a static value for each column of quads.
|
||||
var quadDepth = 0.0;
|
||||
if (volumetric) {
|
||||
quadDepth = fract(glyph.b + time.seconds * config.animationSpeed * config.forwardSpeed);
|
||||
glyph.b = quadDepth;
|
||||
var startDepth = randomFloat(vec2(quadPosition.x, 0.0));
|
||||
quadDepth = fract(startDepth + time.seconds * config.animationSpeed * config.forwardSpeed);
|
||||
}
|
||||
|
||||
// Calculate the vertex's world space position
|
||||
@@ -358,7 +362,7 @@ fn computeResult (isFirstFrame : bool, previousResult : vec4<f32>, glyphPos : ve
|
||||
screenPosition,
|
||||
uv,
|
||||
channel,
|
||||
glyph
|
||||
quadDepth
|
||||
);
|
||||
}
|
||||
|
||||
@@ -368,11 +372,9 @@ fn median3(i : vec3<f32>) -> f32 {
|
||||
return max(min(i.r, i.g), min(max(i.r, i.g), i.b));
|
||||
}
|
||||
|
||||
fn getSymbolUV(glyphCycle : f32) -> vec2<f32> {
|
||||
var symbol = i32(f32(config.glyphSequenceLength) * glyphCycle);
|
||||
fn getSymbolUV(symbol : i32) -> vec2<f32> {
|
||||
var symbolX = symbol % config.glyphTextureGridSize.x;
|
||||
var symbolY = symbol / config.glyphTextureGridSize.x;
|
||||
// TODO: make sure this is working properly, it had a bug in the GLSL for a while.
|
||||
return vec2<f32>(f32(symbolX), f32(symbolY));
|
||||
}
|
||||
|
||||
@@ -404,24 +406,25 @@ fn getSymbolUV(glyphCycle : f32) -> vec2<f32> {
|
||||
uv.y /= config.glyphHeightToWidth;
|
||||
}
|
||||
|
||||
// Retrieve values from the data texture
|
||||
var glyph : vec4<f32>;
|
||||
if (volumetric) {
|
||||
glyph = input.glyph;
|
||||
} else {
|
||||
// Retrieve cell
|
||||
var gridCoord : vec2<i32> = vec2<i32>(uv * config.gridSize);
|
||||
var gridIndex = gridCoord.y * i32(config.gridSize.x) + gridCoord.x;
|
||||
glyph = cells_RO.cells[gridIndex];
|
||||
}
|
||||
var brightness = glyph.r;
|
||||
var symbolUV = getSymbolUV(glyph.g);
|
||||
var quadDepth = glyph.b;
|
||||
var effect = glyph.a;
|
||||
var cell = cells_RO.cells[gridIndex];
|
||||
var symbolUV = getSymbolUV(i32(cell.symbol.r));
|
||||
|
||||
var brightness = cell.shine.r;
|
||||
|
||||
// Modes that don't fade glyphs set their actual brightness here
|
||||
if (config.brightnessOverride > 0.0 && brightness > config.brightnessThreshold) {
|
||||
brightness = config.brightnessOverride;
|
||||
}
|
||||
|
||||
brightness = max(cell.shine.b * config.cursorBrightness, brightness);
|
||||
brightness = max(cell.shine.a, brightness);
|
||||
|
||||
brightness = max(effect, brightness);
|
||||
// In volumetric mode, distant glyphs are dimmer
|
||||
if (volumetric) {
|
||||
brightness *= min(1.0, quadDepth);
|
||||
brightness *= min(1.0, input.quadDepth);
|
||||
}
|
||||
|
||||
// resolve UV to cropped position of glyph in MSDF texture
|
||||
@@ -440,7 +443,7 @@ fn getSymbolUV(glyphCycle : f32) -> vec2<f32> {
|
||||
var output : FragOutput;
|
||||
|
||||
if (bool(config.showComputationTexture)) {
|
||||
output.color = vec4<f32>(glyph.r - alpha, glyph.g * alpha, glyph.a - alpha, 1.0);
|
||||
output.color = vec4<f32>(cell.shine.r - alpha, cell.shine.g * alpha, cell.shine.a - alpha, 1.0);
|
||||
if (volumetric) {
|
||||
output.color.g *= 0.9 + 0.1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user