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