feat: Implement multi-pass rendering using WebGL framebuffers and integrate random texture generation directly into the shader background component.

This commit is contained in:
code002lover 2025-12-06 19:45:28 +01:00
parent 54bf5b38c2
commit d2a8edb004
2 changed files with 190 additions and 70 deletions

View File

@ -1,5 +1,5 @@
import { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
import { createAndBindRandomTextureToIChannel0 } from "./randomTextureForShader"; import { generateRandomRGBABytes } from "./randomTextureForShader";
const buffer_1 = `#version 300 es const buffer_1 = `#version 300 es
precision mediump float; precision mediump float;
@ -964,51 +964,227 @@ export function ShaderBackground() {
} }
} }
const frameBufferTexture = gl.createTexture();
if (!frameBufferTexture) return;
gl.bindTexture(gl.TEXTURE_2D, frameBufferTexture);
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 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);
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");
}
const posBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]),
gl.STATIC_DRAW
);
const ichannel0_location_buffer_1 = gl.getUniformLocation(
buffer_1_program as WebGLProgram,
"iChannel0"
);
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( function render(
gl: WebGL2RenderingContext, gl: WebGL2RenderingContext,
canvas: HTMLCanvasElement, canvas: HTMLCanvasElement,
now: number now: number
) { ) {
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, frameBuffer);
//Clear with Clear color //Clear with Clear color
gl.clear(gl.COLOR_BUFFER_BIT); gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(buffer_1_program); gl.useProgram(buffer_1_program);
setResolution(buffer_1_program as WebGLProgram, canvas, gl); setResolution(buffer_1_program as WebGLProgram, canvas, gl);
setTime(buffer_1_program as WebGLProgram, now, gl); setTime(buffer_1_program as WebGLProgram, now, gl);
const tex = createAndBindRandomTextureToIChannel0( const bytes = generateRandomRGBABytes(canvas.width, canvas.height);
gl,
buffer_1_program as WebGLProgram, const tex = gl.createTexture();
canvas.width, if (!tex) throw new Error("Failed to create texture");
canvas.height
); // Activate texture unit 0 and bind the texture
gl.activeTexture(gl.TEXTURE0); gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, tex); 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 // RENDER 1
gl.drawBuffers([gl.BACK]); gl.framebufferTexture2D(
gl.DRAW_FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
frameBufferTexture,
0
);
gl.drawArrays(gl.TRIANGLES, 0, 6);
gl.useProgram(buffer_2_program); gl.useProgram(buffer_2_program);
setResolution(buffer_2_program as WebGLProgram, canvas, gl); setResolution(buffer_2_program as WebGLProgram, canvas, gl);
// RENDER 2 // RENDER 2
gl.drawBuffers([gl.BACK]); 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); gl.useProgram(buffer_3_program);
setResolution(buffer_3_program as WebGLProgram, canvas, gl); setResolution(buffer_3_program as WebGLProgram, canvas, gl);
// RENDER 3 // RENDER 3
gl.drawBuffers([gl.BACK]); 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); gl.useProgram(buffer_4_program);
setResolution(buffer_4_program as WebGLProgram, canvas, gl); setResolution(buffer_4_program as WebGLProgram, canvas, gl);
// RENDER 4 // RENDER 4
gl.drawBuffers([gl.BACK]); 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); gl.useProgram(finalProgram);
setResolution(finalProgram as WebGLProgram, canvas, gl); setResolution(finalProgram as WebGLProgram, canvas, gl);
// FINAL RENDER // 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); gl.drawArrays(gl.TRIANGLES, 0, 6);
} }
@ -1024,5 +1200,7 @@ export function ShaderBackground() {
requestAnimationFrame(update); requestAnimationFrame(update);
}, []); }, []);
return <canvas ref={canvasRef} />; return (
<canvas ref={canvasRef} id="blackhole_canvas" width="800" height="450" />
);
} }

View File

@ -33,61 +33,3 @@ export function generateRandomRGBABytes(
return out; return out;
} }
/**
* Create a WebGL texture from random data and bind it to texture unit 0,
* then set the sampler2D uniform named `iChannel0` to use unit 0.
*
* gl: WebGLRenderingContext or WebGL2RenderingContext
* program: compiled & linked shader program (must be in use or will be used)
*/
export function createAndBindRandomTextureToIChannel0(
gl: WebGLRenderingContext | WebGL2RenderingContext,
program: WebGLProgram,
width: number,
height: number
): WebGLTexture {
const bytes = generateRandomRGBABytes(width, 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,
width,
height,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
bytes
);
const location = gl.getUniformLocation(program, "iChannel0");
if (location === null) {
// Clean up binding and return but signal with thrown error if desired
gl.bindTexture(gl.TEXTURE_2D, null);
throw new Error("Uniform 'iChannel0' not found in program");
}
// Set sampler uniform to texture unit 0
gl.uniform1i(location, 0);
// Unbind the texture if you prefer (not strictly necessary)
gl.bindTexture(gl.TEXTURE_2D, null);
return tex;
}