1.7. 绘制几何体 Drawing Geometry
As you can see from Figure 1.1, data for drawing geometry (points, lines, and polygons) starts
off in application-controlled memory (1). This memory may be on the host CPU, or, with the
help of some recent additions to OpenGL or under-the-covers data caching by the OpenGL
implementation, it may actually reside in video memory on the graphics accelerator. Either way,
the fact is that it is memory that contains geometry data that the application can cause to be
drawn.
1.7.1. Geometry Specification
The geometric primitives supported in OpenGL are points, lines, line strips, line loops, polygons,
triangles, triangle strips, triangle fans, quadrilaterals, and quadrilateral strips. There are three
main ways to send geometry data to OpenGL. The first is the vertex-at-a-time method, which
calls glBegin to start a primitive and calls
glEnd to end it. In between are commands that set
specific VERTEX ATTRIBUTES such as vertex position, color, normal, texture coordinates, secondary
color, edge flags, and fog coordinates, using calls such as
glVertex, glColor, glNormal, and
glTexCoord. (A number of variants of these function calls allow the application to pass these
values with various data types as well as to pass them by value or by reference.) Up through
version 1.5 of OpenGL, there was no way to send arbitrary (user-defined) pervertex data. The
only per-vertex attributes allowed were those specifically defined in the OpenGL specification.
OpenGL 2.0 added a method for sending arbitrary per-vertex data; that method is described in
Section 7.7 "Specifying Vertex Attributes."
When the vertex-at-a-time method is used, the call to
glVertex signals the end of the data
definition for a single vertex, and it may also define the completion of a primitive. After
glBegin is
called and a primitive type is specified, a graphics primitive is completed whenever
glVertex is
called enough times to completely specify a primitive of the indicated type. For independent
triangles, a triangle is completed every third time
glVertex is called. For triangle strips, a triangle
is completed when glVertex is called for the third time, and an additional connecting triangle is
completed for each subsequent call to glVertex.
The second method of drawing primitives is to use vertex arrays. With this method, applications
store vertex attributes in user-defined arrays, set up pointers to the arrays, and use
glDrawArrays, glMultiDrawArrays,
glDrawElements, glMultiDrawElements,
glDrawRangeElements, or
glInterleavedArrays to draw a large number of primitives at once. Because these entry points can
efficiently pass large amounts of geometry data to OpenGL, application developers are
encouraged to use them for portions of code that are extremely performance critical. Using
glBegin and glEnd requires a function call to specify each attribute of each vertex, so the function
call overhead can become substantial when objects with thousands of vertices are drawn. In
contrast, vertex arrays can be used to draw a large number of primitives with a single function
call after the vertex data is organized into arrays. Processing the array data in this fashion can
be faster because it is often more efficient for the OpenGL implementation to deal with data
organized in this way. The current array of color values is specified with
glColorPointer, the
current array of vertex positions is specified with
glVertexPointer, the current array of normal
vectors is specified with glNormalPointer, and so on. The function
glInterleavedArrays can specify and
enable several interleaved arrays simultaneously (e.g., each vertex might be defined with three
floating-point values representing a normal followed by three floating-point values representing
a vertex position.)
The preceding two methods are referred to as drawing in
IMMEDIATE MODE because primitives are
rendered as soon as they have been specified. The third method involves storing either the
vertex-at-a-time function calls or the vertex array calls in a
DISPLAY LIST, an OpenGL-managed
data structure that stores commands for later execution. Display lists can include commands to
set state as well as commands to draw geometry. Display lists are stored on the server side and
can be processed later with glCallList or
glCallLists. This is not illustrated in Figure 1.1, but it is
another way that data can be provided to the OpenGL processing pipeline. The definition of a
display list is initiated with glNewList, and display list definition is completed with
glEndList. All the
commands issued between those two calls become part of the display list, although certain
OpenGL commands are not allowed within display lists. Depending on the implementation,
DISPLAY LIST MODE can provide a performance advantage over immediate mode. Storing
commands in a display list gives the OpenGL implementation an opportunity to optimize the
commands in the display list for the underlying hardware. It also gives the implementation the
chance to store the commands in a location that enables better drawing performance, perhaps
even in memory on the graphics accelerator. Of course, some extra computation or data
movement is usually required to implement these optimizations, so applications will typically
see a performance benefit only if the display list is executed more than once.
New API calls in version 1.5 of OpenGL permitted vertex array data to be stored in server-side
memory. This mechanism typically provides the highest performance rendering because the
data can be stored in memory on the graphics accelerator and need not be transferred over the
I/O bus each time it is rendered. The API also supports the concept of efficiently streaming data
from client to server. The glBindBuffer command creates a buffer object, and
glBufferData and
glBufferSubData specify the data values in such a buffer.
glMapBuffer can map a buffer object into
the client's address space and obtain a pointer to this memory so that data values can be
specified directly. The command glUnmapBuffer
must be called before the values in the buffer are
accessed by subsequent GL rendering commands. glBindBuffer
can also make a particular buffer
object part of current state. If buffer object 0 is bound when calls are made to vertex array
pointer commands such as glColorPointer,
glNormalPointer, glVertexPointer, and so on, the pointer
parameter to these calls is understood to be a pointer to client-side memory. When a buffer
object other than 0 is bound, the pointer parameter is understood to be an offset into the
currently bound buffer object. Subsequent calls to one of the vertex array drawing commands
(e.g., glMultiDrawArrays) can thus obtain their vertex data from either client- or server-side
memory or a combination thereof.
OpenGL supports the rendering of curves and surfaces with evaluators. Evaluators use a
polynomial mapping to produce vertex attributes such as color, normal, and position that are
sent to the vertex processing stage just as if they had been provided by the client. See the
OpenGL specification for a complete description of this functionality.
1.7.2. Per-Vertex Operations
No matter which of these methods is used, the net result is that geometry data is transferred
into the first stage of processing in OpenGL, VERTEX PROCESSING
(2). At this point, vertex positions
are transformed by the modelview and projection matrices, normals are transformed by the
inverse transpose of the upper leftmost 3 x 3 matrix taken from the modelview matrix, texture
coordinates are transformed by the texture matrices, lighting calculations are applied to modify
the base color, texture coordinates may be automatically generated, color material state is
applied, and point sizes are computed. All of these things are rigidly defined by the OpenGL
specification. They are performed in a specific order, according to specific formulas, with
specific items of OpenGL state controlling the process.
Because the most important things that occur in this stage are transformation and lighting, the
vertex processing stage is sometimes called TRANSFORMATION AND LIGHTING, or, more familiarly,
T&L. There is no application control to this process other than modifying OpenGL state values:
turning lighting on or off with glEnable/glDisable; changing lighting attributes with
glLight and
glLightModel; changing material properties with
glMaterial; or modifying the modelview matrix by
calling matrix manipulation functions such as glMatrixMode,
glLoadMatrix, glMultMatrix,
glRotate,
glScale, glTranslate. At this stage of processing, each vertex is treated independently. The vertex
position computed by the transformation stage is used in subsequent clipping operations. The
transformation process is discussed in detail in Section 1.9.
Lighting effects in OpenGL are controlled by manipulation of the attributes of one or more of the
simulated light sources defined in OpenGL. The number of light sources supported by an
OpenGL implementation is specifically limited to GL_MAX_LIGHTS. This value can be queried
with glGet and must be at least 8. Each simulated light source in OpenGL has attributes that
cause it to behave as a directional light source, a point light source, or a spotlight. Light
attributes that can be adjusted by an application include the color of the emitted light, defined
as ambient, diffuse, and specular RGBA intensity values; the light source position; attenuation
factors that define how rapidly the intensity drops off as a function of distance; and direction,
exponent, and cutoff factors for spotlights. These attributes can be modified for any light with
glLight. Individual lights can be turned on or off by a call to
glEnable/glDisable with a symbolic
constant that specifies the affected light source.
Lighting produces a primary and secondary color for each vertex. The entire process of lighting
can be turned on or off by a call to glEnable/glDisable
with the symbolic constant GL_LIGHTING. If
lighting is disabled, the values of the primary and secondary color are taken from the last color
value set with the glColor command and the last secondary color set with the
glSecondaryColor
command.
The effects from enabled light sources are used in conjunction with surface material properties
to determine the lit color at a particular vertex. Materials are characterized by the color of light
they emit; the color of ambient, diffuse, and specular light they reflect; and their shininess.
Material properties can be defined separately for front-facing surfaces and for back-facing
surfaces and are specified with glMaterial.
Global lighting parameters are controlled with glLightModel. You can use this function to