14 Rasterization and Operations on the Framebuffer
14.010 How do I obtain the address of the OpenGL
framebuffer, so I can write directly to it?
OpenGL doesn't provide a standard mechanism
to let an application obtain the address of the framebuffer.
If an implementation allows this, it's through an extension.
Typically, programmers who write graphics
programs for a single standard graphics hardware format, such
as the VGA standard under Microsoft Windows, will want the
framebuffer's address. The programmers need to understand
that OpenGL is designed to run on a wide variety of graphics
hardware, many of which don't run on Microsoft Windows and
therefore, don't support any kind of standard framebuffer
format. Because a programmer will likely be unfamiliar with
this proprietary framebuffer layout, writing directly to it
would produce unpredictable results. Furthermore, some OpenGL
devices might not have a framebuffer that the CPU can address.
You can read the contents of the color,
depth, and stencil buffers with the glReadPixels() command.
Likewise, glDrawPixels() and glCopyPixels() are available for
sending images to and BLTing images around in the OpenGL
buffers.
14.015 How do I use glDrawPixels() and
glReadPixels()?
glDrawPixels() and glReadPixels() write and
read rectangular areas to and from the framebuffer,
respectively. Also, you can access stencil and depth buffer
information with the format parameter. Single pixels
can be written or read by specifying width and height
parameters of 1.
glDrawPixels() draws pixel data with the
current raster position at the lower left corner. Problems
using glDrawPixels() typically occur because the raster
position is set incorrectly. When the raster position is set
with the glRasterPos*() function, it is transformed as if it
were a 3D vertex. Then the glDrawPixels() data is written to
the resulting device coordinate raster position. (This allows
you to tie pixel arrays and bitmap data to positions in 3D
space).
When the raster position is outside the
view volume, it's clipped and the glDrawPixels() call isn't
rendered. This occurs even when part of the glDrawPixels()
data would be visible. Here's info on how to render when the raster
position is clipped.
glReadPixels() doesn't use the raster
position. Instead, it obtains its (X,Y) device coordinate
address from its first two parameters. Like glDrawPixels(),
the area read has x and y for the lower
left corner. Problems can occur when reading pixels if:
- The area being read is from a window
that is overlapped or partially offscreen.
glReadPixels() will return undefined data for the
obscured area. (More info.)
- Memory wasn't allocated for the return
data (the 7th parameter is a NULL pointer) causing a
segmentation fault, core dump, or program termination.
If you think you've allocated enough memory, but you
still run into this problem, try doubling the amount
of memory you've allocated. If this causes your read
to succeed, chances are you've miscalculated the
amount of memory needed.
For both glDrawPixels() and glReadPixels(),
keep in mind:
- The width and height
parameters are in pixels.
- If the drawn or read pixel data seems
correct, but is slightly off, make sure you've set
alignment correctly. Argument values are controlled
with the glPixelStore*() functions. The PACK and
UNPACK values control sending and receiving pixel
data, from and to OpenGL, respectively.
14.020 How do I change between double- and
single-buffered mode, in an existing a window?
If you create a single-buffered window, you
can't change it.
If you create a double-buffered window, you
can treat it as a single-buffered window by setting
glDrawBuffer() to GL_FRONT and replacing your swap buffers
call with a glFlush() call. To switch back to double-buffered,
you need to set glDrawBuffer() to GL_BACK, and call swap
buffers at the end of the frame.
14.030 How do I read back a single pixel?
Use glReadPixels(), passing a value of one
for the width and height parameters.
14.040 How do I obtain the Z value for a
rendered primitive?
You can obtain a single pixel's depth value
by reading it back from the depth buffer with a call to
glReadPixels(). This returns the screen space depth value.
It could be useful to have this value in
object coordinate space. If so, you'll need to pass the
window X and Y values, along with the screen space depth
value to gluUnProject(). See more information on gluUnProject() here.
14.050 How do I draw a pattern into the stencil
buffer?
You can set up OpenGL state as follows:
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 0x1, 0x1);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
Subsequent rendering will set a 1 bit in
the stencil buffer for every pixel rendered.
14.060 How do I copy from the front buffer to
the back buffer and vice versa?
You need to call glCopyPixels(). The source
and destination of glCopyPixels() are set with calls to
glReadBuffer() and glDrawBuffer(), respectively. Thus, to
copy from the back buffer to the front buffer, you can code
the following:
glReadBuffer (GL_BACK);
glDrawBuffer (GL_FRONT);
glCopyPixels (GL_COLOR);
14.070 Why don't I get valid pixel data for an
overlapped area when I call glReadPixels() where part of the
window is overlapped by another window?
This is due to a portion of the OpenGL
specification called the Pixel Ownership test. If a window is
obscured by another window, it doesn't have to store pixel
data for the obscured region. Therefore, a glReadPixels()
call can return undefined data for the obscured region.
The Pixel Ownership test varies from one
OpenGL implementation to the next. Some OpenGL
implementations store obscured regions of a window, or the
entire window, in an off-screen buffer. Such an
implementation can return valid pixel data for an obscured
window. However, many OpenGL implementations map pixels on
the screen one-to-one to framebuffer storage locations and
don't store (and can't return) pixel data for obscured
regions of a window.
One strategy is to instruct the windowing
system to bring the window forward to the top of the window
stack, render, then perform the glReadPixels() call. However,
such an approach still risks user intervention that might
obscure the source window.
An approach that might work for some
applications is to render into a nonvisible window, such as a
Pixmap under X Windows. This type of drawing surface can't be
obscured by the user, and its contents should always pass the
pixel ownership test. Reading from such a drawing surface
should always yield valid pixel data. Unfortunately,
rendering to such drawing surfaces is often not accelerated
by graphics hardware.
14.080 Why does the appearance of my smooth-shaded
quad change when I view it with different transformations?
An OpenGL implementation may or may not
break up your quad into two triangles for rendering. Whether
it breaks it up or not (and if it does, the method used to
split the quad) will determine how color is interpolated
along the edges and ultimately across each scanline.
Many OpenGL applications avoid quads
altogether because of their inherent rasterization problems.
A quad can be rendered easily as a two-triangle GL_TRIANGLE_STRIP
primitive with the same data transmission cost as the
equivalent quad. Wise programmers use this primitive in place
of quads.
14.090 How do I obtain exact pixelization of
lines?
The OpenGL specification allows for a wide
range of line rendering hardware, so exact pixelization may
not be possible at all.
You might want to read the OpenGL
specification and become familiar yourself with the diamond
exit rule. Being familiar with this rule will give you the
best chance to obtain exact pixelization. Briefly, the
diamond exit rule specifies that a diamond-shaped area exists
within each pixel. A pixel is rasterized by a line only if
the mathematical definition of that line exits the diamond
inscribed within that pixel.
14.100 How do I turn on wide-line endpoint
capping or mitering?
OpenGL draws wide lines by rendering
multiple width-1 component lines adjacent to each other. If
the wide line is Y major, the component lines are offset in X;
if the wide line is X major, the component lines are offset
in Y. This can produce ugly gaps at the junction of line
segments and differences in apparent width depending on the
line segment's slope.
OpenGL doesn't provide a mechanism to
cleanly join lines that share common vertices nor to cleanly
cap the endpoints.
One possible solution is to render smooth (antialiased)
lines instead of normal aliased lines. To produce a clean
junction, you need to draw lines with depth test disabled or
the depth function set to GL_ALWAYS. See the
question on rendering antialiased lines for more info.
Another solution is for the application to
handle the capping and mitering. Instead of rendering lines,
the application needs to render face-on polygons. The
application will need to perform the necessary math to
calculate the vertex locations to provide the desired capping
and joining styles.
14.110 How do I render rubber-band lines?
The unspoken objective of this question is,
"How can I render something, then erase it without
disturbing what has already been rendered?"
Here are two common approaches.
One way is to use overlay planes. You draw
the rubber-band lines into the overlay planes, then clear the
overlay planes. The contents of the main framebuffer isn't
disturbed. The disadvantage of this approach is that OpenGL
devices don't widely support overlay planes.
The other approach is to render with logic
op enabled and set to XOR mode. Assuming you're rendering
into an RGBA window, your code needs to look like:
glEnable(GL_COLOR_LOGIC_OP);
glLogicOp(GL_XOR);
Set the color to white and render your
lines. Where your lines are drawn, the contents of the
framebuffer will be inverted. When you render the lines a
second time, the contents of the framebuffer will be restored.
The logic op command for RGBA windows is
only available with OpenGL 1.1. Under 1.0, you can only
enable logic op in color index windows, and GL_LOGIC_OP is
passed as the parameter to glEnable().
14.120 If I draw a quad in fill mode and again
in line mode, why don't the lines hit the same pixels as the
filled quad?
Filled primitives and line primitives
follow different rules for rasterization.
When a filled primitive is rendered, a
pixel is only touched if its exact center falls within the
primitive's mathematical boundary.
When a line primitive is rasterized,
ideally a pixel is only touched if the line exits a diamond
inscribed in the pixel's boundary.
From these rules, it should be clear that a
line loop specified with the same vertices as those used for
a filled primitive, can rasterize pixels that the filled
primitive doesn't.
(The OpenGL specification allows for some
deviation from the diamond exit line rasterization rule, but
it makes no difference in this scenario.)
14.130 How do I draw a full-screen quad?
See
this question in the Transformation section.
14.140 How do I initialize or clear a buffer
without calling glClear()?
Draw a full screen quad. See the
Transformation section.
14.150 How can I make line or polygon
antialiasing work?
To render smooth (antialiased) lines, an
application needs to do the following:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_LINE_SMOOTH);
If the scene consists entirely of smooth
lines, you need to disable the depth test or set it to GL_ALWAYS.
If a scene contains both smooth lines and
other primitives, turning depth test off isn't an option. You
can achieve nearly correct rendering results if you treat the
smooth lines as transparent primitives. The other (non-blended)
primitives should be rendered first, then the smooth lines
rendered last, in back to front order. See the transparency
section for more information.
Even taking these precautions might not
prevent some rasterization artifacts at the joints of smooth
line segments that share common vertices. The fact that the
depth test is enabled could conceivably cause some line
endpoints to be rendered incorrectly. This is a rendering
artifact that you may have to live with if the depth test
must be enabled while smooth lines are rendered.
Not all OpenGL implementations support
antialiased polygons. According to the OpenGL spec, an
implementation can render an aliased polygon when GL_POLYGON_SMOOTH
is enabled.
14.160 How do I achieve full-scene antialiasing?
See the OpenGL
Programming Guide, Third Edition, p452, for a description of a multi-pass
accumulation buffer technique. This method performs well on
devices that support the accumulation buffer in hardware.
On OpenGL 1.2 implementations that support
the optional imaging extension, a smoothing filter may be
applied to the final framebuffer image.
Many devices support the multisampling
extension.
|