This commit is contained in:
code002lover 2025-12-06 21:25:50 +01:00
parent f58ff49c46
commit 6128804352

View File

@ -935,91 +935,71 @@ export function ShaderBackground() {
gl.STATIC_DRAW gl.STATIC_DRAW
); );
{ // Vertex attributes for each program
gl.useProgram(buffer_1_program); const programs = [
const posAttrLocation = gl.getAttribLocation( buffer_1_program,
buffer_1_program, buffer_2_program,
"a_position" buffer_3_program,
buffer_4_program,
finalProgram,
];
programs.forEach((prog) => {
gl.useProgram(prog);
const loc = gl.getAttribLocation(prog, "a_position");
gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(loc);
});
// --- HELPER FUNC ---
function createFramebufferTexture(w: number, h: number) {
const tex = gl!.createTexture();
gl!.bindTexture(gl!.TEXTURE_2D, tex);
gl!.texImage2D(
gl!.TEXTURE_2D,
0,
gl!.RGBA,
w,
h,
0,
gl!.RGBA,
gl!.UNSIGNED_BYTE,
null
); );
gl.vertexAttribPointer(posAttrLocation, 2, gl.FLOAT, false, 0, 0); gl!.texParameteri(gl!.TEXTURE_2D, gl!.TEXTURE_MIN_FILTER, gl!.LINEAR);
gl.enableVertexAttribArray(posAttrLocation); gl!.texParameteri(gl!.TEXTURE_2D, gl!.TEXTURE_MAG_FILTER, gl!.LINEAR);
} gl!.texParameteri(gl!.TEXTURE_2D, gl!.TEXTURE_WRAP_S, gl!.CLAMP_TO_EDGE);
gl!.texParameteri(gl!.TEXTURE_2D, gl!.TEXTURE_WRAP_T, gl!.CLAMP_TO_EDGE);
{ const fb = gl!.createFramebuffer();
gl.useProgram(buffer_2_program); gl!.bindFramebuffer(gl!.DRAW_FRAMEBUFFER, fb);
const posAttrLocation = gl.getAttribLocation( gl!.framebufferTexture2D(
buffer_2_program, gl!.DRAW_FRAMEBUFFER,
"a_position" gl!.COLOR_ATTACHMENT0,
gl!.TEXTURE_2D,
tex,
0
); );
gl.vertexAttribPointer(posAttrLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(posAttrLocation); return { tex, fb };
} }
{ // 1. Scene Buffer (Stores the main render)
gl.useProgram(buffer_3_program); const sceneTarget = createFramebufferTexture(canvas.width, canvas.height);
const posAttrLocation = gl.getAttribLocation(
buffer_3_program,
"a_position"
);
gl.vertexAttribPointer(posAttrLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(posAttrLocation);
}
{ // 2. Ping-Pong Buffers (For doing multipass bloom effects)
gl.useProgram(buffer_4_program); const pingTarget = createFramebufferTexture(canvas.width, canvas.height);
const posAttrLocation = gl.getAttribLocation( const pongTarget = createFramebufferTexture(canvas.width, canvas.height);
buffer_4_program,
"a_position"
);
gl.vertexAttribPointer(posAttrLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(posAttrLocation);
}
{ // --- NOISE TEXTURE ---
gl.useProgram(finalProgram); // Fix: Create once, not every frame
const posAttrLocation = gl.getAttribLocation(finalProgram, "a_position"); const noiseBytes = generateRandomRGBABytes(canvas.width, canvas.height);
gl.vertexAttribPointer(posAttrLocation, 2, gl.FLOAT, false, 0, 0); const noiseTexture = gl.createTexture();
gl.enableVertexAttribArray(posAttrLocation); gl.activeTexture(gl.TEXTURE0);
} gl.bindTexture(gl.TEXTURE_2D, noiseTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
function setResolution( gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
program: WebGLProgram, gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
canvas: HTMLCanvasElement, gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl: WebGL2RenderingContext
) {
const loc = gl.getUniformLocation(program, "iResolution");
if (loc === null) {
console.warn(
"iResolution uniform not found (maybe not used by shader)."
);
} else {
const w = canvas.width;
const h = canvas.height;
gl.uniform3fv(loc, [w, h, 1]);
}
}
function setTime(
program: WebGLProgram,
now: number,
gl: WebGL2RenderingContext
) {
const loc = gl.getUniformLocation(program, "iTime");
if (loc === null) {
console.warn(
"iTime uniform not found (maybe not used by shader).",
program
);
} else {
gl.uniform1f(loc, now);
}
}
const frameBufferTexture = gl.createTexture();
if (!frameBufferTexture) return;
gl.bindTexture(gl.TEXTURE_2D, frameBufferTexture);
gl.texImage2D( gl.texImage2D(
gl.TEXTURE_2D, gl.TEXTURE_2D,
0, 0,
@ -1029,219 +1009,154 @@ export function ShaderBackground() {
0, 0,
gl.RGBA, gl.RGBA,
gl.UNSIGNED_BYTE, gl.UNSIGNED_BYTE,
null noiseBytes
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
const frameBufferTexture2 = gl.createTexture();
if (!frameBufferTexture2) return;
gl.bindTexture(gl.TEXTURE_2D, frameBufferTexture2);
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
canvas.width,
canvas.height,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
null
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
const frameBuffer = gl.createFramebuffer();
if (!frameBuffer) return;
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, frameBuffer);
gl.framebufferTexture2D(
gl.DRAW_FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
frameBufferTexture,
0
); );
const status = gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER); function setResolution(program: WebGLProgram) {
switch (status) { const loc = gl!.getUniformLocation(program, "iResolution");
case gl.FRAMEBUFFER_COMPLETE: if (loc) gl!.uniform3fv(loc, [canvas!.width, canvas!.height, 1]);
console.log("Framebuffer is complete");
break;
case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
console.log("Framebuffer is incomplete attachment");
break;
case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
console.log("Framebuffer is incomplete missing attachment");
break;
case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
console.log("Framebuffer is incomplete dimensions");
break;
case gl.FRAMEBUFFER_UNSUPPORTED:
console.log("Framebuffer is unsupported");
break;
default:
console.log("Framebuffer is unknown");
} }
const ichannel0_location_buffer_1 = gl.getUniformLocation( function setTime(program: WebGLProgram, now: number) {
buffer_1_program as WebGLProgram, const loc = gl!.getUniformLocation(program, "iTime");
"iChannel0" if (loc) gl!.uniform1f(loc, now);
);
const ichannel0_location_buffer_2 = gl.getUniformLocation(
buffer_2_program as WebGLProgram,
"iChannel0"
);
const ichannel0_location_buffer_3 = gl.getUniformLocation(
buffer_3_program as WebGLProgram,
"iChannel0"
);
const ichannel0_location_buffer_4 = gl.getUniformLocation(
buffer_4_program as WebGLProgram,
"iChannel0"
);
const ichannel0_location_final = gl.getUniformLocation(
finalProgram as WebGLProgram,
"iChannel0"
);
function render(
gl: WebGL2RenderingContext,
canvas: HTMLCanvasElement,
now: number
) {
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, frameBuffer);
//Clear with Clear color
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(buffer_1_program);
setResolution(buffer_1_program as WebGLProgram, canvas, gl);
setTime(buffer_1_program as WebGLProgram, now, gl);
const bytes = generateRandomRGBABytes(canvas.width, canvas.height);
const tex = gl.createTexture();
if (!tex) throw new Error("Failed to create texture");
// Activate texture unit 0 and bind the texture
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, tex);
// Texture parameters suitable for data textures
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
// Upload pixel data as RGBA UNSIGNED_BYTE
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
canvas.width,
canvas.height,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
bytes
);
// Set sampler uniform to texture unit 0
gl.uniform1i(ichannel0_location_buffer_1, 0);
// RENDER 1
gl.framebufferTexture2D(
gl.DRAW_FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
frameBufferTexture,
0
);
gl.drawArrays(gl.TRIANGLES, 0, 6);
gl.useProgram(buffer_2_program);
setResolution(buffer_2_program as WebGLProgram, canvas, gl);
// RENDER 2
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, frameBufferTexture);
gl.uniform1i(ichannel0_location_buffer_2, 1);
gl.framebufferTexture2D(
gl.DRAW_FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
frameBufferTexture2,
0
);
gl.drawArrays(gl.TRIANGLES, 0, 6);
gl.useProgram(buffer_3_program);
setResolution(buffer_3_program as WebGLProgram, canvas, gl);
// RENDER 3
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, frameBufferTexture2);
gl.uniform1i(ichannel0_location_buffer_3, 1);
gl.framebufferTexture2D(
gl.DRAW_FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
frameBufferTexture,
0
);
gl.drawArrays(gl.TRIANGLES, 0, 6);
gl.useProgram(buffer_4_program);
setResolution(buffer_4_program as WebGLProgram, canvas, gl);
// RENDER 4
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, frameBufferTexture);
gl.uniform1i(ichannel0_location_buffer_4, 1);
gl.framebufferTexture2D(
gl.DRAW_FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
frameBufferTexture2,
0
);
gl.drawArrays(gl.TRIANGLES, 0, 6);
gl.useProgram(finalProgram);
setResolution(finalProgram as WebGLProgram, canvas, gl);
// FINAL RENDER
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, frameBufferTexture2);
gl.uniform1i(ichannel0_location_final, 1);
gl.framebufferTexture2D(
gl.DRAW_FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
frameBufferTexture,
0
);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
gl.drawArrays(gl.TRIANGLES, 0, 6);
} }
// Cache uniform locations
const locs = {
b1_iChannel0: gl.getUniformLocation(buffer_1_program, "iChannel0"),
b2_iChannel0: gl.getUniformLocation(buffer_2_program, "iChannel0"),
b3_iChannel0: gl.getUniformLocation(buffer_3_program, "iChannel0"),
b4_iChannel0: gl.getUniformLocation(buffer_4_program, "iChannel0"),
final_iChannel0: gl.getUniformLocation(finalProgram, "iChannel0"),
final_iChannel3: gl.getUniformLocation(finalProgram, "iChannel3"), // Bloom result
};
let reqId: number;
const started = Date.now(); const started = Date.now();
function update(now: number) { function update(now: number) {
// time in seconds
const time = (now - started) / 1000; const time = (now - started) / 1000;
render(gl as WebGL2RenderingContext, canvas as HTMLCanvasElement, time);
requestAnimationFrame(update); // ----------------------------------------------------
// PASS 1: Render SCENE -> sceneTarget
// ----------------------------------------------------
gl!.bindFramebuffer(gl!.DRAW_FRAMEBUFFER, sceneTarget.fb);
gl!.viewport(0, 0, canvas!.width, canvas!.height);
gl!.clear(gl!.COLOR_BUFFER_BIT);
gl!.useProgram(buffer_1_program);
setResolution(buffer_1_program!);
setTime(buffer_1_program!, time);
// Bind Noise to Unit 0
gl!.activeTexture(gl!.TEXTURE0);
gl!.bindTexture(gl!.TEXTURE_2D, noiseTexture);
gl!.uniform1i(locs.b1_iChannel0, 0);
gl!.drawArrays(gl!.TRIANGLES, 0, 6);
// ----------------------------------------------------
// PASS 2: Bloom Prep (Buffer 2) -> pingTarget
// input: sceneTarget (Unit 0)
// ----------------------------------------------------
gl!.bindFramebuffer(gl!.DRAW_FRAMEBUFFER, pingTarget.fb);
gl!.useProgram(buffer_2_program);
setResolution(buffer_2_program!);
gl!.activeTexture(gl!.TEXTURE0);
gl!.bindTexture(gl!.TEXTURE_2D, sceneTarget.tex);
gl!.uniform1i(locs.b2_iChannel0, 0);
gl!.drawArrays(gl!.TRIANGLES, 0, 6);
// ----------------------------------------------------
// PASS 3: Horizontal Blur (Buffer 3) -> pongTarget
// input: pingTarget (Unit 0)
// ----------------------------------------------------
gl!.bindFramebuffer(gl!.DRAW_FRAMEBUFFER, pongTarget.fb);
gl!.useProgram(buffer_3_program);
setResolution(buffer_3_program!);
gl!.activeTexture(gl!.TEXTURE0);
gl!.bindTexture(gl!.TEXTURE_2D, pingTarget.tex);
gl!.uniform1i(locs.b3_iChannel0, 0);
gl!.drawArrays(gl!.TRIANGLES, 0, 6);
// ----------------------------------------------------
// PASS 4: Vertical Blur (Buffer 4) -> pingTarget (Final Bloom)
// input: pongTarget (Unit 0)
// ----------------------------------------------------
// Reuse pingTarget to store the final bloom result
gl!.bindFramebuffer(gl!.DRAW_FRAMEBUFFER, pingTarget.fb);
gl!.useProgram(buffer_4_program);
setResolution(buffer_4_program!);
gl!.activeTexture(gl!.TEXTURE0);
gl!.bindTexture(gl!.TEXTURE_2D, pongTarget.tex);
gl!.uniform1i(locs.b4_iChannel0, 0);
gl!.drawArrays(gl!.TRIANGLES, 0, 6);
// ----------------------------------------------------
// PASS 5: Final Composition -> Screen
// Inputs:
// - iChannel0: sceneTarget (Unit 0)
// - iChannel3: pingTarget aka Bloom Result (Unit 3)
// ----------------------------------------------------
gl!.bindFramebuffer(gl!.DRAW_FRAMEBUFFER, null); // Screen
gl!.useProgram(finalProgram);
setResolution(finalProgram!);
// Bind Scene to Unit 0
gl!.activeTexture(gl!.TEXTURE0);
gl!.bindTexture(gl!.TEXTURE_2D, sceneTarget.tex);
gl!.uniform1i(locs.final_iChannel0, 0);
// Bind Bloom to Unit 3
gl!.activeTexture(gl!.TEXTURE3);
gl!.bindTexture(gl!.TEXTURE_2D, pingTarget.tex);
gl!.uniform1i(locs.final_iChannel3, 3); // Tell shader iChannel3 is on unit 3
gl!.drawArrays(gl!.TRIANGLES, 0, 6);
reqId = requestAnimationFrame(update);
} }
requestAnimationFrame(update); reqId = requestAnimationFrame(update);
// CLEANUP
return () => {
cancelAnimationFrame(reqId);
gl!.deleteProgram(buffer_1_program);
gl!.deleteProgram(buffer_2_program);
gl!.deleteProgram(buffer_3_program);
gl!.deleteProgram(buffer_4_program);
gl!.deleteProgram(finalProgram);
gl!.deleteShader(buffer_1_shader);
gl!.deleteShader(buffer_2_shader);
gl!.deleteShader(buffer_3_shader);
gl!.deleteShader(buffer_4_shader);
gl!.deleteShader(fragmentShader);
gl!.deleteShader(vertexShader);
gl!.deleteBuffer(posBuffer);
gl!.deleteTexture(noiseTexture);
gl!.deleteTexture(sceneTarget.tex);
gl!.deleteFramebuffer(sceneTarget.fb);
gl!.deleteTexture(pingTarget.tex);
gl!.deleteFramebuffer(pingTarget.fb);
gl!.deleteTexture(pongTarget.tex);
gl!.deleteFramebuffer(pongTarget.fb);
};
}, []); }, []);
return ( return (