.......几个术语...
components. For example, a vertex position might be stored as an x, a y, and a z value.
That would be three out of the four components. Internally, OpenGL makes the fourth
component (W if you just have to know) a one. In fact, if you are drawing just in the xy
plane (and ignoring z), then the third component will be automatically made a zero, and
again the fourth will be made a one. To complete the pattern, if you send down only a
single floating-point value as an attribute, the second and third components are zero,
while the fourth is still made a one. This default behavior applies to any attribute you set
up, not just vertex positions, so be careful when you don’t use all four components available
to you. Other things you might change per vertex besides the position in space are
texture coordinates, color values, and surface normals used for lighting calculations.
Attributes, however, can have any meaning you want in the vertex program; you are in
control.
Attributes are copied from a pointer to local client memory to a buffer that is stored (most
likely) on the graphics hardware. Attributes are only processed by the vertex shader and
have no meaning to the fragment shader. Also, to clarify that attributes change per vertex,
this does not mean they cannot have duplicate values, only that there is actually one
stored value per vertex. Usually, they are different of course, but it is possible you could
have a whole array of the same values. This would be very wasteful, however, and if you
needed a data element that was the same for all the attributes in a single batch, there is a
better way.
Uniforms
A uniform is a single value that is, well, uniform for the entire batch of attributes; that is,
it doesn’t change. You set the values of uniform variables usually just before you send the
command to render a primitive batch. Uniforms can be used for virtually an unlimited
number of uses. You could set a single color value that is applied to an entire surface. You
could set a time value that you change every time you render to do some type of vertex
animation (note the uniform changes once per batch, not once per vertex here). One of
the most common uses of uniforms is to set transformation matrices in the vertex shader
(this is almost the entire purpose of Chapter 4, “Basic Transformations: A Vector/Matrix
Primer”).
Like attributes, uniform values can be floating-point, integer, or boolean in nature, but
unlike attributes, you can have uniform variables in both the vertex and the fragment
shader. Uniforms can be scalar or vector types, and you can have matrix uniforms.
Technically, you can also have matrix attributes, where each column of the matrix takes
up one of those four component vector slots, but this is not often done. There are even
some special uniform setting functions we discuss in Chapter 5, “Basic Texturing,” that
deal with this.
The Basic Graphics Pipeline 83
3
Download from www.wowebook.com
Texture
A third type of data that you can pass to a shader is texture data. It is a bit early to try and
go into much detail about how textures are handled and passed to a shader, but you know
from Chapter 1, “Introduction to 3D Graphics and OpenGL,” basically what a texture is.
Texture values can be sampled and filtered from both the vertex and the fragment shader.
Fragment shaders typically sample a texture to apply image data across the surface of a
triangle. Texture data, however, is more useful than just to represent images. Most image
file formats store color components in unsigned byte values (8 bits per color channel), but
you can also have floating-point textures. This means potentially any large block of floating-
point data, such as a large lookup table of an expensive function, could be passed to a
shader in this way.
Outs
The fourth type of data shown in the diagram in Figure 3.1 are outs. An out variable is
declared as an output from one shader stage and declared as an in in the subsequent
shader stage. Outs can be passed simply from one stage to the next, or they may be interpolated
in various ways. Client code has no access to these internal variables, but rather
they are declared in both the vertex and the fragment shader (and possibly the optional
geometry shader). The vertex shader assigns a value to the out variable, and the value is
constant, or can be interpolated between vertexes as the primitive is rasterized. The fragment
shader’s corresponding in variable of the same name receives this constant or interpolated
value. In Chapter 6, we see how this works in more detail.