A framebuffer object redirects drawing from the canvas into textures that the next pass can sample: mirrors, post-processing (Post-Processing and Particles), deferred lighting (Deferred Rendering) and shadow maps. The seventeenth part of webgl-helper.js builds one:
// Section 3.13: an offscreen framebuffer: an optional RGBA8 color texture and a 24-bit depth
// texture set to compare for sampler2DShadow; throws unless the framebuffer is complete
GLH.framebuffer = function (gl, width, height, { color = true } = {}) {
const fbo = gl.createFramebuffer(), T = gl.TEXTURE_2D, F = gl.FRAMEBUFFER;
const texture = (format, attachment) => {
const t = gl.createTexture();
gl.bindTexture(T, t);
gl.texStorage2D(T, 1, format, width, height); // one level, fixed format
gl.texParameteri(T, gl.TEXTURE_MIN_FILTER, gl.LINEAR); // no mipmaps
gl.texParameteri(T, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(T, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.framebufferTexture2D(F, attachment, T, t, 0);
return t;
};
gl.bindFramebuffer(F, fbo);
const colorTexture = color ? texture(gl.RGBA8, gl.COLOR_ATTACHMENT0) : null;
if (!color) gl.drawBuffers([gl.NONE]); // depth only
const depth = texture(gl.DEPTH_COMPONENT24, gl.DEPTH_ATTACHMENT);
gl.texParameteri(T, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE);
const status = gl.checkFramebufferStatus(F);
gl.bindFramebuffer(F, null);
if (status !== gl.FRAMEBUFFER_COMPLETE) throw new Error(`Incomplete framebuffer: ${status}`);
return { fbo, color: colorTexture, depth, width, height };
};