94 lines
2.8 KiB
TypeScript
94 lines
2.8 KiB
TypeScript
export function generateRandomRGBABytes(
|
|
width: number,
|
|
height: number
|
|
): Uint8Array {
|
|
if (width <= 0 || height <= 0) {
|
|
throw new Error("width and height must be positive");
|
|
}
|
|
const len = width * height * 4;
|
|
const out = new Uint8Array(len);
|
|
|
|
// Maximum bytes per getRandomValues call (per spec / browsers)
|
|
const MAX_GETRANDOM_BYTES = 65536;
|
|
|
|
if (typeof crypto !== "undefined" && "getRandomValues" in crypto) {
|
|
// Fill in chunks of up to MAX_GETRANDOM_BYTES
|
|
let offset = 0;
|
|
while (offset < len) {
|
|
const chunkSize = Math.min(MAX_GETRANDOM_BYTES, len - offset);
|
|
// Subarray view for the current chunk
|
|
const chunkView = out.subarray(offset, offset + chunkSize);
|
|
crypto.getRandomValues(chunkView);
|
|
offset += chunkSize;
|
|
}
|
|
// Ensure alpha channel is fully opaque (255)
|
|
for (let i = 3; i < len; i += 4) out[i] = 255;
|
|
} else {
|
|
// Fallback to Math.random for all bytes
|
|
for (let i = 0; i < len; i++) {
|
|
if ((i + 1) % 4 === 0) out[i] = 255;
|
|
else out[i] = Math.floor(Math.random() * 256);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|