Skip to content

Instantly share code, notes, and snippets.

@greggman
Created March 5, 2026 18:04
Show Gist options
  • Select an option

  • Save greggman/43d31c07ac3b8dbe6e85cf92bad97161 to your computer and use it in GitHub Desktop.

Select an option

Save greggman/43d31c07ac3b8dbe6e85cf92bad97161 to your computer and use it in GitHub Desktop.
WebGL2: render to RGBA16F
:root { color-scheme: light dark; }
canvas { border: 1px solid gray; }
pre { margin: 0; }
<canvas></canvas>
const gl = document.querySelector('canvas').getContext('webgl2');
getExtension(gl, 'EXT_color_buffer_half_float');
getExtension(gl, 'EXT_color_buffer_float');
const vs = `#version 300 es
out vec4 v_color;
void main() {
float x = float(gl_VertexID) * 0.5 - 0.5;
float y = float(gl_VertexID % 2) - 0.5;
gl_Position = vec4(x, y, 0, 1);
v_color = vec4(1);
v_color[gl_VertexID] = 0.0;
}
`;
const fs = `#version 300 es
precision highp float;
in vec4 v_color;
out vec4 fragColor;
void main() {
fragColor = v_color;
}
`;
const blitVS = `#version 300 es
out vec2 v_color;
void main() {
vec2 points[3] = vec2[](vec2(-1, 3), vec2(3, -1), vec2(-1, -1));
gl_Position = vec4(points[gl_VertexID], 0, 1);
}
`;
const blitFS = `#version 300 es
precision highp float;
uniform sampler2D tex;
out vec4 fragColor;
void main() {
fragColor = texelFetch(tex, ivec2(gl_FragCoord.xy), 0);
}
`;
const blitPrg = createProgram(gl, blitVS, blitFS);
const drawPrg = createProgram(gl, vs, fs);
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, gl.canvas.width, gl.canvas.height, 0, gl.RGBA, gl.FLOAT, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
log(`checkFramebufferStatus: ${glEnumToString(gl, gl.checkFramebufferStatus(gl.FRAMEBUFFER))}`);
gl.useProgram(drawPrg);
gl.drawArrays(gl.TRIANGLES, 0, 3);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.useProgram(blitPrg);
gl.drawArrays(gl.TRIANGLES, 0, 3);
log('error:', glErrorToString(gl, gl.getError()));
function getExtension(gl, name) {
const ext = gl.getExtension(name);
log(`${name}: ${ext ? 'exists' : 'not available'}`);
}
function createShader(gl, type, src) {
const shader = gl.createShader(type);
gl.shaderSource(shader, src);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw new Error(gl.getShaderInfoLog(shader));
}
return shader;
}
function createProgram(gl, vs, fs, tf) {
const program = gl.createProgram();
gl.attachShader(program, createShader(gl, gl.VERTEX_SHADER, vs));
gl.attachShader(program, createShader(gl, gl.FRAGMENT_SHADER, fs));
if (tf) {
gl.transformFeedbackVaryings(program, tf, gl.INTERLEAVED_ATTRIBS); // gl.SEPARATE_ATTRIBS);
}
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
throw new Error(gl.getProgramInfoLog(program));
}
return program;
}
function log(...args) {
const elem = document.createElement('pre');
elem.textContent = args.join(' ');
document.body.appendChild(elem);
}
function glEnumToString(gl, value) {
const keys = [];
for (const key in gl) {
if (gl[key] === value) {
keys.push(key);
}
}
return keys.length ? keys.join(' | ') : `0x${value.toString(16)}`;
}
function glErrorToString(gl, value) {
return value === 0 ? 'NONE' : glEnumToString(gl, value);
}
{"name":"WebGL2: render to RGBA16F","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