In this approach, the vertex properties are placed in arrays, which are then passed to OpenGL using the following calls: void glColorPointerGLint size, GLenum type, GLsizei stride, GLvoi
Trang 18.1.1 PRIMITIVE TYPES
OpenGL ES 1.0 supports the geometric primitives shown in Figure 3.3: points, lines, and
triangles OpenGL ES 1.1 amends this list with point sprites
Points
The point is the simplest OpenGL primitive, and it requires only one vertex Its primary
property is its size, which is set by the function
void glPointSize{fx}(type size).
The point size corresponds to its diameter (the total width), defined in pixels, and it
defaults to 1 Points can be drawn either with or without antialiasing (enabled and
dis-abled with GL_POINT_SMOOTH) With antialiasing off, the points are drawn as squares
and the point size is rounded to the closest integer With antialiasing enabled, the points
are drawn as circles, and the alpha values of the pixels on the boundary are affected by
how much the point covers those pixels
Even though points can be drawn quite efficiently, in practice many graphics engines
are optimized for rendering of triangles This may mean that non-antialiased points are
drawn as two triangles, and the maximum point size for smooth points may be just one
pixel Similar optimizations may be used for line drawing
Point sprites and size attenuation
OpenGL ES 1.1 provides features for points that are especially useful for particle effects:
point sprites, point size arrays, and point size attenuation Many natural phenomena such
as rain, smoke, or fire, can be modeled by replicating several small pictures representing
raindrops, puffs of smoke, or individual flames The idea is that a set of points describes
the positions of point sprites, and their appearance comes from the current texture map
Section 9.2.8 describes how to apply a texture to points
When points are defined by an array, in OpenGL ES 1.0 they all have the same size, defined
by glPointSize{fx} In OpenGL ES 1.1 it is possible to give each point its own size
(see Section 8.1.2), and the point sizes may be attenuated by the distance between each
point and the camera The derived point size comes from the formula:
derived size = impl clamp
user clamp
size∗
1
a + bd + cd2
(8.1)
where d is the eye-coordinate distance from the camera, the attenuated point size is
affec-ted by the distance attenuation coefficients a, b, c, it is clamped by user-specified min-max
Trang 2range of GL_POINT_SIZE_MIN and GL_POINT_SIZE_MAX, and finally clamped to implementation-dependent point size range If multisampling is disabled, this is the size used for rasterizing the point With multisampling, the point size is clamped to have a minimum threshold, and the alpha value of the point is modulated by
alpha fade=
derived size threshold
2
The point attenuation components are set using
void glPointParameter{fx}(GLenum pname, T param) void glPointParameter{fx}v(GLenum pname, T * params)
where pname GL_POINT_SIZE_MIN and GL_POINT_SIZE_MAX are used to
change the clamping values for the point size calculation GL_POINT_DISTANCE_ ATTENUATIONis used to pass in params an array containing the distance attenuation coefficients a, b, and c, in that order GL_POINT_FADE_THRESHOLD_SIZE specifies
the point alpha fade threshold
Keeping the attenuation components in their default values (1, 0, 0) in practice disables point size attenuation
Point sprites are enabled by calling glEnable with the token GL_POINT_SPRITE_ OES When the global point sprite mode is enabled and the texture environment for the given texture unit is set to GL_COORD_REPLACE_OES (see Section 9.2.8), all points submitted for drawing are handled as point sprites A point sprite can be thought of as
a textured quad whose center lies at the transformed screen-space position of the vertex representing the point and whose screen dimensions are equivalent to the derived size of the point
Here is a simple code that draws a32 × 32 point sprite We use a single point size, but we could have varied it using glPointSizePointerOES
glPointSize( 32 );
glEnable( GL_POINT_SPRITE_OES );
glTexEnvi( GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE ); glDrawArrays( GL_POINTS, 0, 1 );
The entry point definition for glTexEnv is
void glTexEnv{ifx}(GLenum target, GLenum pname,T param), and its other uses for specifying how texture mapping is done are described in Section 9.2.5 and Section 9.2.7
Pitfall: Point clipping in OpenGL ES works so that if the transformed vertex of the point
is outside the view frustum, the whole primitive is considered to be outside the frustum
Trang 3and is thus discarded This way a very simple clipping formula can be applied already
at the geometry stage to cull away the point geometry As a side effect, points or point
sprites wider than one pixel vanish before all of the pixels of the primitive move outside
of the view frustum If this is a problem the application can set the viewport to be larger
than the display and set the scissor to match the size of the display
Although this should work as specified in the API, in practice most implementations
of OpenGL ES in the market have some issues with either the point clipping when the
viewport has been extended, or with point sprites in general
Lines
There are three ways for defining a set of lines in OpenGL ES The first is a collection of
separate lines, with a line segment connecting the first and the second vertices, then the
third and the fourth, and so on The second type is a line strip, which simply connects
each vertex in a list to the next one with a line segment The third type is the line loop,
which closes a line strip by adding a segment between the last and first vertex
The line width, in pixels, can be set with
void glLineWidth{fx}(GLfloatwidth),
and the lines can be drawn either with or without antialiasing (enabled and disabled with
GL_LINE_SMOOTH)
The desktop OpenGL also supports stippling, that is, dotting and dashing the lines.
Since this can be emulated by texture mapping the lines, stippling is not supported in
OpenGL ES
Polygons
The only Polygon type supported by OpenGL ES is a triangle Desktop OpenGL also
sup-ports quadrilaterals, n-sided polygons, and screen-aligned rectangles, but these were left
out of OpenGL ES for the reasons described in Section 3.1.1
There are three methods for defining triangles in OpenGL ES The first way is as a
collection of separate triangles, where the first three vertices of a vertex array form the
first triangle, the next three form the second triangle, and so forth The second way is
a triangle strip There the first three vertices create a triangle, and after that, every new
vertex creates a new triangle by connecting to the two previous vertices The third way is
a triangle fan Again, the first triangle is made of the first three vertices After that, every
new vertex creates a new triangle using the new vertex, the previous vertex, and the first
vertex Thus the triangles create a fan around the first vertex
Trang 48.1.2 SPECIFYING VERTEX DATA
The original model for specifying vertex data in OpenGL used the glBegin - glEnd model For example, a triangle strip of two triangles, with two red and two green vertices, could be specified by
/* glBegin - glEnd NOT SUPPORTED BY OpenGL ES!! */
glColor4f ( 1.0f, 0.0f, 0.0f, 1.0f );
glVertex3f( 0.0f, 1.0f, 0.0f );
glVertex3f( 0.0f, 0.0f, 0.0f );
glColor4f ( 0.0f, 1.0f, 0.0f, 1.0f );
glVertex3f( 1.0f, 1.0f, 0.0f );
glVertex3f( 1.0f, 0.0f, 0.0f );
glEnd();
The function glBegin indicates the primitive type The current values of vertex proper-ties such as color, the normal vector, and texture coordinates are specified in an arbitrary order A call to glVertex specifies the vertex location and completes the vertex defi-nition using the current property values This approach creates a very complicated state machine, and requires a large number of function calls to render the geometry, slowing
graphics hardware down Display lists are a way to deal with this issue by collecting all the
GL calls and their arguments into a list which can be cached by the graphics engine, and later drawn using a single function call
Desktop OpenGL 1.1 introduced vertex arrays, which greatly simplified specification of
the vertex data, and made both the glBegin - glEnd model and the display lists largely redundant In this approach, the vertex properties are placed in arrays, which are then passed to OpenGL using the following calls:
void glColorPointer(GLint size, GLenum type, GLsizei stride, GLvoid * pointer) void glTexCoordPointer(GLint size, GLenum type, GLsizei stride, GLvoid * pointer) void glVertexPointer(GLint size, GLenum type, GLsizei stride, GLvoid * pointer) void glNormalPointer(GLenum type, GLsizei stride, GLvoid * pointer)
void glPointSizePointerOES(GLenum type, GLsizei stride, GLvoid * pointer)
All of the functions above have similar syntax The parameter size describes the dimen-sionality of the data, e.g., size = 2 for glVertexPointer indicates that the x and y coordinates are specified, while z is left to the default value of 0 Note that for
glNormalPointerand glPointSizePointerOES the size parameter is omitted,
as normals always have three components and points only one The type parameter is used
to denote the basic type for storing the array data See Table 8.1 for the allowed
combi-nations of size and type for each array Also note that glPointSizePointerOES is
only supported in OpenGL ES 1.1
The stride parameter gives the distance in bytes between consecutive array elements Finally, the pointer parameter points to the actual vertex attribute data.
Trang 5T a b l e 8.1: Vertex array sizes (values per vertex) and data types.
Length 8 bytes
Offset:
X coordinate
short (16-bit)
Y coordinate short (16-bit)
Z coordinate short (16-bit)
s texture coordinate char (8-bit)
t texture coordinate char (8-bit)
F i g u r e 8.1:Example of packed vertex array data.
The stride can be used to skip data if the vertex array is in an interleaved format
Figure 8.1 shows an example of packed vertex data that stores vertex coordinates and
texture coordinates in an interleaved format The vertex pointer in this case would point
to the beginning of the array and have a stride value of 8 (equaling the size of the packed
vertex) The texture coordinate pointer in this case would point to the beginning of the
array plus 6 bytes, and the stride value would be 8 bytes
Specifying a stride of zero always matches the stride that would be used for tightly packed
vertex data For example, if the vertex array has three GL_SHORT coordinates and a stride
of zero, the implementation interprets the actual stride as being 6 bytes
Performance tip: Depending on the implementation, the vertex data format may have
a great impact on performance For example, the amount of bandwidth required for
transmitting the geometry data over the system buses depends directly on the type used
to specify the vertex data Also, especially for pure software-based implementations of
OpenGL ES on mobile devices that often lack floating-point units, using floating-point
vertex data may force the implementation to fall into a much slower version of the
trans-formation and lighting pipeline Even with the integer data types, using the more
com-pact data types gives the implementation more freedom to optimize performance Often
GL_SHORTis enough for almost any kind of vertex data in 3D
Trang 6The arrays have to be explicitly enabled (or disabled) using
void glEnableClientState(GLenum cap) void glDisableClientState(GLenum cap)
where the cap parameter is one of
GL_COLOR_ARRAY, GL_NORMAL_ARRAY, GL_TEXTURE_COORD_ARRAY, GL_VERTEX_ARRAY, or GL_POINT_SIZE_ARRAY_OES.
OpenGL ES 1.0 supports the multitexturing API but is not required to provide more than one texturing unit, while OpenGL ES 1.1 guarantees the availability of at least two textur-ing units
void glClientActiveTexture(GLenumtexture)
is used to select which of texture units is affected by glTexCoordPointer, glEnable ClientState(GL_TEXTURE_COORD_ARRAY), and glDisableClientState
(GL_TEXTURE_COORD_ARRAY) calls The parameter texture defines the new active
texture unit (GL_TEXTURE0, GL_TEXTURE1, etc.)
Default values
OpenGL ES allows a default value to be set for normals, colors, and texture coordinates, and then the corresponding vertex array does not need to be specified If one of the arrays has not been enabled with glEnableClientState, these default values are used instead The following calls are used to define the default values:
void glNormal3{fx}(Tnx,Tny,Tnz)
void glColor4{fx ub}(Tred,Tgreen,Tblue,Talpha)
void glMultiTexCoord4{fx}(GLenumtarget,Ts,Tt,Tr,Tq)
The target is GL_TEXTUREi, where 0 ≤ i < the value of GL_MAX_TEXTURE_UNITS
which is an implementation-dependent value
8.1.3 DRAWING THE PRIMITIVES
Once the vertex data has been specified, there are two functions that can be used to draw the resulting shapes The function
void glDrawArrays(GLenummode,GLintfirst,GLsizeicount)
Trang 7is used to draw consecutive primitives starting from the first index in the vertex array The
parameter mode defines the type of the primitives to be drawn: GL_POINTS, GL_LINES,
GL_LINE_LOOP, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP, or
GL_TRIANGLE_FAN The count determines how many vertices are submitted for
rendering
glDrawArraysis typically used in cases where triangles are represented with strips
that are organized directly in the correct order The second drawing function uses a list of
indices to the vertex array to define the primitives:
void glDrawElements(GLenummode,GLsizecount,
GLenumtype,const GLvoid *indices)
The parameter mode is the same as in glDrawArrays type defines the type of
the data that is stored in the array indices and can be either GL_UNSIGNED_BYTE
or GL_UNSIGNED_SHORT count determines the number of indices to process See
Section 6.4 for further discussion about the benefits of indexed rendering, such as
better use of vertex caches
The following example code renders red triangles using both of these methods
static const GLbyte vertices1[8*2] = { 0,0, 0,0, — 20,20, 20,20,
— 20,40, 20,40, — 20,60, 20,60 };
static const GLbyte vertices2[7*2] = { 0,100, 100,0, 0, — 100,
— 100,0, 0,50, 45,20,
— 45,20 };
glEnableClientState( GL_VERTEX_ARRAY );
glColor4ub( 255, 0, 0, 255 );
glVertexPointer( 2, GL_BYTE, 0, vertices1 );
/* skip vertex 0, draw five triangles */
glDrawArrays( GL_TRIANGLE_STRIP, 1, 7 );
glVertexPointer( 2, GL_BYTE, 0, vertices2 );
/* draw three triangles, using the first seven vertices */
glDrawElements( GL_TRIANGLES, 9, GL_UNSIGNED_SHORT, indices );
8.1.4 VERTEX BUFFER OBJECTS
Since the vertex arrays are stored in user-controlled memory, and the user can change
their content between draw calls without the graphics engine being aware of the change,
the GL driver cannot cache them This results in costly data transfers between the system
memory and the graphics engine whenever draw calls are issued Vertex buffer objects,
introduced in OpenGL ES 1.1, provide a mechanism for storing the vertex arrays into
memory controlled by the graphics server and allow buffer data updates only via explicit
function calls A driver may then optimize the vertex buffer usage by storing that data in
an optimized memory layout, or by converting the values into a type that executes faster
on the hardware
Trang 8A buffer object is created with a call to
void glBindBuffer(GLenumtarget,GLuintbuffer)
where target is GL_ARRAY_BUFFER and buffer is a handle to the buffer If buffer is an
unused handle and greater than 0, a new zero-sized memory buffer is created Otherwise
the existing buffer object becomes bound If 0 is given for buffer, the graphics engine will
behave as if there were no currently bound vertex buffer object
A list of existing buffer objects and their resources are deleted with
void glDeleteBuffers(GLsizein,const GLuint *buffers)
If any of the buffer objects being deleted are bound as active vertex attribute pointers, the bindings are released when the function call returns
Handles to the buffers can be created by calling
void glGenBuffers(GLsizein,GLuint *buffers)
which stores n buffer object handles to an array specified by buffers and marks them as
being used The actual buffers still need to be created with glBindBuffer A side effect
of glDeleteBuffers is to make the deleted handles available again
The actual data are stored into the currently bound vertex buffer object by calling
void glBufferData(GLenumtarget,GLsizeiptrsize,const GLvoid *data,
GLenumusage)
If the buffer object already contains data, the old data is freed and replaced by the new
data For the vertex data the parameter target is set to GL_ARRAY_BUFFER, size gives the size of the data to be copied in bytes, data is a pointer to the source data, and usage
gives a hint about the intended usage for this vertex buffer object GL_STATIC_DRAW advises the driver to optimize for data staying constant across GL draw calls, while GL_DYNAMIC_DRAWindicates that the data for this buffer object are changed dynami-cally between subsequent frames or even between draw calls
void glBufferSubData(GLenumtarget,GLintptroffset,GLsizeiptrsize,const
GLvoid *data)
is used to replace some of the data in the serside store for the currently bound
ver-tex buffer object target is again GL_ARRAY_BUFFER, and offset gives an offset in bytes
to the location from which the data is to be replaced in the server-side store size gives the length of data to be replaced, and data gives the actual data to be copied to the
server-side store Note that this function cannot be used to extend the size of the server
side store If offset+size extends beyond the data buffer stored originally with a call to
glBufferData, a GL error is generated and the data will not be copied
Performance tip: At first sight, GL_DYNAMIC_DRAW does not seem to improve
on the standard vertex arrays, as the driver is assuming that the data is modi-fied often However, if the data behind the vertex buffer object is shared even for
Trang 9two draw calls, GL_DYNAMIC_DRAW allows the driver to keep the data in the
server-side storage across those invocations, whereas a standard vertex array would have
to be sent every time GL_DYNAMIC_DRAW hints to the implementation that it should
not perform particularly costly optimizations for the data representation as it will get
replaced many times
After a vertex buffer object is set up, it can be bound to any vertex attribute array by calling
the relevant function such as glColorPointer The pointer argument now does not
contain the vertex data, but an offset to the currently bound vertex buffer object Multiple
vertex array pointers can be set up from the same vertex buffer object, e.g., packed vertex
data representations can be used the same way as with standard vertex array calls The
vertex buffer objects are disabled with glBindBuffer(GL_ARRAY_BUFFER, 0),
after which the vertex pointer calls work as described in Section 8.1.2
Array indices in buffer objects
It is also possible to store indices that are used with glDrawElements into buffer
objects by setting the target argument of calls glBindBuffer, glBufferData,
and glBufferSubData to GL_ELEMENT_ARRAY_BUFFER If the currently bound
buffer object is a GL_ELEMENT_ARRAY_BUFFER, glDrawElements takes the index
data from the buffer, and interprets the indices parameter as an offset to the buffer object
data
Example
The following example code renders some colored triangles using vertex buffer objects:
-100,0, 0,50, 45,20, -45,20 };
255,0,0,255, 255,255,255,255, 255,0,255,255, 255,255,0,255,
0,255,255,255 };
/* create handles */
GLuint handle[3];
glGenBuffers( 3, &handle[0] );
/* load the vertex data into the first VBO */
glBindBuffer( GL_ARRAY_BUFFER, handle[0] );
glBufferData( GL_ARRAY_BUFFER, sizeof(vertices),
&vertices[0], GL_STATIC_DRAW );
/* load the index data into the second VBO */
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, handle[1] );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof(indices),
&indices[0], GL_STATIC_DRAW );
/* load the color data into the third VBO */
glBindBuffer( GL_ARRAY_BUFFER, handle[2] );
glBufferData( GL_ARRAY_BUFFER, sizeof(colors),
Trang 10glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_COLOR_ARRAY );
glBindBuffer( GL_ARRAY_BUFFER, handle[0] );
glVertexPointer( 2, GL_BYTE, 0, NULL );
glBindBuffer( GL_ARRAY_BUFFER, handle[2] );
glColorPointer( 4, GL_UNSIGNED_BYTE, 0, NULL );
/* skip vertex 0, draw five triangles */
glDrawArrays( GL_TRIANGLE_STRIP, 1, 6 );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, handle[1] );
/* draw three triangles, using the first seven vertices */
glDrawElements( GL_TRIANGLES, 9, GL_UNSIGNED_SHORT, NULL );
/* Unbind all VBOs */
glBindBuffer( GL_ARRAY_BUFFER, 0 );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
8.2 VERTEX TRANSFORMATION PIPELINE
This section covers the vertex transformation pipeline, shown in Figure 8.2 First, the vertex coordinates and the vertex normals are transformed from model coordinates to
eye coordinates using the modelview matrix Lighting and user clipping are done in the eye coordinate space Next the projection matrix transforms the lit vertices into the clip
space, where the primitives formed from the vertices are clipped against the viewing frus-tum After clipping, the vertices are transformed into normalized device coordinates by
a perspective division, and the primitives are rasterized, i.e., converted into pixels The
texture matrix is also applied to texture coordinates during the rasterization to correctly sample the texture maps Finally the viewport transformation determines where and with
which depth values the rasterized fragments are stored into the frame buffer The math-ematics behind the transformation pipeline are described in Chapter 2
8.2.1 MATRICES
The matrix functions operate on the current matrix The active matrix type is selected using
void glMatrixMode(GLenummode)
Modelview
Matrix
Projection Matrix
Perspective Division
Viewport Transformation
Object
Coordinates
Clip Coordinates
Normalized Device Coordinates
Window Coordinates
Eye Coordinates
F i g u r e 8.2: The vertex transformation pipeline is parametrized by user-given modelview and projection matrices, and view-port transformation parameters 4D homogeneous coordinates are mapped from clip coordinates to normalized device
coor-dinates by a division by the fourth, w, component.