Framebuffer

From OpenGL Wiki
Jump to navigation Jump to search

A Framebuffer is a collection of buffers that can be used as the destination for rendering. OpenGL has two kinds of framebuffers: the Default Framebuffer, which is provided by the OpenGL Context; and user-created framebuffers called Framebuffer Objects (FBOs). The buffers for default framebuffers are part of the context and usually represent a window or display device. The buffers for FBOs reference images from either Textures or Renderbuffers; they are never directly visible.

Note that the term "buffer" here refers to a specific location in the framebuffer. An image may or may not be associated with a particular buffer in a framebuffer. Buffers in FBOs are also called "attachment points"; they're the locations where images can be attached.

Default framebuffers cannot change their buffer attachments, but a particular default framebuffer may not have images associated with certain buffers. For example the GL_BACK_RIGHT buffer will only have an image if the default framebuffer is double-buffered and uses stereoscopic 3D.

The default framebuffer's buffer names are separate from framebuffer object buffer names.

Bind points[edit]

glBindFramebuffer is used to bind framebuffers to the context. They can be bound to one of two targets: GL_DRAW_FRAMEBUFFER and GL_READ_FRAMEBUFFER. The draw framebuffer is used as the destination for rendering, clearing, and other writing operations. The read framebuffer is used as the source for reading operations.

Binding to the GL_FRAMEBUFFER target is equivalent to binding that framebuffer to both GL_DRAW_FRAMEBUFFER and GL_READ_FRAMEBUFFER. Note that most other uses of GL_FRAMEBUFFER mean the draw framebuffer; this is the case when it means both.

Colorspace[edit]

Color Image Formats can be in a linear or sRGB colorspace. Normally, sRGB images perform color correction, such that texture reads from them will always convert them to linear RGB. However, when writing values from a Fragment Shader to the image, a question arises: in what colorspace are the values written by the shader?

When the output buffer is linear RGB, the answer is assumed to be linear. But if we are writing to an sRGB image, it is entirely reasonable for a shader to want to write linear values which will be automatically converted to sRGB for storage. However, at other times, it is entirely reasonable for a shader to want to write sRGB values directly, and therefore want to store the written provided without any conversion.

The fragment shader is not given the power to control this, as the controls for it depend partially on state external to the fragment shader (namely, the colorspace of the image buffer being written to). Instead, this is made a global state value.

When GL_FRAMEBUFFER_SRGB is disabled, the system assumes that the color written by the fragment shader is in whatever colorspace the image it is being written to is. Therefore, no colorspace correction is performed.

If GL_FRAMEBUFFER_SRGB is enabled however, then if the destination image is in the sRGB colorspace (as queried through glGetFramebufferAttachmentParameter(GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)), then it will assume the shader's output is in the linear RGB colorspace. It will therefore convert the output from linear RGB to sRGB.

Note that Logical Operations are disabled for any sRGB buffer when GL_FRAMEBUFFER_SRGB is enabled.

Blending[edit]

Blending interacts with sRGB colorspace in destination images. The sRGB colorspace is non-linear, and thus linearly interpolating between non-linear colorspaces generally leads to colors that are not accurate. The correct operation is to linearize the destination color, do the blending in linear RGB space, and then convert back to sRGB.

If GL_FRAMEBUFFER_SRGB is enabled, then the appropriate correction will be done for sRGB destination images. That is, if a particular destination image is sRGB, the destination value will be converted to linear RGB, blended with the linear source value via the current blend functions, and the result will be converted back to sRGB for writing to the destination image.

If GL_FRAMEBUFFER_SRGB is not enabled, then it is assumed that the user knows what they are doing. Therefore, blending against an sRGB image when GL_FRAMEBUFFER_SRGB is not enabled will not perform any of the above correction. This is generally not a good idea, even if you are writing sRGB color values from the Fragment Shader.

Read color buffer[edit]

Certain OpenGL operations can read pixel data from the color buffer. These operations are:

All of these operations read from the framebuffer bound to GL_READ_FRAMEBUFFER. However, because framebuffers often have multiple color images that could be read from, there is a selector in the framebuffer object that defines which color buffer these operations read from.

The framebuffer's read buffer is specified by:

void glReadBuffer(GLenum mode​);
Warning: This sets the read buffer for the framebuffer currently bound to GL_READ_FRAMEBUFFER. So make sure that the framebuffer you want is bound to that buffer.

The read buffer is part of the framebuffer's state, so each framebuffer will remember its previously set read buffer.

If the GL_READ_FRAMEBUFFER is the default framebuffer, then mode​ must be the name of a color buffer name. It may be one of the multiple buffer aliases, which degrades as specified. If the read framebuffer is an FBO, then mode​ must be GL_COLOR_ATTACHMENTi, for one of the legal values of i.

mode​ may also be GL_NONE, which indicates that no reading can be performed from color buffers on this framebuffer. Any attempt to perform read operations will fail with an error (GL_INVALID_OPERATION).

Draw color buffers[edit]

Much as with the read buffer, framebuffers have a set of draw buffers where writing operations write to. Unlike the read buffer, there are multiple draw buffers, which allow writing operations to write values to different buffers in the framebuffer at the same time. Fragment shader color outputs allow a fragment shader to define fragment color outputs, for example.

The framebuffer therefore has a table of draw buffers, which are indexed on the half-open range [0, GL_MAX_DRAW_BUFFERS). The framebuffer's table maps from these indices to named color buffers in the framebuffer. To set this mapping table (on the framebuffer bound to GL_DRAW_FRAMEBUFFER), use this function:

  void glDrawBuffers( GLsizei n​, const GLenum *bufs​ );

This function sets the first n​ entries of the mapping table. The indices of the enumerators correspond to the output fragment colors from the fragment shader. Thus, n​ can only be as large as GL_MAX_DRAW_BUFFERS. The entries in the bufs​ array are enumerators referring to buffer names in the framebuffer. All entries after n​ are set to GL_NONE.

The values in bufs​ must name specific color buffers in the framebuffer. For the default framebuffer, buffer names cannot be the one of the multiple buffer aliases; you must use GL_BACK_LEFT rather than GL_BACK. Framebuffer Objects use GL_COLOR_ATTACHMENTi buffer names. An entry in the list can be GL_NONE, which means that the output (if the shader outputs a value for it at all) is discarded.

If you are only setting up one draw buffer, you may use glDrawBuffer. It takes one enumeration value and sets the fragment color 0 to draw to that buffer. All other draw buffer indices are set to GL_NONE. For the default framebuffer, the multiple buffer aliases can be used (though it is not advised to do so). They will cause drawing operations to write to all of the specified buffers.

The state set by glDrawBuffers is part of the state of the framebuffer. So you can generally set this up once and leave it set.

Buffer clearing[edit]

Images in a framebuffer may be cleared to a particular value. Clearing is affected by the following state:

  • The Write Mask. Only unmasked components will be changed by a clear call.
  • The Pixel Ownership Test, which matters only for the Default Framebuffer. Pixels that are not owned will have undefined values.
  • The Scissor Test. If the scissor test is enabled, then the clear region is bounded by the scissor rectangle.
  • The Rasterizer Discard state. If discarding is enabled, all clearing commands are ignored.

Framebuffer image clearing commands are Rendering Commands. Therefore, they are affected by Conditional Rendering.

Clearing can be done in one of two ways. The old way can clear multiple buffers in a single call:

void glClear(GLbitfield mask​);

This will clear the current GL_DRAW_FRAMEBUFFER. The mask​ field is a bitmask that defines which kinds of buffers to clear. It can be a combination of GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT. When GL_COLOR_BUFFER_BIT is cleared, all of the active draw buffers are cleared.

The values that the given buffers are cleared to are set beforehand via OpenGL state functions:

void glClearColor(GLfloat red​, GLfloat green​, GLfloat blue​, GLfloat alpha​);

void glClearDepth(GLdouble depth​);

void glClearStencil(GLint s​);

These functions set the values that will be used to clear the appropriate buffers. Note that you must call these before calling glClear. depth​ will be clamped to the range [0, 1]. s​, the stencil index, will be clamped to the valid integer range for the stencil buffer's image format.

Note that glClearColor takes floating-point values. Yet it is perfectly legal to use integer Image Formats for images in framebuffers. Attempting to clear integer buffers with floating-point data will not work.

Instead, you must clear each buffer individually with this suite of functions:

void glClearBufferiv(GLenum buffer​, GLint drawBuffer​, const GLint * value​);

void glClearBufferuiv(GLenum buffer​, GLint drawBuffer​, const GLuint * value​);

void glClearBufferfv(GLenum buffer​, GLint drawBuffer​, const GLfloat * value​);

void glClearBufferfi(GLenum buffer​, GLint drawBuffer​, GLfloat depth​, GLint stencil​);

This will clear a buffer in the current GL_DRAW_FRAMEBUFFER. buffer​ is either GL_COLOR, GL_DEPTH, GL_DEPTH_STENCIL, or GL_STENCIL.

When clearing a GL_COLOR buffer, the value​ must have 4 components, even if the target image has fewer components. When clearing the GL_DEPTH buffer, you must use glClearBufferfv, and the depth value (a single float) will be clamped to [0, 1]. When using GL_STENCIL, you must use glClearBufferiv, and the stencil value (a single integer) will be clamped to the range of the stencil image format's precision. When using GL_DEPTH_STENCIL, you must use glClearBufferfi, and depth​ and stencil​ will each be clamped as appropriate for their value.

The drawbuffer​ specifies the draw buffer index for the buffer to clear. This is only relevant if the buffer​ is GL_COLOR; in all other cases it must be 0. Note that this is the draw buffer index, not one of the GL_COLOR_ATTACHMENTi values. So 0 represents whatever the first buffer named by glDrawBuffers is.

Warning: Buffer clearing, in all its forms, will respect the current state of the Scissor Test. If you wish to clear the entire buffer, you must make sure the scissor test is disabled.

Buffer reading[edit]

Pixel data can be read from a framebuffer and stored into CPU memory (or a buffer object). The framebuffer being read from is the framebuffer bound to GL_READ_FRAMEBUFFER; remember that binding to GL_FRAMEBUFFER binds to both the read and the draw.

To begin reading pixels, use this command:

void glReadPixels(GLint x​, GLint y​, GLsizei width​, GLsizei height​, GLenum format​, GLenum type​, GLvoid * data​)

This performs a Pixel Transfer read operation; as such, the destination data​ can be an offset into a Pixel Buffer Object if you so desire.

Framebuffers have many buffers to read from. Which buffer is read from depends in part on the format​ parameter. If format​ is GL_DEPTH_COMPONENT, then the depth buffer is read from. If it is GL_STENCIL_INDEX, then the stencil buffer is read from. If it is GL_DEPTH_STENCIL then both the depth and stencil buffers are read from.

If format​ is a color format, then the current read color buffer is used. If the current read buffer is GL_NONE, then a GL_INVALID_OPERATION error will occur.

Read color clamping[edit]

The color values read via glReadPixels may be clamped to the [0, 1] range. This is controlled via this function:

void glClampColor(GLenum target​, GLenum clamp​);

The target​ must be GL_CLAMP_READ_COLOR​. The clamp​ can be set to one of the following:

  • GL_TRUE​: Clamping is always on, no matter what the format​ or type​ parameters of the read pixels call.
  • GL_FALSE​: Clamping is always off, no matter what the format​ or type​ parameters of the read pixels call.
  • GL_FIXED_ONLY​: Clamping is only on if the type of the image being read is a normalized signed or unsigned value.

Note that the clamping behavior is not framebuffer object state. It will not be stored with the current framebuffer.

Invalidation[edit]

Framebuffer Invalidation
Core in version 4.6
Core since version 4.3
Core ARB extension ARB_invalidate_subdata

The contents of a framebuffer can be invalidated. This means that the application is no longer interested in whatever pixel data happens to be stored in any images in that part of the framebuffer. Invalidation is useful, as it allows the implementation to allocate new memory if asynchronous commands are working with the previously invalidated framebuffer images. In particular, invalidation after asynchronous pixel reads are quite useful.

To invalidate the framebuffer as a whole, the following function is used:

glInvalidateFramebuffer(GLenum target​, GLsizei numAttachments​, const GLenum * attachments​)

The framebuffer bound to target​ will be invalidated. The specific images in the framebuffer to be invalidated are specified by the array attachments​, which must have numAttachments​ entries in it. The entries in the array must be names of specific images in the framebuffer. For user-created Framebuffer Objects, these must be the names of one of the available attachment points.

For the default framebuffer, the names can be one of the images in the default framebuffer; none of the color buffer's aliases work here. The default framebuffer can also use the names GL_COLOR​ (which means GL_BACK_LEFT​ if double-buffered and GL_FRONT_LEFT​ if single-bufferred.

Instead of invalidating whole sets of images in the framebuffer, a portion of the framebuffer can be invalidated instead:

glInvalidateSubFramebuffer(GLenum target​, GLsizei numAttachments​, const GLenum * attachments​, GLint x​, GLint y​, GLint width​, GLint height​)

The x​, y​, width​, and height​ parameters specify a rectangular region of the specified images to invalidate.

After invalidation, the contents of pixels stored in those areas are undefined. You can clear them to a set value after invalidation.

Blitting[edit]

Framebuffers can be bound to two separate targets with glBindFramebuffer: GL_DRAW_FRAMEBUFFER and GL_READ_FRAMEBUFFER. Binding to GL_FRAMEBUFFER is equivalent to binding the framebuffer to both.

The reason for the separation of these targets is to allow data in one framebuffer to be blitted to another framebuffer.

A blit operation is a special form of copy operation; it copies a rectangular area of pixels from one framebuffer to another. This function also has some very specific properties with regard to multisampling.

Framebuffer blitting commands are considered Rendering Commands. Therefore, they are affected by Conditional Rendering. However, they do not invoke most parts of the Rendering Pipeline. Blits are affected by the Scissor Test (they always use viewport index 0) as well as the Pixel Ownership Test, but are not affected by the Write Mask, Depth Test or any other parts of the rendering pipeline.

You bind the source framebuffer to GL_READ_FRAMEBUFFER, then bind the destination framebuffer to GL_DRAW_FRAMEBUFFER. The read framebuffer is the source of the blit, and the draw framebuffer is the destination. The read and draw framebuffers can be the same.

After binding the framebuffers, you call this function:

void glBlitFramebuffer(
     GLint srcX0​, GLint srcY0​, GLint srcX1​, GLint srcY1​,
     GLint dstX0​, GLint dstY0​, GLint dstX1​, GLint dstY1​,
     GLbitfield mask​, GLenum filter​);

The pixels in the rectangular area specified by the src​ values are copied to the rectangular area specified by the dst​ values. The mask​ parameter is a bitfield that specifies which kinds of buffers you want copied: GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT, or some combination. The filter​ parameter specifies how you want filtering performed if the two rectangles are not the same size.

One thing to keep in mind is this: when using GL_COLOR_BUFFER_BIT​, the only colors read will come from the read color buffer in the read FBO, specified by glReadBuffer. The colors written will only go to the draw color buffers in the write FBO, specified by glDrawBuffers. If multiple draw buffers are specified, then multiple color buffers are updated with the same data.

The depth and stencil buffers of the source framebuffers are blitted to the depth and stencil buffers of the destination if the mask​ specifies them.

Note that it is perfectly valid to blit from or to the Default Framebuffer.

If one (or both) of the framebuffers is a layered framebuffer (blitting fails if the framebuffers are incomplete. So they will be either all layered or all not layered), then blitting happens rather oddly. Only layer 0 will be read and only layer 0 will be written. This is true even if both the read and draw FBOs are layered and have the same number of layers. If you want to blit other layers, you will have to bind those layers to some other FBO and blit from/to that new FBO.

Format Considerations[edit]

Blitting is not the same as performing a pixel transfer or a texture copy. The conversion between source and destination format is more limited. Blitting depth and stencil buffers works as expected: values are converted from one bitdepth to the other as needed. Conversion between color formats is different.

A blit operation can only convert between formats within 3 groups. Signed integral and unsigned integral formats make up two groups, with all normalized and floating-point formats making up the third. Thus, it is legal to blit from an GL_RGBA8 buffer to a GL_RGBA32F and vice versa. But it is not legal to blit a GL_RGBA8 from or to a GL_RGBA8I format image.

The data during blitting is converted according to simple rules. Blitting from a floating-point format to a normalized integer format will cause clamping, either to [0, 1] for unsigned normalized or [-1, 1] for signed normalized.

Multisampling Considerations[edit]

Multisampling is supported with the Default Framebuffer (through WGL/GLX_multisample) and/or Framebuffer Objects (through multisampled renderbuffers or textures, where supported).

As explained in the article on Multisampling, a multisampled buffer must be resolved into a single sample before it can be displayed. When the default framebuffer uses multisampling, this resolving operation is automatic, occurring during framebuffer swapping (though reading from the framebuffer can cause it to happen anyway).

Each framebuffer has a specific number of samples; for Framebuffer Objects, they cannot be framebuffer-complete if all of the attached images do not have the same number of samples. A sample count of zero represents a framebuffer that is not multisampled.

If you perform a blit operation and and at least one of the framebuffers is multisampled, then the source and destination sizes must be the same. That is, you cannot do multisampled blits and rescaling at the same time.

If you blit between two framebuffers with the same number of samples, the copy is done directly; the destination buffer gets the same information the source had.

It is an error to blit between framebuffers with different numbers of samples, unless one of the framebuffers has zero samples.

Blitting from a non-multisampled framebuffer to a multisampled framebuffer causes all of samples in a pixel of the draw framebuffer to get the same value from the corresponding pixel in the read framebuffer.

Blitting from a multisampled framebuffer to a non-multisampled framebuffer performs a multisample resolve operation, computing the value of a draw framebuffer's pixel from all of the samples in the corresponding pixel of the read framebuffer. This explicit resolve operation is very useful when wanting to display multisampled buffers.

As with all multisample behavior, none of this works at all unless glEnable(GL_MULTISAMPLE) is in effect (which is the default).