Last active
April 28, 2025 06:53
-
-
Save greggman/786eaae29aef4044d9bef33fe3312d79 to your computer and use it in GitHub Desktop.
WebGL: Draw quads using instancing with position data in f32 texture
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
html, body { | |
margin: 0; | |
height: 100%; | |
} | |
canvas { | |
width: 100%; | |
height: 100%; | |
display: block; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<canvas></canvas> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'https://cdn.jsdelivr.net/npm/@petamoriken/[email protected]/+esm' | |
import 'https://greggman.github.io/webgl-lint/webgl-lint.js'; | |
import * as twgl from 'https://twgljs.org/dist/6.x/twgl-full.module.js'; | |
const gl = document.querySelector('canvas').getContext('webgl2'); | |
const useF32 = false; // false = use INT_10_10_10_2_REV, true = use FLOAT | |
const useRGB = true; // false = use RGBA32F, true = use RGB32F | |
const vs = `#version 300 es | |
uniform sampler2D tex; | |
layout(location = 0) in vec4 px; | |
layout(location = 1) in vec4 py; | |
layout(location = 2) in vec4 pc; | |
out vec3 v_color; | |
void main() { | |
float i = float(gl_InstanceID); | |
float x = texelFetch(tex, ivec2(i, 0), 0).x; | |
float y = texelFetch(tex, ivec2(i, 1), 0).x; | |
gl_Position = vec4(px.x * 0.1 + x, py.y * 0.1 + y, 0, 1); | |
v_color = vec3(pc.z / float(0x200) * 0.5 + 0.5); | |
} | |
` | |
const fs = `#version 300 es | |
precision highp float; | |
in vec3 v_color; | |
out vec4 fragColor; | |
void main() { | |
fragColor = vec4(v_color, 1); | |
} | |
`; | |
const unsigned = v => v > 0 ? v : (0x1000000 + v) & 0x3ff; | |
const r = (min, max) => Math.random() * (max - min) + min; | |
const remap = (v, fromMin, fromMax, toMin, toMax) => (v - fromMin) * (toMax - toMin) / (fromMax - fromMin) + toMin; | |
const toInt10 = v => unsigned(Math.round(remap(v, -1, 1, -0x200, 0x1ff))); | |
const quadData = new ArrayBuffer(6 * 4 * 3); | |
const asF32 = new Float32Array(quadData); | |
const asF16 = new Float16Array(quadData); | |
const asU32 = new Uint32Array(quadData); | |
const quad = [ | |
-1, -1, | |
1, -1, | |
-1, 1, | |
-1, 1, | |
1, -1, | |
1, 1, | |
]; | |
for (let i = 0; i < quad.length; i += 2) { | |
const o = i / 2; | |
if (useF32) { | |
asF32[o * 3 + 0] = quad[i]; | |
asF32[o * 3 + 1] = quad[i + 1]; | |
asF32[o * 3 + 2] = r(-0x200, 0x1ff); | |
} else { | |
const y = toInt10(quad[i + 1]); | |
const c = toInt10(r(-1, 1)); | |
asF16[o * 6 + 0] = quad[i]; | |
asU32[o * 3 + 1] = (y << 20) | (y << 10) | y; | |
asU32[o * 3 + 2] = (c << 20) | (c << 10) | c; | |
} | |
} | |
const vBuf = gl.createBuffer(); | |
gl.bindBuffer(gl.ARRAY_BUFFER, vBuf); | |
gl.bufferData(gl.ARRAY_BUFFER, quadData, gl.STATIC_DRAW); | |
const iBuf = gl.createBuffer(); | |
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuf); | |
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 3, 4, 5]), gl.STATIC_DRAW); | |
const prg = twgl.createProgram(gl, [vs, fs]); | |
const tex = gl.createTexture(); | |
gl.bindTexture(gl.TEXTURE_2D, tex); | |
const numQuads = 15; | |
const positions = new Float32Array(numQuads * 4 * 2); | |
const tMul = useRGB ? 3 : 4; | |
for (let i = 0; i < numQuads; ++i) { | |
const offset0 = (numQuads * 0 + i) * tMul; | |
const offset1 = (numQuads * 1 + i) * tMul; | |
positions[offset0 + 0] = r(-1, 1); | |
positions[offset1 + 0] = r(-1, 1); | |
} | |
gl.texImage2D(gl.TEXTURE_2D, 0, useRGB ? gl.RGB32F : gl.RGBA32F, numQuads, 2, 0, useRGB ? gl.RGB : gl.RGBA, gl.FLOAT, positions); | |
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); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); | |
function render() { | |
gl.clearColor(0.3, 0.2, 0.5, 1); | |
gl.clear(gl.COLOR_BUFFER_BIT); | |
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); | |
gl.useProgram(prg); | |
gl.enableVertexAttribArray(0); | |
gl.enableVertexAttribArray(1); | |
gl.enableVertexAttribArray(2); | |
if (useF32) { | |
gl.vertexAttribPointer(0, 1, gl.FLOAT, false, 12, 0); | |
gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 12, 4); | |
gl.vertexAttribPointer(2, 1, gl.FLOAT, false, 12, 8); | |
} else { | |
gl.vertexAttribPointer(0, 1, gl.HALF_FLOAT, false, 12, 0); | |
gl.vertexAttribPointer(1, 4, gl.INT_2_10_10_10_REV, true, 12, 4); | |
gl.vertexAttribPointer(2, 4, gl.INT_2_10_10_10_REV, false, 12, 8); | |
} | |
gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, numQuads); | |
} | |
const observer = new ResizeObserver(entries => { | |
for (const entry of entries) { | |
const canvas = entry.target; | |
canvas.width = entry.contentBoxSize[0].inlineSize; | |
canvas.height = entry.contentBoxSize[0].blockSize; | |
} | |
render(); | |
}); | |
observer.observe(gl.canvas); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{"name":"WebGL: Draw quads using instancing with position data in f32 texture","settings":{},"filenames":["index.html","index.css","index.js"]} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment