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

View File

@ -935,212 +935,71 @@ export function ShaderBackground() {
gl.STATIC_DRAW
);
{
gl.useProgram(buffer_1_program);
const posAttrLocation = gl.getAttribLocation(
// Vertex attributes for each program
const programs = [
buffer_1_program,
"a_position"
);
gl.vertexAttribPointer(posAttrLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(posAttrLocation);
}
{
gl.useProgram(buffer_2_program);
const posAttrLocation = gl.getAttribLocation(
buffer_2_program,
"a_position"
);
gl.vertexAttribPointer(posAttrLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(posAttrLocation);
}
{
gl.useProgram(buffer_3_program);
const posAttrLocation = gl.getAttribLocation(
buffer_3_program,
"a_position"
);
gl.vertexAttribPointer(posAttrLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(posAttrLocation);
}
{
gl.useProgram(buffer_4_program);
const posAttrLocation = gl.getAttribLocation(
buffer_4_program,
"a_position"
);
gl.vertexAttribPointer(posAttrLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(posAttrLocation);
}
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);
});
{
gl.useProgram(finalProgram);
const posAttrLocation = gl.getAttribLocation(finalProgram, "a_position");
gl.vertexAttribPointer(posAttrLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(posAttrLocation);
}
function setResolution(
program: WebGLProgram,
canvas: HTMLCanvasElement,
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.TEXTURE_2D,
// --- 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,
canvas.width,
canvas.height,
gl!.RGBA,
w,
h,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
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);
gl!.texParameteri(gl!.TEXTURE_2D, gl!.TEXTURE_MIN_FILTER, gl!.LINEAR);
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 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,
const fb = gl!.createFramebuffer();
gl!.bindFramebuffer(gl!.DRAW_FRAMEBUFFER, fb);
gl!.framebufferTexture2D(
gl!.DRAW_FRAMEBUFFER,
gl!.COLOR_ATTACHMENT0,
gl!.TEXTURE_2D,
tex,
0
);
const status = gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER);
switch (status) {
case gl.FRAMEBUFFER_COMPLETE:
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");
return { tex, fb };
}
const ichannel0_location_buffer_1 = gl.getUniformLocation(
buffer_1_program as WebGLProgram,
"iChannel0"
);
// 1. Scene Buffer (Stores the main render)
const sceneTarget = createFramebufferTexture(canvas.width, canvas.height);
const ichannel0_location_buffer_2 = gl.getUniformLocation(
buffer_2_program as WebGLProgram,
"iChannel0"
);
// 2. Ping-Pong Buffers (For doing multipass bloom effects)
const pingTarget = createFramebufferTexture(canvas.width, canvas.height);
const pongTarget = createFramebufferTexture(canvas.width, canvas.height);
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
// --- NOISE TEXTURE ---
// Fix: Create once, not every frame
const noiseBytes = generateRandomRGBABytes(canvas.width, canvas.height);
const noiseTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, tex);
// Texture parameters suitable for data textures
gl.bindTexture(gl.TEXTURE_2D, noiseTexture);
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,
@ -1150,98 +1009,154 @@ export function ShaderBackground() {
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
bytes
noiseBytes
);
// 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);
function setResolution(program: WebGLProgram) {
const loc = gl!.getUniformLocation(program, "iResolution");
if (loc) gl!.uniform3fv(loc, [canvas!.width, canvas!.height, 1]);
}
function setTime(program: WebGLProgram, now: number) {
const loc = gl!.getUniformLocation(program, "iTime");
if (loc) gl!.uniform1f(loc, now);
}
// 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();
function update(now: number) {
// time in seconds
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 (