Single-channel textures
An R8 webcam pass chained into history to show single-channel textures in action.
View live demoRaw .ts/**
* Webcam into a real single-channel R8 texture. One ShaderPad renders grayscale
* into an offscreen pass; a second ShaderPad chains that result with history to
* show that reduced-channel textures can be rendered, passed around, and read.
*/
import ShaderPad from 'shaderpad';
import helpers from 'shaderpad/plugins/helpers';
import { getWebcamVideo, stopVideoStream } from '@/examples/demo-utils';
import type { ExampleContext } from '@/examples/runtime';
const SIZE = 256;
const HISTORY_LENGTH = 12;
export async function init({ mount }: ExampleContext) {
const bwFragmentSrc = `#version 300 es
precision mediump float;
in vec2 v_uv;
out vec4 outColor;
uniform sampler2D u_webcam;
void main() {
vec4 webcamColor = texture(u_webcam, vec2(1.0 - v_uv.x, v_uv.y));
float gray = dot(webcamColor.rgb, vec3(0.299, 0.587, 0.114));
outColor = vec4(gray, gray, gray, 1.0);
}`;
const historyFragmentSrc = `#version 300 es
precision mediump float;
in vec2 v_uv;
out vec4 outColor;
uniform highp sampler2DArray u_input;
uniform int u_inputFrameOffset;
void main() {
float zCurrent = historyZ(u_input, u_inputFrameOffset, 0);
float zPast = historyZ(u_input, u_inputFrameOffset, ${HISTORY_LENGTH});
vec2 uv = vec2(v_uv.x, v_uv.y);
float z = v_uv.x < 0.5 ? zPast : zCurrent;
float gray = texture(u_input, vec3(uv, z)).r;
outColor = vec4(gray, gray, gray, 1.0);
}`;
const container = document.createElement('div');
container.className = 'canvas-container';
mount.appendChild(container);
const video = await getWebcamVideo();
const canvasBwOffscreen = new OffscreenCanvas(SIZE, SIZE);
const canvasHistory = document.createElement('canvas');
canvasHistory.width = SIZE;
canvasHistory.height = SIZE;
container.appendChild(canvasHistory);
const padOptions = {
internalFormat: 'R8' as const,
format: 'RED' as const,
type: 'UNSIGNED_BYTE' as const,
};
const bwPad = new ShaderPad(bwFragmentSrc, {
canvas: canvasBwOffscreen,
...padOptions,
});
bwPad.initializeTexture('u_webcam', video);
const historyPad = new ShaderPad(historyFragmentSrc, {
canvas: canvasHistory,
plugins: [helpers()],
...padOptions,
});
historyPad.initializeTexture('u_input', bwPad, { history: HISTORY_LENGTH, ...padOptions });
historyPad.play(() => {
bwPad.updateTextures({ u_webcam: video });
bwPad.draw();
historyPad.updateTextures({ u_input: bwPad });
});
return () => {
bwPad.destroy();
historyPad.destroy();
stopVideoStream(video);
container.remove();
};
}