mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-14 12:29:30 -07:00
Merged the vertex and fragment shader WGSL files, because their bindings can't collide anyhow. The rain render shader now accepts a bind group of time uniforms, which change on every frame, as well as MSDF uniforms, along with an MSDF sampler and texture that's loaded in from the PNG. The shader currently renders the correct grid of quads for volumetric mode, displays the first glyph raw in each one, and every sixty frames, turns on and off the blue channel.
This commit is contained in:
@@ -3,6 +3,32 @@ const getCanvasSize = (canvas) => {
|
|||||||
return [canvas.clientWidth * devicePixelRatio, canvas.clientHeight * devicePixelRatio];
|
return [canvas.clientWidth * devicePixelRatio, canvas.clientHeight * devicePixelRatio];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loadTexture = async (device, url) => {
|
||||||
|
const image = new Image();
|
||||||
|
image.crossOrigin = "anonymous";
|
||||||
|
image.src = url;
|
||||||
|
await image.decode();
|
||||||
|
const imageBitmap = await createImageBitmap(image);
|
||||||
|
|
||||||
|
const texture = device.createTexture({
|
||||||
|
size: [imageBitmap.width, imageBitmap.height, 1],
|
||||||
|
format: "rgba8unorm",
|
||||||
|
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT, // Which of these are necessary?
|
||||||
|
});
|
||||||
|
|
||||||
|
device.queue.copyExternalImageToTexture(
|
||||||
|
{
|
||||||
|
source: imageBitmap,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
texture: texture,
|
||||||
|
},
|
||||||
|
[imageBitmap.width, imageBitmap.height]
|
||||||
|
);
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
};
|
||||||
|
|
||||||
export default async (canvas, config) => {
|
export default async (canvas, config) => {
|
||||||
console.log(config);
|
console.log(config);
|
||||||
|
|
||||||
@@ -37,9 +63,11 @@ export default async (canvas, config) => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: create buffers, uniforms, textures, samplers
|
const sampler = device.createSampler();
|
||||||
|
|
||||||
const uniformBufferSize = 4 * (1 + 1);
|
const msdfTexture = await loadTexture(device, config.glyphTexURL);
|
||||||
|
|
||||||
|
const uniformBufferSize = 4 * (1 * 1 + 1 * 1);
|
||||||
const uniformBuffer = device.createBuffer({
|
const uniformBuffer = device.createBuffer({
|
||||||
size: uniformBufferSize,
|
size: uniformBufferSize,
|
||||||
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, // Which of these are necessary?
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, // Which of these are necessary?
|
||||||
@@ -48,9 +76,24 @@ export default async (canvas, config) => {
|
|||||||
new Int32Array(uniformBuffer.getMappedRange()).set([numColumns, numRows]);
|
new Int32Array(uniformBuffer.getMappedRange()).set([numColumns, numRows]);
|
||||||
uniformBuffer.unmap();
|
uniformBuffer.unmap();
|
||||||
|
|
||||||
// TODO: create pipelines, bind groups, shaders
|
const msdfUniformBufferSize = 4 * (1 * 1);
|
||||||
|
const msdfUniformBuffer = device.createBuffer({
|
||||||
|
size: msdfUniformBufferSize,
|
||||||
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.FRAGMENT | GPUBufferUsage.COPY_DST, // Which of these are necessary?
|
||||||
|
mappedAtCreation: true,
|
||||||
|
});
|
||||||
|
new Int32Array(msdfUniformBuffer.getMappedRange()).set([config.glyphTextureColumns]);
|
||||||
|
msdfUniformBuffer.unmap();
|
||||||
|
|
||||||
const [vert, frag] = await Promise.all(["shaders/rainPass.vert.wgsl", "shaders/rainPass.frag.wgsl"].map(async (path) => (await fetch(path)).text()));
|
const timeBufferSize = 4 * (1 * 1 + 1 * 1);
|
||||||
|
const timeBuffer = device.createBuffer({
|
||||||
|
size: uniformBufferSize,
|
||||||
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.VERTEX | GPUBufferUsage.FRAGMENT | GPUBufferUsage.COMPUTE | GPUBufferUsage.COPY_DST, // Which of these are necessary?
|
||||||
|
});
|
||||||
|
|
||||||
|
const [rainRenderShader] = await Promise.all(["shaders/rainRenderPass.wgsl"].map(async (path) => (await fetch(path)).text()));
|
||||||
|
|
||||||
|
const rainRenderShaderModule = device.createShaderModule({ code: rainRenderShader });
|
||||||
|
|
||||||
const additiveBlendComponent = {
|
const additiveBlendComponent = {
|
||||||
operation: "add",
|
operation: "add",
|
||||||
@@ -65,16 +108,12 @@ export default async (canvas, config) => {
|
|||||||
|
|
||||||
const rainRenderPipeline = device.createRenderPipeline({
|
const rainRenderPipeline = device.createRenderPipeline({
|
||||||
vertex: {
|
vertex: {
|
||||||
module: device.createShaderModule({
|
module: rainRenderShaderModule,
|
||||||
code: vert,
|
entryPoint: "vertMain",
|
||||||
}),
|
|
||||||
entryPoint: "main",
|
|
||||||
},
|
},
|
||||||
fragment: {
|
fragment: {
|
||||||
module: device.createShaderModule({
|
module: rainRenderShaderModule,
|
||||||
code: frag,
|
entryPoint: "fragMain",
|
||||||
}),
|
|
||||||
entryPoint: "main",
|
|
||||||
targets: [
|
targets: [
|
||||||
{
|
{
|
||||||
format: presentationFormat,
|
format: presentationFormat,
|
||||||
@@ -85,7 +124,7 @@ export default async (canvas, config) => {
|
|||||||
primitive: {
|
primitive: {
|
||||||
// What happens if this isn't here?
|
// What happens if this isn't here?
|
||||||
topology: "triangle-list", // What happens if this isn't here?
|
topology: "triangle-list", // What happens if this isn't here?
|
||||||
cullMode: "none", // What happens if this isn't here?
|
cullMode: "back", // What happens if this isn't here?
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -101,18 +140,55 @@ export default async (canvas, config) => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const msdfBindGroup = device.createBindGroup({
|
||||||
|
layout: rainRenderPipeline.getBindGroupLayout(1),
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
binding: 0,
|
||||||
|
resource: {
|
||||||
|
buffer: msdfUniformBuffer,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
binding: 1,
|
||||||
|
resource: sampler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
binding: 2,
|
||||||
|
resource: msdfTexture.createView(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const timeBindGroup = device.createBindGroup({
|
||||||
|
layout: rainRenderPipeline.getBindGroupLayout(2),
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
binding: 0,
|
||||||
|
resource: {
|
||||||
|
buffer: timeBuffer,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const rainRenderPipelineBindGroups = [uniformBindGroup, msdfBindGroup, timeBindGroup];
|
||||||
|
|
||||||
const bundleEncoder = device.createRenderBundleEncoder({
|
const bundleEncoder = device.createRenderBundleEncoder({
|
||||||
colorFormats: [presentationFormat],
|
colorFormats: [presentationFormat],
|
||||||
});
|
});
|
||||||
|
|
||||||
bundleEncoder.setPipeline(rainRenderPipeline);
|
bundleEncoder.setPipeline(rainRenderPipeline);
|
||||||
bundleEncoder.setBindGroup(0, uniformBindGroup);
|
rainRenderPipelineBindGroups.forEach((bindGroup, index) => {
|
||||||
bundleEncoder.draw(6 * numColumns * numRows, 1, 0, 0);
|
bundleEncoder.setBindGroup(index, bindGroup);
|
||||||
|
});
|
||||||
|
const numQuads = numColumns * numRows;
|
||||||
|
bundleEncoder.draw(6 * numQuads, 1, 0, 0);
|
||||||
const renderBundles = [bundleEncoder.finish()];
|
const renderBundles = [bundleEncoder.finish()];
|
||||||
|
|
||||||
// queue.writeBuffer(uniformBuffer, 0, new Int32Array([numColumns, numRows]));
|
let frame = 0;
|
||||||
|
|
||||||
const frame = (now) => {
|
const renderLoop = (now) => {
|
||||||
const canvasSize = getCanvasSize(canvas);
|
const canvasSize = getCanvasSize(canvas);
|
||||||
if (canvasSize[0] !== canvasConfig.size[0] || canvasSize[1] !== canvasConfig.size[1]) {
|
if (canvasSize[0] !== canvasConfig.size[0] || canvasSize[1] !== canvasConfig.size[1]) {
|
||||||
canvasConfig.size = canvasSize;
|
canvasConfig.size = canvasSize;
|
||||||
@@ -123,9 +199,9 @@ export default async (canvas, config) => {
|
|||||||
// TODO: update camera matrix, screen size, write to queue
|
// TODO: update camera matrix, screen size, write to queue
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: update the uniforms that change, write to queue
|
queue.writeBuffer(timeBuffer, 0, new Int32Array([now, frame]));
|
||||||
|
frame++;
|
||||||
|
|
||||||
renderPassConfig.colorAttachments[0].loadValue.r = Math.sin((now / 1000) * 2) / 2 + 0.5;
|
|
||||||
renderPassConfig.colorAttachments[0].view = canvasContext.getCurrentTexture().createView();
|
renderPassConfig.colorAttachments[0].view = canvasContext.getCurrentTexture().createView();
|
||||||
|
|
||||||
const encoder = device.createCommandEncoder();
|
const encoder = device.createCommandEncoder();
|
||||||
@@ -135,8 +211,8 @@ export default async (canvas, config) => {
|
|||||||
const commandBuffer = encoder.finish();
|
const commandBuffer = encoder.finish();
|
||||||
queue.submit([commandBuffer]);
|
queue.submit([commandBuffer]);
|
||||||
|
|
||||||
requestAnimationFrame(frame);
|
requestAnimationFrame(renderLoop);
|
||||||
};
|
};
|
||||||
|
|
||||||
requestAnimationFrame(frame);
|
requestAnimationFrame(renderLoop);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
[[stage(fragment)]] fn main([[location(0)]] UV : vec2<f32>) -> [[location(0)]] vec4<f32> {
|
|
||||||
return vec4<f32>(0.0, UV, 1.0);
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
[[block]] struct Uniforms {
|
|
||||||
numColumns: i32;
|
|
||||||
numRows: i32;
|
|
||||||
};
|
|
||||||
[[binding(0), group(0)]] var<uniform> uniforms : Uniforms;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
[[builtin(position)]] Position : vec4<f32>;
|
|
||||||
[[location(0)]] UV : vec2<f32>;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[stage(vertex)]] fn main([[builtin(vertex_index)]] VertexIndex : u32) -> VertexOutput {
|
|
||||||
|
|
||||||
var i = i32(VertexIndex);
|
|
||||||
var quadIndex = i / 6;
|
|
||||||
|
|
||||||
var cornerPosition = vec2<f32>(
|
|
||||||
f32(i % 2),
|
|
||||||
f32(((i + 1) % 6 / 3))
|
|
||||||
);
|
|
||||||
|
|
||||||
var x = uniforms.numColumns;
|
|
||||||
|
|
||||||
var position = cornerPosition;
|
|
||||||
position = position + vec2<f32>(
|
|
||||||
f32(quadIndex % uniforms.numColumns),
|
|
||||||
f32(quadIndex / uniforms.numColumns)
|
|
||||||
);
|
|
||||||
position = position / vec2<f32>(
|
|
||||||
f32(uniforms.numColumns),
|
|
||||||
f32(uniforms.numRows)
|
|
||||||
);
|
|
||||||
position = position * 2.0 - 1.0;
|
|
||||||
|
|
||||||
return VertexOutput(
|
|
||||||
vec4<f32>(position, 1.0, 1.0),
|
|
||||||
cornerPosition
|
|
||||||
);
|
|
||||||
}
|
|
||||||
69
shaders/rainRenderPass.wgsl
Normal file
69
shaders/rainRenderPass.wgsl
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
let PI:f32 = 3.14159265359;
|
||||||
|
let TWO_PI:f32 = 6.28318530718;
|
||||||
|
|
||||||
|
[[block]] struct Uniforms {
|
||||||
|
numColumns: i32;
|
||||||
|
numRows: i32;
|
||||||
|
};
|
||||||
|
[[group(0), binding(0)]] var<uniform> uniforms:Uniforms;
|
||||||
|
|
||||||
|
[[block]] struct MSDFUniforms {
|
||||||
|
numColumns: i32;
|
||||||
|
};
|
||||||
|
[[group(1), binding(0)]] var<uniform> msdfUniforms:MSDFUniforms;
|
||||||
|
[[group(1), binding(1)]] var msdfSampler: sampler;
|
||||||
|
[[group(1), binding(2)]] var msdfTexture: texture_2d<f32>;
|
||||||
|
|
||||||
|
[[block]] struct TimeUniforms {
|
||||||
|
time: i32;
|
||||||
|
frame: i32;
|
||||||
|
};
|
||||||
|
[[group(2), binding(0)]] var<uniform> timeUniforms:TimeUniforms;
|
||||||
|
|
||||||
|
// Vertex shader
|
||||||
|
|
||||||
|
struct VertexOutput {
|
||||||
|
[[builtin(position)]] Position:vec4<f32>;
|
||||||
|
[[location(0)]] UV:vec2<f32>;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[stage(vertex)]] fn vertMain([[builtin(vertex_index)]] VertexIndex:u32) -> VertexOutput {
|
||||||
|
|
||||||
|
var i = i32(VertexIndex);
|
||||||
|
var quadIndex = i / 6;
|
||||||
|
|
||||||
|
var cornerPosition = vec2<f32>(
|
||||||
|
f32(i % 2),
|
||||||
|
f32(((i + 1) % 6 / 3))
|
||||||
|
);
|
||||||
|
|
||||||
|
var x = uniforms.numColumns;
|
||||||
|
|
||||||
|
var position = cornerPosition;
|
||||||
|
position = position + vec2<f32>(
|
||||||
|
f32(quadIndex % uniforms.numColumns),
|
||||||
|
f32(quadIndex / uniforms.numColumns)
|
||||||
|
);
|
||||||
|
position = position / vec2<f32>(
|
||||||
|
f32(uniforms.numColumns),
|
||||||
|
f32(uniforms.numRows)
|
||||||
|
);
|
||||||
|
position = 1.0 - position * 2.0;
|
||||||
|
// position.x = position.x + f32(quadIndex) * 0.01;
|
||||||
|
|
||||||
|
return VertexOutput(
|
||||||
|
vec4<f32>(position, 1.0, 1.0),
|
||||||
|
cornerPosition
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fragment shader
|
||||||
|
|
||||||
|
[[stage(fragment)]] fn fragMain([[location(0)]] UV:vec2<f32>) -> [[location(0)]] vec4<f32> {
|
||||||
|
var msdf:vec4<f32> = textureSample(msdfTexture, msdfSampler, UV / f32(msdfUniforms.numColumns));
|
||||||
|
// msdf.b = msdf.b * (sin(f32(timeUniforms.time) / 1000.0 * TWO_PI) * 0.5 + 0.5);
|
||||||
|
msdf.b = msdf.b * f32(timeUniforms.frame / 60 % 2);
|
||||||
|
var time = timeUniforms.time;
|
||||||
|
|
||||||
|
return msdf;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user