Screen Shaders
Toodle supports fullscreen shaders, also known as post-processing effects.
WARNING
API is low-level and experimental, expect breaking changes.
ts
import { Toodle, Colors, Backends } from "@bloopjs/toodle";
const canvas = document.querySelector("canvas")!;
const toodle = await Toodle.attach(canvas, { filter: "linear" });
if (!(toodle.backend instanceof Backends.WebGPUBackend)) {
throw new Error("Post-processing requires WebGPU backend");
}
const device = toodle.backend.device;
const presentationFormat = toodle.backend.presentationFormat;
const pipeline = device.createRenderPipeline({
label: "color inversion pipeline",
layout: "auto",
primitive: { topology: "triangle-strip" },
vertex: {
module: Backends.PostProcessDefaults.vertexShader(device),
},
fragment: {
targets: [{ format: presentationFormat }],
module: device.createShaderModule({
label: "color inversion fragment shader",
code: /*wgsl*/ `
@group(0) @binding(0) var inputTex: texture_2d<f32>;
@group(0) @binding(1) var inputSampler: sampler;
@fragment
fn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {
let color = textureSample(inputTex, inputSampler, uv);
return vec4f(1.0 - color.rgb, color.a);
}
`,
}),
},
});
// Create a simple color inversion post-process effect
const postprocess: Backends.PostProcess = {
process(queue, encoder, pingpong, screen) {
const renderPass = encoder.beginRenderPass({
label: "invert colors render pass",
colorAttachments: [
{
view: screen.createView(),
clearValue: Colors.web.black,
loadOp: "clear" as const,
storeOp: "store" as const,
},
],
});
const sampler = Backends.PostProcessDefaults.sampler(device);
const bindGroup = device.createBindGroup({
label: "color inversion bind group",
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: pingpong[0].createView() },
{ binding: 1, resource: sampler },
],
});
renderPass.setPipeline(pipeline);
renderPass.setBindGroup(0, bindGroup);
renderPass.draw(4);
renderPass.end();
}
};
(function frame() {
toodle.startFrame();
// Draw a moving circle
toodle.draw(toodle.shapes.Circle({
size: { width: 100, height: 100 },
color: Colors.web.cornflowerBlue,
position: {
x: Math.sin(performance.now() / 1000) * 150,
y: Math.cos(performance.now() / 1000) * 150
},
}));
// Toggle post-process effect every 2 seconds or so
if (toodle.diagnostics.frames % 240 > 120) {
toodle.postprocess = postprocess;
} else {
toodle.postprocess = null;
}
toodle.endFrame();
requestAnimationFrame(frame);
})();Post-processing effects can be used to create a variety of visual effects, such as:
- Color grading
- Bloom
- Blur
- Distortion effects
- CRT scanlines
- Glitch effects
- Lighting