Chapter 12. Framebuffer Objects

In this chapter we describe what framebuffer objects are, how applications can create them, and how applications can use them for rendering to an off-screen buffer or rendering to a texture. We start by discussing why we need framebuffer objects. We then introduce framebuffer objects and discuss what they are, new object types they add to OpenGL ES, and how they differ from EGL surfaces that we read about in Chapter 3, “An Introduction to EGL.” We go on to discuss how to create framebuffer objects; how to specify color, depth, and stencil attachments to a framebuffer object; and then provide examples that demonstrate rendering to a framebuffer object. Last but not least, we discuss performance tips and tricks applications that we should be aware of and use to ensure good performance when using framebuffer objects.

Why Framebuffer Objects?

A rendering context and a drawing surface need to be first created and made current before any OpenGL ES commands can be called by an application. The rendering context and the drawing surface are usually provided by the native windowing system through an API such as EGL. Chapter 3 describes how to create an EGL context and surface and how to attach them to a rendering thread. The rendering context contains appropriate state required for correct operation. The drawing surface provided by the native windowing system can be a surface that will be displayed on the screen, referred to as the window system provided framebuffer, or can be an off-screen surface, referred to as a pbuffer. The calls to create the EGL drawing surfaces let you specify the width and height of the surface in pixels, whether the surface uses color-, depth-, and stencil buffers, and bit depths of these buffers.

By default, OpenGL ES uses the window system provided framebuffer as the drawing surface. If the application is only drawing to an on-screen surface, the window system provided framebuffer is usually sufficient. However, many applications need to render to a texture, and for this using the window system provided framebuffer as your drawing surface is usually not an ideal option. Examples of where render to texture is useful are dynamic reflections and environment-mapping, multipass techniques for depth-of-field, motion blur effects, and post-processing effects.

There are two techniques that applications can use to render to a texture:

  • Implement render to texture by drawing to the window system provided framebuffer and then copy the appropriate region of the framebuffer to the texture. This can be implemented using glCopyTexImage2D and glCopyTexSubImage2D APIs. As the name implies, these APIs perform a copy from the framebuffer to the texture buffer and this copy operation can often adversely impact performance. In addition, this only works if the dimensions of the texture are less than or equal to the dimensions of the framebuffer.

  • Implement render to texture by using a pbuffer that is attached to a texture. We know that a window system provided surface must be attached to a rendering context. This can be inefficient on some implementations that require separate contexts for each pbuffer and window surface. Additionally, switching between window system provided drawables can sometimes require the implementation to completely finish all previous rendering prior to the switch. This can introduce expensive “bubbles” into the rendering pipeline. On such systems, our recommendation is to avoid using pbuffers to render to textures because of the overhead associated with context and window system provided drawable switching.

Neither of these two methods are ideal for rendering to a texture or other off-screen surface. What is needed are APIs that allow applications to directly render to a texture or the ability to create an off-screen surface within the OpenGL ES API and use it as a rendering target. Framebuffer objects and renderbuffer objects allow applications to do exactly this without requiring additional rendering contexts to be created. We no longer have to worry about the overhead of a context and drawable switch that can occur when using window system provided drawables. Framebuffer objects therefore provide a better and more efficient method for rendering to a texture or an off-screen surface.

The Framebuffer objects API supports the following operations:

  • Creating framebuffer objects using OpenGL ES commands only.

  • Creating and using multiple framebuffer objects within a single EGL context; that is, without requiring a rendering context per framebuffer.

  • Creating off-screen color, depth, or stencil renderbuffers and textures, and attaching these to a framebuffer object.

  • Sharing color, depth or stencil buffers across multiple framebuffers.

  • Attaching textures directly to a framebuffer as color or depth and avoiding the need to do a copy operation.

Framebuffer and Renderbuffer Objects

We describe what a renderbuffer and a framebuffer object are, how they differ from window system provided drawables, and when to use a renderbuffer instead of a texture.

A renderbuffer object is a 2D image buffer allocated by the application. The renderbuffer can be used to allocate and store color, depth, or stencil values and can be used as a color, depth, or stencil attachment in a framebuffer object. A renderbuffer is similar to an off-screen window system provided drawable surface, such as a pbuffer. A renderbuffer, however, cannot be directly used as a GL texture.

A framebuffer object (often referred to as an FBO) is a collection of color, depth, and stencil buffer attachment points; state that describes properties such as the size and format of the color, depth, and stencil buffers attached to the FBO; and the names of the texture and renderbuffer objects attached to the FBO. Various 2D images can be attached to the color attachment point in the framebuffer object. These include a renderbuffer object that stores color values, a mip-level of a 2D texture or a cubemap face, or even a mip-level of a 2D slice in a 3D texture. Similarly, various 2D images containing depth values can be attached to the depth attachment point of an FBO. These can include a renderbuffer, a mip-level of a 2D texture or a cubemap face that stores depth values. The only 2D image that can be attached to the stencil attachment point of an FBO is a renderbuffer object that stores stencil values.

Figure 12-1 shows the relationship among framebuffer objects, renderbuffer objects, and textures. Note that there can only be one color, depth, and stencil attachment in a framebuffer object.

Framebuffer Objects, Renderbuffer Objects, and Textures

Figure 12-1. Framebuffer Objects, Renderbuffer Objects, and Textures

Note

The GL_OES_texture_3D optional extension allows a 2D slice of a 3D texture to be used as a framebuffer attachment.

The GL_OES_depth_texture optional extension allows for 2D and cubemap depth textures.

The GL_OES_packed_depth_stencil extension allows the application to use a packed depth stencil texture as a depth and stencil buffer attachment.

Choosing a Renderbuffer Versus a Texture as a Framebuffer Attachment

For render to texture use cases, attach a texture object to the framebuffer object. Examples include rendering to a color buffer that will be used as a color texture, or rendering into a depth buffer that will be used as a depth texture for shadows.

There are also several reasons to use renderbuffers instead of textures. These include the following:

  • Certain image formats do not support texturing, such as stencil index values. A renderbuffer must be used instead.

  • If the image will not be used as a texture, using a renderbuffer may have a performance advantage. This is because the implementation might be able to store the renderbuffer in a much more efficient format, better suited for rendering than for texturing. The implementation can only do so, however, if it knows in advance that the image will not be used as a texture.

Framebuffer Objects Versus EGL Surfaces

The differences between an FBO and the window system provided drawable surface are as follows:

  • Pixel ownership test—This test determines if the pixel at location (xw, yw) in the framebuffer is currently owned by OpenGL ES. This test allows the window system to control which pixels in the framebuffer belong to the current OpenGL ES context. An example would be if a window that is being rendered into by OpenGL ES is obscured. For an application-created framebuffer object, the pixel ownership test always succeeds as the framebuffer object owns all the pixels.

  • The window system might support only double-buffered surfaces. Framebuffer objects, on the other hand, only support single-buffered attachments.

  • Sharing of stencil and depth buffers between framebuffers is possible using framebuffer objects but usually not with the window system provided framebuffer. Stencil and depth buffers and their corresponding state are usually allocated implicitly with the window system provided drawable surface and therefore cannot be shared between drawable surfaces. With application-created framebuffer objects, stencil and depth renderbuffers can be created independently and then associated with a framebuffer object by attaching these buffers to appropriate attachment points in multiple framebuffer objects, if desired.

  • Window system provided framebuffers might support multisampling. Multisample buffers can be specified by using the window system surface creation API. In EGL, multisample buffers can be specified by setting the EGL_SAMPLE_BUFFERS value in EGLconfig used to create the window system provided framebuffer. Application-created framebuffer objects do not support multisample buffers as attachments.

Creating Framebuffer and Renderbuffer Objects

Creating framebuffer and renderbuffer objects is similar to how texture or vertex buffer objects are created in OpenGL ES 2.0.

The glGenRenderbuffers API call is used to allocate renderbuffer object names. This API is described next.

void

glGenRenderbuffers(GLsizei n, GLuint *renderbuffers)

n

number of renderbuffer object names to return

renderbuffers

pointer to an array of n entries, where the allocated renderbuffer objects are returned

glGenRenderbuffers allocates n renderbuffer object names and returns them in renderbuffers. The renderbuffer object names returned by glGenRenderbuffers are unsigned integer numbers other than 0. The renderbuffer names returned are marked in use but do not have any state associated with them. The value 0 is reserved by OpenGL ES and does not refer to a renderbuffer object. Applications trying to modify or query buffer object state for renderbuffer object 0 will generate an appropriate error.

The glGenFramebuffers API call is used to allocate framebuffer object names. This API is described here.

void

glGenFramebuffers(GLsizei n, GLuint *ids)

n

number of framebuffer object names to return

ids

pointer to an array of n entries, where allocated framebuffer objects are returned

glGenFramebuffers allocates n framebuffer object names and returns them in ids. The framebuffer object names returned by glGenFramebuffers are unsigned integer numbers other than 0. The framebuffer names returned are marked in use but do not have any state associated with them. The value 0 is reserved by OpenGL ES and refers to the window system provided framebuffer. Applications trying to modify or query buffer object state for framebuffer object 0 will generate an appropriate error.

Using Renderbuffer Objects

In this section, we describe how to specify the data storage, format, and dimensions of the renderbuffer image. To specify this information for a specific renderbuffer object, we need to make this object the current renderbuffer object. The glBindRenderbuffer command is used to set the current renderbuffer object.

void

glBindRenderbuffer(GLenum target, GLuint renderbuffer)

target

must be set to GL_RENDERBUFFER

renderbuffer

renderbuffer object name

Note that glGenRenderbuffers is not required to assign a renderbuffer object name before it is bound using glBindRenderbuffer. Although it is a good practice to call glGenRenderbuffers, there are lots of applications that specify compile-time constants for their buffers. An application can specify an unused renderbuffer object name to glBindRenderbuffer. However, we do recommend that OpenGL ES applications call glGenRenderbuffers and use renderbuffer object names returned by glGenRenderbuffers instead of specifying their own buffer object names.

The first time the renderbuffer object name is bound by calling glBindRenderbuffer, the renderbuffer object is allocated with the appropriate default state and if the allocation is successful, this allocated object will become the newly bound renderbuffer object.

The following state and default values are associated with a renderbuffer object:

  • Width and height in pixels—The default value is zero.

  • Internal format—This describes the format of the pixels stored in the renderbuffer. It must be a color-, depth-, or stencil-renderable format.

  • Color bit-depth—This is valid only if the internal format is a color-renderable format. The default value is zero.

  • Depth bit-depth—This is valid only if the internal format is a depth-renderable format. The default value is zero.

  • Stencil bit-depth—This is valid only if the internal format is a stencil-renderable format. The default value is zero.

glBindRenderbuffer can also be used to bind to an existing renderbuffer object (i.e., an object that has been assigned and used before and therefore has valid state associated with it). No changes to the state of the newly bound renderbuffer object are made by the bind command.

Once a renderbuffer object is bound, we can specify the dimensions and format of the image stored in the renderbuffer. The glRenderbufferStorage command can be used to specify this information.

void

glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height)

target

must be set to GL_RENDERBUFFER

internalformat

must be a format that can be used as a color buffer, depth buffer, or stencil buffer

The following formats must be supported:

GL_RGB565

GL_RGBA4

GL_RGB5_A1

GL_DEPTH_COMPONENT16

GL_STENCIL_INDEX8

The following formats are optional:

GL_RGB8_OES

GL_RGBA8_OES

GL_DEPTH_COMPONENT24_OES

GL_DEPTH_COMPONENT32_OES

GL_STENCIL_INDEX1_OES

GL_STENCIL_INDEX4_OES

GL_DEPTH24_STENCIL8_OES

width

width of the renderbuffer in pixels; must be <= GL_MAX_RENDERBUFFER_SIZE.

height

height of the renderbuffer in pixels; must be <= GL_MAX_RENDERBUFFER_SIZE.

glRenderbufferStorage looks very similar to glTexImage2D except that no image data is supplied. The width and height of the renderbuffer is specified in pixels and must be values that are smaller than the maximum renderbuffer size supported by the implementation. The minimum size value that must be supported by all OpenGL ES implementations is 1. The actual maximum size supported by the implementation can be queried using the following code example.

GLint maxRenderbufferSize;
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxRenderbufferSize);

The internalformat argument specifies the format that the application would like to use to store pixels in the renderbuffer object.

  • If internalformat is GL_RGB565, GL_RGBA4, GL_RGB5_A1, GL_RGB8_OES, or GL_RGBA8_OES, the renderbuffer stores a color-renderable buffer.

  • If internalformat is GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24_OES, or GL_DEPTH_COMPONENT32_OES, the renderbuffer stores a depth-renderable buffer.

  • If internalformat is GL_STENCIL_INDEX8, GL_STENCIL_INDEX4_OES, or GL_STENCIL_INDEX1_OES, the renderbuffer stores a stencil-renderable buffer.

The renderbuffer object can be attached to the color, depth, or stencil attachment of the framebuffer object without the renderbuffer’s storage format and dimensions specified. The renderbuffer’s storage format and dimensions can be specified before or after the renderbuffer object has been attached to the framebuffer object. This information will, however, need to be correctly specified before the framebuffer object and renderbuffer attachment can be used for rendering.

Note

The GL_RGB8_OES and GL_RGBA8_OES formats can be used if the GL_OES_rgb8_rgba8 extension is supported.

The GL_DEPTH_COMPONENT24_OES or GL_DEPTH_COMPONENT32_OES formats can be used if the GL_OES_depth24 or GL_OES_depth32 extensions are supported, respectively.

The GL_STENCIL_INDEX1_OES or GL_STENCIL_INDEX4_OES formats can be used if the GL_OES_stencil1 or GL_OES_stencil4 extensions are supported, respectively.

The GL_DEPTH24_STENCIL8_OES format can be used if the GL_OES_packed_depth_stencil extension is supported.

Using Framebuffer Objects

We describe how to use framebuffer objects to render to an off-screen buffer (i.e., renderbuffer) or to render to a texture. Before we can use a framebuffer object and specify its attachments, we need to make it the current framebuffer object. The glBindFramebuffer command is used to set the current framebuffer object.

void

glBindFramebuffer(GLenum target, GLuint framebuffer)

target

must be set to GL_FRAMEBUFFER

framebuffer

framebuffer object name

Note that glGenFramebuffers is not required to assign a framebuffer object name before it is bound using glBindFramebuffer. An application can specify an unused framebuffer object name to glBindFramebuffer. However, we do recommend that OpenGL ES applications call glGenFramebuffers and use framebuffer object names returned by glGenFramebuffers instead of specifying their own buffer object names.

The first time a framebuffer object name is bound by calling glBindFramebuffer, the framebuffer object is allocated with appropriate default state, and if the allocation is successful, this allocated object is bound as the current framebuffer object for the rendering context.

The following state is associated with a framebuffer object:

  • Color attachment point—The attachment point for the color buffer.

  • Depth attachment point—The attachment point for the depth buffer.

  • Stencil attachment point—The attachment point for the stencil buffer.

  • Framebuffer completeness status—Whether or not the framebuffer is in a complete state and can be rendered to.

For each attachment point, the following information is specified:

  • Object type—Specifies the type of object that is associated with the attachment point. This can be GL_RENDERBUFFER if a renderbuffer object is attached or GL_TEXTURE if a texture object is attached. The default value is GL_NONE.

  • Object name—Specifies the name of the object attached. This can be either the renderbuffer object name or the texture object name. The default value is 0.

  • Texture level—If a texture object is attached, then this specifies the mip-level of the texture associated with the attachment point. The default value is 0.

  • Texture cubemap face—If a texture object is attached and the texture is a cubemap, then this specifies which one of the six cubemap faces is to be used as the attachment point. The default value is GL_TEXTURE_CUBE_MAP_POSITIVE_X.

  • Texture Z offset—This is an optional value only available if the GL_OES_texture_3D extension is supported by the OpenGL ES 2.0 implementation. It specifies the 2D slice of the 3D texture to be used as the attachment point. The default value is 0.

glBindFramebuffer can also be used to bind to an existing framebuffer object (i.e., an object that has been assigned and used before and therefore has valid state associated with it). No changes are made to the state of the newly bound framebuffer object.

Once a framebuffer object has been bound, the color, depth, and stencil attachments of the currently bound framebuffer object can be set to a renderbuffer object or a texture. As shown in Figure 12-1, the color attachment can be set to a renderbuffer that stores color values, or to a mip-level of a 2D texture or a cubemap face, or to a mip-level of a 2D slice in a 3D texture. The depth attachment can be set to a renderbuffer that stores depth values, to a mip-level of a 2D depth texture, or to a depth cubemap face. The stencil attachment must be set to a renderbuffer that stores stencil values.

Attaching a Renderbuffer as a Framebuffer Attachment

The glFramebufferRenderbuffer command is used to attach a renderbuffer object to a framebuffer attachment point.

void

glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)

target

must be set to GL_FRAMEBUFFER

attachment

attachment must be one of the following enums:

GL_COLOR_ATTACHMENT0

GL_DEPTH_ATTACHMENT

GL_STENCIL_ATTACHMENT

renderbuffertarget

must be set to GL_RENDERBUFFER

renderbuffer

the renderbuffer object that should be used as attachment. renderbuffer must be either zero or the name of an existing renderbuffer object

If glFramebufferRenderbuffer is called with renderbuffer not equal to zero, this renderbuffer object will be used as the new color, depth, or stencil attachment point as specified by value of attachment argument.

The attachment point’s state will be modified to:

  • Object type = GL_RENDERBUFFER

  • Object name = renderbuffer

  • Texture level, texture cubemap face, and texture Z offset = 0

The newly attached renderbuffer object’s state or contents of its buffer do not change.

If glFramebufferRenderbuffer is called with renderbuffer equal to zero, then the color, depth, or stencil buffer as specified by attachment is detached and reset to zero.

Attaching a 2D Texture as a Framebuffer Attachment

The glFramebufferTexture2D command is used to attach a mip-level of a 2D texture or a cubemap face to a framebuffer attachment point. It can be used to attach a texture as a color or depth attachment. A texture as a stencil attachment is not allowed.

void

glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)

target

must be set to GL_FRAMEBUFFER

attachment

attachment must be one of the following enums:

GL_COLOR_ATTACHMENT0

GL_DEPTH_ATTACHMENT

textarget

specifies the texture target. This is the value specified in the textarget argument in glTexImage2D

texture

specifies the texture object

level

specifies the mip-level of texture image

If glFramebufferTexture2D is called with texture not equal to zero, then the color or depth attachment will be set to texture. If, on the other hand, glFramebufferTexture2D generates an error, no change is made to the state of framebuffer.

The attachment point’s state will be modified to:

  • Object type = GL_TEXTURE

  • Object name = texture

  • Texture level = level

  • Texture cubemap face = valid if texture attachment is a cubemap and is one of the following values:

    GL_TEXTURE_CUBE_MAP_POSITIVE_X
    GL_TEXTURE_CUBE_MAP_POSITIVE_Y
    GL_TEXTURE_CUBE_MAP_POSITIVE_Z
    GL_TEXTURE_CUBE_MAP_NEGATIVE_X
    GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
    GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
  • Texture Z offset = 0

The newly attached texture object’s state or contents of its image are not modified by glFramebufferTexture2D. Note that the texture object’s state and image can be modified after it has been attached to a framebuffer object.

If glFramebufferTexture2D is called with texture equal to zero, then the color or depth attachment is detached and reset to zero.

Attaching an Image of a 3D Texture as a Framebuffer Attachment

The glFramebufferTexture3DOES command is used to attach a 2D slice and a specific mip-level of a 3D texture to a framebuffer attachment point. It can only be used to attach a texture as a color attachment. A 3D texture as a depth or stencil attachment is not allowed. Refer to Chapter 9, “Texturing,” for a detailed description of how 3D textures work.

void

glFramebufferTexture3DOES(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset)

target

must be set to GL_FRAMEBUFFER

attachment

attachment must be the following enum:

GL_COLOR_ATTACHMENT0

textarget

specifies the texture target. This is the value specified in the textarget argument in glTexImage3DOES

texture

specifies the texture object

level

specifies the mip-level of texture image

zoffset

specifies the z-offset, which identifies the 2D slice of 3D texture. Must be a value between 0 and (depth – 1), where depth is specified in glTexImage3DOES

If glFramebufferTexture3DOES is called with texture not equal to 0, then the color attachment will be set to texture. If, on the other hand, glFramebufferTexture3DOES generates an error, no change is made to the state of framebuffer.

The attachment point’s state will be modified to:

  • Object type = GL_TEXTURE

  • Object name = texture

  • Texture level = level

  • Texture cubemap face = 0

  • Texture Z offset = zoffset

The newly attached texture object’s state or contents of its image are not modified by glFramebufferTexture3DOES. Note that the texture object’s state and image can be modified after it has been attached to a framebuffer object.

If glFramebufferTexture3DOES is called with texture equal to zero, then the color attachment is detached and reset to zero.

One interesting question arises: What happens if we are rendering into a texture and at the same time use this texture object as a texture in a fragment shader? Will the OpenGL ES implementation generate an error when such a situation arises? In some cases, it is possible for the OpenGL ES implementation to determine if a texture object is being used as a texture input and a framebuffer attachment into which we are currently drawing. glDrawArrays and glDrawElements could then generate an error. To ensure that glDrawArrays and glDrawElements can be executed as fast as possible, however, these checks are not performed. Instead of generating an error, in this case, rendering results are undefined. It is the application’s responsibility to make sure that this situation does not occur.

Checking for Framebuffer Completeness

A framebuffer object needs to be defined as complete before it can be used as rendering target. If the currently bound framebuffer object is not complete, OpenGL ES commands that draw primitives or read pixels will fail and generate an appropriate error that indicates the reason the framebuffer is incomplete.

The rules for a framebuffer object to be considered complete are as follows:

  • Make sure that the color, depth, and stencil attachments are valid. A color attachment is valid if it is zero (i.e., there is no attachment) or if it is a color-renderable renderbuffer object or a texture object. The following formats are color-renderable: GL_RGB565, GL_RGBA4, GL_RGB5_A1, and optionally GL_RGB8_OES and GL_RGBA8_OES if the appropriate extensions are supported. A depth attachment is valid if it is zero or is a depth-renderable renderbuffer object or a depth texture. The following formats are depth-renderable: GL_DEPTH_COMPONENT16 and optionally GL_DEPTH_COMPONENT24_OES and GL_DEPTH_COMPONENT32_OES if the appropriate extensions are supported. A stencil attachment is valid if it is zero or is a stencil-renderable renderbuffer object. The following formats are stencil-renderable: GL_STENCIL_INDEX8 and optionally GL_STENCIL_INDEX1_OES and GL_STENCIL_INDEX4_OES if the appropriate extensions are supported.

  • There is a minimum of one valid attachment. A framebuffer is not complete if it has no attachments as there is nothing to draw into or read from.

  • Valid attachments associated with a framebuffer object must have the same width and height.

  • The combination of color, depth, and stencil internal formats results in a rendering target that can be used as a destination surface for a particular implementation. Even though there is a sufficient list of renderable formats for color, depth, and stencil buffers, not all combinations of formats might be supported by an implementation. For example, an implementation might not be able to support rendering into a surface that uses 16-bit depth and 8-bit stencil buffers. In such a case, a framebuffer object that uses a GL_DEPTH_COMPONENT16 attachment and a GL_STENCIL_INDEX8 stencil attachment will be considered unsupported.

The glCheckFramebufferStatus command can be used to verify if a framebuffer object is complete.

GLenum

glCheckFramebufferStatus(GLenum target)

target

must be set to GL_FRAMEBUFFER

glCheckFramebufferStatus returns 0 if target is not equal to GL_FRAMEBUFFER. If target is equal to GL_FRAMEBUFFER, one of the following enums is returned:

  • GL_FRAMEBUFFER_COMPLETEFramebuffer is complete.

  • GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENTThe framebuffer attachment points are not complete. This might be due to the fact that the required attachment is zero or is not a valid texture or renderbuffer object.

  • GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENTNo valid attachments in the framebuffer.

  • GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONSAttachments do not have the same width and height.

  • GL_FRAMEBUFFER_INCOMPLETE_FORMATSInternal format used by the attachments is not renderable.

  • GL_FRAMEBUFFER_UNSUPPORTEDCombination of internal formats used by attachments in the framebuffer results in a nonrenderable target.

If the currently bound framebuffer object is not complete, attempts to use the framebuffer object for reading and writing pixels will fail. This means that calls to draw primitives such as glDrawArrays, glDrawElements, and commands that read the framebuffer such as glReadPixels and glCopyTexImage2D, glCopyTexSubImage2D, and glCopyTexSubImage3DOES will generate a GL_INVALID_FRAMEBUFFER_OPERATION error.

Deleting Framebuffer and Renderbuffer Objects

After the application is done using renderbuffer objects, they can be deleted. Deleting renderbuffer and framebuffer objects is very similar to deleting texture objects.

Renderbuffer objects are deleted using the glDeleteRenderbuffers API.

void

glDeleteRenderbuffers(GLsizei n, GLuint *renderbuffers)

n

number of renderbuffer object names to delete

renderbuffers

pointer to an array of n renderbuffer object names to be deleted

glDeleteRenderbuffers deletes the renderbuffer objects specified in renderbuffers. Once a renderbuffer object is deleted, it has no state associated with it and is marked as unused and can later be reused as a new renderbuffer object. When deleting a renderbuffer object that is also the currently bound renderbuffer object, the renderbuffer object is deleted and the current renderbuffer binding is reset to zero. If renderbuffer object names specified in renderbuffers are invalid or zero, they are ignored (i.e., no error will be generated). Further if the renderbuffer is attached to the currently bound framebuffer object, it is first detached from the framebuffer and then deleted.

Framebuffer objects are deleted using the glDeleteFramebuffers API.

void

glDeleteFramebuffers(GLsizei n, GLuint *framebuffers)

n

number of framebuffer object names to delete

framebuffers

pointer to an array of n framebuffer object names to be deleted

glDeleteFramebuffers deletes the framebuffer objects specified in framebuffers. Once a framebuffer object is deleted, it has no state associated with it and is marked as unused and can later be reused as a new framebuffer object. When deleting a framebuffer object that is also the currently bound framebuffer object, the framebuffer object is deleted and the current framebuffer binding is reset to zero. If framebuffer object names specified in framebuffers are invalid or zero, they are ignored and no error will be generated.

Deleting Renderbuffer Objects That Are Used as Framebuffer Attachments

What happens if a renderbuffer object being deleted is used as an attachment in a framebuffer object? If the renderbuffer object to be deleted is used as an attachment in the currently bound framebuffer object, glDeleteRenderbuffers will reset the attachment to zero. If the renderbuffer object to be deleted is used as an attachment in framebuffer objects that are not currently bound, then glDeleteRenderbuffers will not reset these attachments to zero. It is the responsibility of the application to detach these deleted renderbuffer objects from the appropriate framebuffer objects.

Reading Pixels and Framebuffer Objects

The glReadPixels command reads pixels from the color buffer and returns them in a user allocated buffer. The color buffer that will be read from is the color buffer allocated by the window system provided framebuffer or the color attachment of the currently bound framebuffer object. Two combinations of format and type arguments in glReadPixels are supported: a format and type of GL_RGB and GL_UNSIGNED_BYTE or implementation-specific format and type values returned by querying GL_IMPLEMENTATION_COLOR_READ_FORMAT and GL_IMPLEMENTATION_COLOR_READ_TYPE. The implementation-specific format and type returned will depend on the format and type of the currently attached color buffer. These values can change if the currently bound framebuffer changes. These must be queried anytime the currently bound framebuffer object changes to determine the correct implementation-specific format and type values that must be passed to glReadPixels.

Examples

Let’s now look at some examples that demonstrate how to use framebuffer objects. Example 12-1 demonstrates how to render to texture using framebuffer objects. In this example we draw to a texture using a framebuffer object. We then use this texture to draw a quad to the window system provided framebuffer (i.e., the screen). Figure 12-2 shows the generated image.

Example 12-1. Render to Texture

GLuint   framebuffer;
GLuint   depthRenderbuffer;
GLuint   texture;
GLint    texWidth = 256, texHeight = 256;
GLint    maxRenderbufferSize;

glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxRenderbufferSize);

// check if GL_MAX_RENDERBUFFER_SIZE is >= texWidth and texHeight
if((maxRenderbufferSize <= texWidth) ||
   (maxRenderbufferSize <= texHeight))
{
   // cannot use framebuffer objects as we need to create
   // a depth buffer as a renderbuffer object
   // return with appropriate error
}

// generate the framebuffer, renderbuffer, and texture object names
glGenFramebuffers(1, &framebuffer);
glGenRenderbuffers(1, &depthRenderbuffer);
glGenTextures(1, &texture);

// bind texture and load the texture mip-level 0
// texels are RGB565
// no texels need to be specified as we are going to draw into
// the texture
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texWidth, texHeight,
             0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

// bind renderbuffer and create a 16-bit depth buffer
// width and height of renderbuffer = width and height of
// the texture
glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
                      texWidth, texHeight);

// bind the framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

// specify texture as color attachment
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                       GL_TEXTURE_2D, texture, 0);

// specify depth_renderbufer as depth attachment
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                          GL_RENDERBUFFER, depthRenderbuffer);

// check for framebuffer complete
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(status == GL_FRAMEBUFFER_COMPLETE)
{
   // render to texture using FBO
   // clear color and depth buffer
   glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   // load uniforms for vertex and fragment shader
   // used to render to FBO. The vertex shader is the
   // ES 1.1 vertex shader described as Example 8-8 in
   // Chapter 8. The fragment shader outputs the color
   // computed by vertex shader as fragment color and
   // is described as Example 1-2 in Chapter 1.
   set_fbo_texture_shader_and_uniforms();

   // drawing commands to the framebuffer object
   draw_teapot();

   // render to window system provided framebuffer
   glBindFramebuffer(GL_FRAMEBUFFER, 0);
   // Use texture to draw to window system provided framebuffer
   // We draw a quad that is the size of the viewport.
   //
   // The vertex shader outputs the vertex position and texture
   // coordinates passed as inputs.
   //
   // The fragment shader uses the texture coordinate to sample
   // the texture and uses this as the per-fragment color value.
   set_screen_shader_and_uniforms();
   draw_screen_quad();
}

// cleanup
glDeleteRenderbuffers(1, &depthRenderbuffer);
glDeleteFramebuffers(1, &framebuffer);
glDeleteTextures(1, &texture);
Render to Color Texture

Figure 12-2. Render to Color Texture

In Example 12-1 we create the framebuffer, texture, and depthRenderbuffer objects using appropriate glGen*** commands. framebuffer uses a color attachment that is a texture object (texture) and a depth attachment that is a renderbuffer object (depthRenderbuffer).

Before we create these objects, we query the maximum renderbuffer size (GL_MAX_RENDERBUFFER_SIZE) to make sure that the maximum renderbuffer size supported by the implementation is less than or equal to the width and height of texture that will be used as a color attachment. This is to ensure that we can create a depth renderbuffer successfully and use it as the depth attachment in framebuffer.

After the objects have been created, we call glBindTexture(texture) to make the texture the currently bound texture object. The texture mip-level is then specified using glTexImage2D. Note that the pixels argument is NULL. This is because we are rendering to the entire texture region and therefore there is no reason to specify any input data as it will get overwritten.

The depthRenderbuffer object is bound using glBindRenderbuffer and glRenderbufferStorage is called to allocate storage for a 16-bit depth buffer.

The framebuffer object is bound using glBindFramebuffer. texture is attached as a color attachment to framebuffer and depthRenderbuffer is attached as a depth attachment to framebuffer.

We now check if the framebuffer status is complete before we begin drawing into framebuffer. Once framebuffer rendering is complete, we reset the currently bound framebuffer to the window system provided framebuffer by calling glBindFramebuffer(GL_FRAMEBUFFER, 0). We can now use texture that was used as a render target in framebuffer to draw to the window system provided framebuffer.

In Example 12-1, the depth buffer attachment to framebuffer was a renderbuffer object. In Example 12-2 we look at how to use a depth texture as a depth buffer attachment to framebuffer. This feature is available if the OES_depth_texture extension is supported by the implementation. Applications can render to the depth texture used as a framebuffer attachment from the light source. The rendered depth texture can then be used as a shadow map to calculate the percentage in shadow for each fragment. Figure 12-3 shows the generated image.

Example 12-2. Render to Depth Texture

#define COLOR_TEXTURE    0
#define DEPTH_TEXTURE    1

GLuint   framebuffer;
GLuint   textures[2];
GLint    texWidth = 256, texHeight = 256;

// generate the framebuffer, & texture object names
glGenFramebuffers(1, &framebuffer);
glGenTextures(2, textures);

// bind  color texture and load the texture mip-level 0
// texels are RGB565
// no texels need to specified as we are going to draw into
// the texture
glBindTexture(GL_TEXTURE_2D, textures[COLOR_TEXTURE]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texWidth, texHeight,
             0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

// bind  depth texture and load the texture mip-level 0
// no texels need to specified as we are going to draw into
// the texture
glBindTexture(GL_TEXTURE_2D, textures[DEPTH_TEXTURE]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, texWidth,
             texHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT,
             NULL);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);



// bind the framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

// specify texture as color attachment
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                       GL_TEXTURE_2D, textures[COLOR_TEXTURE], 0);

// specify texture as depth attachment
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                       GL_TEXTURE_2D, textures[DEPTH_TEXTURE], 0);

// check for framebuffer complete
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status == GL_FRAMEBUFFER_COMPLETE)
{
   // render to color & depth textures using FBO
   // clear color & depth buffer
   glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   // load uniforms for vertex and fragment shader
   // used to render to FBO. The vertex shader is the
   // ES 1.1 vertex shader described as Example 8-8 in
   // Chapter 8. The fragment shader outputs the color
   // computed by vertex shader as fragment color and
   // is described as Example 1-2 in Chapter 1.
   set_fbo_texture_shader_and_uniforms();

   // drawing commands to the framebuffer object
   draw_teapot();

   // render to window system provided framebuffer
   glBindFramebuffer(GL_FRAMEBUFFER, 0);

   // Use depth texture to draw to window system framebuffer
   // We draw a quad that is the size of the viewport.
   //
   // The vertex shader outputs the vertex position and texture
   // coordinates passed as inputs.
   //
   // The fragment shader uses the texture coordinate to sample
   // the texture and uses this as the per-fragment color value.
   set_screen_shader_and_uniforms();
   draw_screen_quad();
}

// cleanup
glDeleteRenderbuffers(1, &depthRenderbuffer);
glDeleteFramebuffers(1, &framebuffer);
glDeleteTextures(1, &texture);
Render to Depth Texture

Figure 12-3. Render to Depth Texture

Note

The width and height of the off-screen renderbuffers do not have to be a power of two. OpenGL ES supports non-power-of-two textures provided the texture only uses the base mip-level and the addressing mode is GL_CLAMP_TO_EDGE. These rules are relaxed by the OES_texture_npot extension.

Performance Tips and Tricks

Here we discuss some performance tips that developers should carefully consider when using framebuffer objects.

  • Avoid switching between rendering to window system provided framebuffer and rendering to framebuffer objects frequently. This is an issue for handheld OpenGL ES 2.0 implementations as many of these implementations use a tile-based rendering architecture. With a tile-based rendering architecture, dedicated internal memory is used to store the color, depth, and stencil values for a tile (i.e., region) of the framebuffer. The internal memory is used as it is much more efficient in power, and has better memory latency and bandwidth compared with going to external memory. After rendering to a tile is completed, the tile is written out to device (or system) memory. Every time you switch from one rendering target to another, the appropriate texture and renderbuffer attachments will need to be rendered, saved, and restored. This can become quite expensive. The best method would be to render to the appropriate framebuffers in the scene first and then render to the window system provided framebuffer followed by the eglSwapBuffers command to swap the display buffer.

  • Don’t create and destroy framebuffer and renderbuffer objects (or any other large data objects for that matter) per frame.

  • Try to avoid modifying textures (using glTexImage2D, glTexSubImage2D, glCopyTexImage2D, etc.) that are attachments to framebuffer objects used as rendering targets.

  • Set pixels argument in glTexImage2D and glTexImage3DOES to NULL if the entire texture image will be rendered as the original data will not be used anyway. Make sure you do a glClear to clear the texture image before drawing to the texture if you are expecting the image to have any defined pixel values in it.

  • Share depth and stencil renderbuffers as attachments used by framebuffer objects wherever possible to keep the memory footprint requirement to a minimum. We recognize that this has limited use as the width and height of these buffers have to be the same. In a future version of OpenGL ES, the rule that width and height of various attachments of a framebuffer object must be equal might be relaxed, making sharing easier.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset