mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-14 12:29:30 -07:00
Adding some more comments. Destructuring the context object in the pass modules. A little code cleanup in bloomPass. Changing the endPass sampler to be cheaper.
This commit is contained in:
1
TODO.txt
1
TODO.txt
@@ -1,7 +1,6 @@
|
|||||||
TODO:
|
TODO:
|
||||||
|
|
||||||
WebGPU
|
WebGPU
|
||||||
Make sure everything is properly commented
|
|
||||||
Update links in issues
|
Update links in issues
|
||||||
Get rid of end pass once it's possible to copy a bgra8unorm to a canvas texture
|
Get rid of end pass once it's possible to copy a bgra8unorm to a canvas texture
|
||||||
Switch to rgba32float somehow?
|
Switch to rgba32float somehow?
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { loadText, makePassFBO, makePass } from "./utils.js";
|
import { loadText, makePassFBO, makePass } from "./utils.js";
|
||||||
|
|
||||||
// The bloom pass is basically an added high-pass blur.
|
// The bloom pass is basically an added high-pass blur.
|
||||||
// The blur approximation is the sum of a pyramid of downscaled textures.
|
// The blur approximation is the sum of a pyramid of downscaled, blurred textures.
|
||||||
|
|
||||||
const pyramidHeight = 5;
|
const pyramidHeight = 5;
|
||||||
const levelStrengths = Array(pyramidHeight)
|
const levelStrengths = Array(pyramidHeight)
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
import { structs } from "/lib/gpu-buffer.js";
|
import { structs } from "/lib/gpu-buffer.js";
|
||||||
import { makeComputeTarget, makePyramidView, loadShader, makeUniformBuffer, makeBindGroup, makePass } from "./utils.js";
|
import { makeComputeTarget, makePyramidView, loadShader, makeUniformBuffer, makeBindGroup, makePass } from "./utils.js";
|
||||||
|
|
||||||
export default (context) => {
|
// The bloom pass is basically an added blur of the rain pass's high-pass output.
|
||||||
const { config, device } = context;
|
// The blur approximation is the sum of a pyramid of downscaled, blurred textures.
|
||||||
|
|
||||||
|
export default ({ config, device }) => {
|
||||||
const pyramidHeight = 4;
|
const pyramidHeight = 4;
|
||||||
const bloomSize = config.bloomSize;
|
const bloomSize = config.bloomSize;
|
||||||
const bloomStrength = config.bloomStrength;
|
const bloomStrength = config.bloomStrength;
|
||||||
const bloomRadius = 2; // Looks better with more, but is more costly
|
const bloomRadius = 2; // Looks better with more, but is more costly
|
||||||
|
|
||||||
const enabled = true;
|
const enabled = bloomSize > 0 && bloomStrength > 0;
|
||||||
|
|
||||||
// If there's no bloom to apply, return a no-op pass with an empty bloom texture
|
// If there's no bloom to apply, return a no-op pass with an empty bloom texture
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
const emptyTexture = makeComputeTarget(device, 1, 1);
|
const emptyTexture = makeComputeTarget(device, [1, 1]);
|
||||||
return makePass(null, (size, inputs) => ({ ...inputs, bloom: emptyTexture }));
|
return makePass(null, (size, inputs) => ({ ...inputs, bloom: emptyTexture }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,6 +25,8 @@ export default (context) => {
|
|||||||
minFilter: "linear",
|
minFilter: "linear",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// The blur pipeline applies a blur in one direction; it's applied horizontally
|
||||||
|
// to the first image pyramid, and then vertically to the second image pyramid.
|
||||||
let blurPipeline;
|
let blurPipeline;
|
||||||
let hBlurPyramid;
|
let hBlurPyramid;
|
||||||
let vBlurPyramid;
|
let vBlurPyramid;
|
||||||
@@ -31,6 +34,8 @@ export default (context) => {
|
|||||||
let vBlurBuffer;
|
let vBlurBuffer;
|
||||||
let hBlurBindGroups;
|
let hBlurBindGroups;
|
||||||
let vBlurBindGroups;
|
let vBlurBindGroups;
|
||||||
|
|
||||||
|
// The combine pipeline blends the last image pyramid's layers into the output.
|
||||||
let combinePipeline;
|
let combinePipeline;
|
||||||
let combineBuffer;
|
let combineBuffer;
|
||||||
let combineBindGroup;
|
let combineBindGroup;
|
||||||
@@ -63,6 +68,8 @@ export default (context) => {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
const build = (screenSize, inputs) => {
|
const build = (screenSize, inputs) => {
|
||||||
|
|
||||||
|
// Since the bloom is blurry, we downscale everything
|
||||||
scaledScreenSize = screenSize.map((x) => Math.floor(x * bloomSize));
|
scaledScreenSize = screenSize.map((x) => Math.floor(x * bloomSize));
|
||||||
|
|
||||||
hBlurPyramid?.destroy();
|
hBlurPyramid?.destroy();
|
||||||
@@ -74,17 +81,18 @@ export default (context) => {
|
|||||||
output?.destroy();
|
output?.destroy();
|
||||||
output = makeComputeTarget(device, scaledScreenSize);
|
output = makeComputeTarget(device, scaledScreenSize);
|
||||||
|
|
||||||
const hBlurPyramidViews = [];
|
|
||||||
const vBlurPyramidViews = [];
|
|
||||||
hBlurBindGroups = [];
|
hBlurBindGroups = [];
|
||||||
vBlurBindGroups = [];
|
vBlurBindGroups = [];
|
||||||
|
|
||||||
|
// The first pyramid's level 1 texture is the input texture blurred.
|
||||||
|
// The subsequent levels of the pyramid are the preceding level blurred.
|
||||||
|
let srcView = inputs.highPass.createView();
|
||||||
for (let i = 0; i < pyramidHeight; i++) {
|
for (let i = 0; i < pyramidHeight; i++) {
|
||||||
hBlurPyramidViews[i] = makePyramidView(hBlurPyramid, i);
|
const hBlurPyramidView = makePyramidView(hBlurPyramid, i);
|
||||||
vBlurPyramidViews[i] = makePyramidView(vBlurPyramid, i);
|
const vBlurPyramidView = makePyramidView(vBlurPyramid, i);
|
||||||
const srcView = i === 0 ? inputs.highPass.createView() : hBlurPyramidViews[i - 1];
|
hBlurBindGroups[i] = makeBindGroup(device, blurPipeline, 0, [hBlurBuffer, linearSampler, srcView, hBlurPyramidView]);
|
||||||
hBlurBindGroups[i] = makeBindGroup(device, blurPipeline, 0, [hBlurBuffer, linearSampler, srcView, hBlurPyramidViews[i]]);
|
vBlurBindGroups[i] = makeBindGroup(device, blurPipeline, 0, [vBlurBuffer, linearSampler, hBlurPyramidView, vBlurPyramidView]);
|
||||||
vBlurBindGroups[i] = makeBindGroup(device, blurPipeline, 0, [vBlurBuffer, linearSampler, hBlurPyramidViews[i], vBlurPyramidViews[i]]);
|
srcView = hBlurPyramidView;
|
||||||
}
|
}
|
||||||
|
|
||||||
combineBindGroup = makeBindGroup(device, combinePipeline, 0, [combineBuffer, linearSampler, vBlurPyramid.createView(), output.createView()]);
|
combineBindGroup = makeBindGroup(device, combinePipeline, 0, [combineBuffer, linearSampler, vBlurPyramid.createView(), output.createView()]);
|
||||||
@@ -100,8 +108,11 @@ export default (context) => {
|
|||||||
|
|
||||||
computePass.setPipeline(blurPipeline);
|
computePass.setPipeline(blurPipeline);
|
||||||
for (let i = 0; i < pyramidHeight; i++) {
|
for (let i = 0; i < pyramidHeight; i++) {
|
||||||
const downsample = 2 ** -i;
|
const dispatchSize = [
|
||||||
const dispatchSize = [Math.ceil(Math.floor(scaledScreenSize[0] * downsample) / 32), Math.floor(Math.floor(scaledScreenSize[1] * downsample)), 1];
|
Math.ceil(Math.floor(scaledScreenSize[0] * 2 ** -i) / 32),
|
||||||
|
Math.floor(Math.floor(scaledScreenSize[1] * 2 ** -i)),
|
||||||
|
1
|
||||||
|
];
|
||||||
computePass.setBindGroup(0, hBlurBindGroups[i]);
|
computePass.setBindGroup(0, hBlurBindGroups[i]);
|
||||||
computePass.dispatch(...dispatchSize);
|
computePass.dispatch(...dispatchSize);
|
||||||
computePass.setBindGroup(0, vBlurBindGroups[i]);
|
computePass.setBindGroup(0, vBlurBindGroups[i]);
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import { loadShader, makeBindGroup, makePass } from "./utils.js";
|
import { loadShader, makeBindGroup, makePass } from "./utils.js";
|
||||||
|
|
||||||
|
// Eventually, WebGPU will allow the output of the final pass in the pipeline to be copied to the canvas texture.
|
||||||
|
// Until then, this render pass does the job.
|
||||||
|
|
||||||
const numVerticesPerQuad = 2 * 3;
|
const numVerticesPerQuad = 2 * 3;
|
||||||
|
|
||||||
export default (context) => {
|
export default ({ device, canvasFormat, canvasContext }) => {
|
||||||
const { config, device, canvasFormat, canvasContext } = context;
|
const nearestSampler = device.createSampler();
|
||||||
|
|
||||||
const linearSampler = device.createSampler({
|
|
||||||
magFilter: "linear",
|
|
||||||
minFilter: "linear",
|
|
||||||
});
|
|
||||||
|
|
||||||
const renderPassConfig = {
|
const renderPassConfig = {
|
||||||
colorAttachments: [
|
colorAttachments: [
|
||||||
@@ -46,7 +44,7 @@ export default (context) => {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
const build = (size, inputs) => {
|
const build = (size, inputs) => {
|
||||||
renderBindGroup = makeBindGroup(device, renderPipeline, 0, [linearSampler, inputs.primary.createView()]);
|
renderBindGroup = makeBindGroup(device, renderPipeline, 0, [nearestSampler, inputs.primary.createView()]);
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ import { makeComputeTarget, loadTexture, loadShader, makeBindGroup, makePass } f
|
|||||||
|
|
||||||
const defaultBGURL = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Flammarion_Colored.jpg/917px-Flammarion_Colored.jpg";
|
const defaultBGURL = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Flammarion_Colored.jpg/917px-Flammarion_Colored.jpg";
|
||||||
|
|
||||||
export default (context) => {
|
export default ({ config, device }) => {
|
||||||
const { config, device } = context;
|
|
||||||
|
|
||||||
const bgURL = "bgURL" in config ? config.bgURL : defaultBGURL;
|
const bgURL = "bgURL" in config ? config.bgURL : defaultBGURL;
|
||||||
const assets = [loadTexture(device, bgURL), loadShader(device, "shaders/wgsl/imagePass.wgsl")];
|
const assets = [loadTexture(device, bgURL), loadShader(device, "shaders/wgsl/imagePass.wgsl")];
|
||||||
|
|
||||||
|
|||||||
@@ -75,9 +75,7 @@ const makePalette = (device, paletteUniforms, entries) => {
|
|||||||
// won't persist across subsequent frames. This is a safe trick
|
// won't persist across subsequent frames. This is a safe trick
|
||||||
// in screen space.
|
// in screen space.
|
||||||
|
|
||||||
export default (context) => {
|
export default ({ config, device, timeBuffer }) => {
|
||||||
const { config, device, timeBuffer } = context;
|
|
||||||
|
|
||||||
const linearSampler = device.createSampler({
|
const linearSampler = device.createSampler({
|
||||||
magFilter: "linear",
|
magFilter: "linear",
|
||||||
minFilter: "linear",
|
minFilter: "linear",
|
||||||
|
|||||||
@@ -31,9 +31,7 @@ const makeConfigBuffer = (device, configUniforms, config, density, gridSize) =>
|
|||||||
return makeUniformBuffer(device, configUniforms, configData);
|
return makeUniformBuffer(device, configUniforms, configData);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default (context) => {
|
export default ({ config, device, timeBuffer, canvasFormat }) => {
|
||||||
const { config, device, timeBuffer, canvasFormat } = context;
|
|
||||||
|
|
||||||
const assets = [loadTexture(device, config.glyphTexURL), loadShader(device, "shaders/wgsl/rainPass.wgsl")];
|
const assets = [loadTexture(device, config.glyphTexURL), loadShader(device, "shaders/wgsl/rainPass.wgsl")];
|
||||||
|
|
||||||
// The volumetric mode multiplies the number of columns
|
// The volumetric mode multiplies the number of columns
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ import { loadShader, makeUniformBuffer, makeComputeTarget, makeBindGroup, makePa
|
|||||||
|
|
||||||
const numVerticesPerQuad = 2 * 3;
|
const numVerticesPerQuad = 2 * 3;
|
||||||
|
|
||||||
export default (context) => {
|
export default ({ config, device, timeBuffer }) => {
|
||||||
const { config, device, timeBuffer } = context;
|
|
||||||
|
|
||||||
const linearSampler = device.createSampler({
|
const linearSampler = device.createSampler({
|
||||||
magFilter: "linear",
|
magFilter: "linear",
|
||||||
minFilter: "linear",
|
minFilter: "linear",
|
||||||
|
|||||||
@@ -37,9 +37,7 @@ const numVerticesPerQuad = 2 * 3;
|
|||||||
// won't persist across subsequent frames. This is a safe trick
|
// won't persist across subsequent frames. This is a safe trick
|
||||||
// in screen space.
|
// in screen space.
|
||||||
|
|
||||||
export default (context, getInputs) => {
|
export default ({ config, device, timeBuffer }) => {
|
||||||
const { config, device, timeBuffer } = context;
|
|
||||||
|
|
||||||
// Expand and convert stripe colors into 1D texture data
|
// Expand and convert stripe colors into 1D texture data
|
||||||
const input =
|
const input =
|
||||||
"stripeColors" in config ? config.stripeColors.split(",").map(parseFloat) : config.effect === "pride" ? prideStripeColors : transPrideStripeColors;
|
"stripeColors" in config ? config.stripeColors.split(",").map(parseFloat) : config.effect === "pride" ? prideStripeColors : transPrideStripeColors;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[[group(0), binding(0)]] var linearSampler : sampler;
|
[[group(0), binding(0)]] var nearestSampler : sampler;
|
||||||
[[group(0), binding(1)]] var tex : texture_2d<f32>;
|
[[group(0), binding(1)]] var tex : texture_2d<f32>;
|
||||||
|
|
||||||
struct VertOutput {
|
struct VertOutput {
|
||||||
@@ -15,5 +15,5 @@ struct VertOutput {
|
|||||||
[[stage(fragment)]] fn fragMain(input : VertOutput) -> [[location(0)]] vec4<f32> {
|
[[stage(fragment)]] fn fragMain(input : VertOutput) -> [[location(0)]] vec4<f32> {
|
||||||
var uv = input.uv;
|
var uv = input.uv;
|
||||||
uv.y = 1.0 - uv.y;
|
uv.y = 1.0 - uv.y;
|
||||||
return textureSample( tex, linearSampler, uv );
|
return textureSample( tex, nearestSampler, uv );
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user