Framebuffers

You may need to read the article about textures before this one.


A framebuffer (also called a "framebuffer object" or "FBO") can be thought of as a collection of attachments, where each attachment is a buffer with a specific purpose. They can be used to render to textures and renderbuffers, which are objects that contain images. Renderbuffers differ from textures in that they support more formats but can't be sampled. Renderbuffers also support multisampling, which is a process for reducing aliasing, which is an effect that reduces the quality of images that are under-sampled. This means that multisampling is a form of anti-aliasing. As the name implies, renderbuffers are optimized for use as render targets.

A WebGLRenderbuffer can be created with createRenderbuffer.

const renderbuffer = gl.createRenderbuffer();

Like textures and buffers, renderbuffers must be bound to a binding point with bindRenderbuffer in order to be accessed by functions. Renderbuffers have only one binding point (RENDERBUFFER). The renderbuffer's data store can be created and initialized with renderbufferStorage.

gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT24, 0x100, 0x100);

A WebGLFramebuffer can be created with createFramebuffer.

const framebuffer = gl.createFramebuffer();

Like textures and buffers, framebuffers must be bound to a binding point with bindFramebuffer in order to be accessed by functions. Framebuffers have three binding points:

In other words, functions that modify what is being rendered (such as clear, drawElements, and drawArrays) will always modify the currently-bound DRAW_FRAMEBUFFER. There is a default framebuffer that is rendered to the canvas. When no framebuffer is explicitly attached to any of the framebuffer binding points, the default framebuffer is used. In order to use the default framebuffer, simply attach null to the desired binding point.

Attachments can be added using framebufferTexture2D, framebufferRenderbuffer, and framebufferTextureLayer. Every attachment in a framebuffer must have the same dimensions.

gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(
	gl.FRAMEBUFFER,
	gl.COLOR_ATTACHMENT0,
	gl.TEXTURE_2D,
	texture,
	0
);

Rendering to Textures

Rather than writing to the default framebuffer and having that data rendered to the rendering context, it is possible to write to a custom framebuffer to store that data in textures or renderbuffers.

Notice that framebuffers don't have all of the attachments that the default one does by default. For example, in order to use depth testing in a framebuffer, it needs to be given a depth attachment (DEPTH_ATTACHMENT). You'll also usually want at least one color attachment (COLOR_ATTACHMENT0).

const color = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, color);
gl.texImage2D(
	gl.TEXTURE_2D,
	0,
	gl.RGBA,
	0x100,
	0x100,
	0,
	gl.RGBA,
	gl.UNSIGNED_BYTE,
	null
);

const depth = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, depth);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT24, 0x100, 0x100);

const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(
	gl.FRAMEBUFFER,
	gl.COLOR_ATTACHMENT0,
	gl.TEXTURE_2D,
	color,
	0
);
gl.framebufferRenderbuffer(
	gl.FRAMEBUFFER,
	gl.DEPTH_ATTACHMENT,
	gl.RENDERBUFFER,
	depth
);

Framebuffers can have one depth attachment, one stencil attachment, and an implementation-defined number of color attachments. Color attachments can be set as draw buffers with drawBuffers, which means that fragment colors will be written into them corresponding to their layout in the fragment shader, with each output variable corresponding to a different draw buffer. Similarly, one color attachment can be set as the read buffer with readBuffer, which means that it will be used as the pixel source for subsequent read operations. By default, only the first color attachment is set as a draw buffer, and the first color attachment is also set as the read buffer.


The next article is about lighting.