Accessing Texture Maps in Shaders (OpenGL Programming)

GLSL also supports accessing texture maps in both vertex and fragment shaders. Implementations can choose to support texture mapping in vertex shaders (provided the underlying OpenGL implementation supports this functionality), but requires support for fragment shaders.

To access a texture map, GLSL makes an association between an active texture unit configured in the OpenGL application, and a variable declared in a shader. Such variables use one of the sampler data types shown in Table 15-8 to allow the shader program access to the texture map’s data. The dimensions of the associated texture map must match the type of the sampler.

Sampler Name

Description

samplerlD

Accesses a ID texture map

sampler2D

Accesses a 2D texture map

sampler3D

Accesses a 3D texture map

samplerCube

Accesses a cube map (for reflection mapping)

samplerlDShadow

Accesses a ID shadow map

sampler2DShadow

Accesses a 2D shadow map

Table 15-8   Fragment Shader Texture Sampler Types

Samplers must be declared as uniform variables in the shader and must have their value assigned from within the OpenGL application. Samplers may also be used as parameters in functions, but must be used with samplers of matching type.


Samplers must have a texture unit assigned to them before their use in a shader, and can only be initialized by glUniformli(), or glUniformliv(), with the index of the texture unit that the sampler should use (see Example 15-5).

Example 15-5 Associating Texture Units with Sampler Variables

Associating Texture Units with Sampler Variables

Accessing Textures in GLSL

Sampling a texture map from within a GLSL shader uses the sampler variable that you’ve declared and associated with a texture unit. There are a number of texture access routines for accessing all OpenGL supported texture map types.

In Example 15-6, we sample the two-dimensional texture map associated with the samplei2D variable tex, and combine the results with the fragment’s color, providing the same results as using GL_MODULATE mode for the texture environment mode.

Example 15-6 Sampling a Texture Within a GLSL Shader

Sampling a Texture Within a GLSL Shader

Even though the example seems simple, that’s truly all there is to do. However, much more interesting applications are enabled when the values in a texture map do not necessarily represent colors, but other data used for subsequent computation after retrieving the results from the texture map. Specifically, one application is using the values in one texture map as indices into another.The same results can be accomplished using the combiner texture environment, but are much simpler to do using shaders.

The results you compute after sampling a texture are controlled by the code you write in your shader, but how the texture map is sampled is still controlled by the state settings in your application. For example, you control whether a texture map contains mipmaps, and how those mipmaps are sampled, as well as the filters used for resolving the returned texel values (basically, the parameters you set with glTexParameter*()). From inside the shader, you can control the biasing of mipmap selection, and using projec-tive-texture techniques.

Dependent Texture Reads

During the execution of a shader that employs texture mapping, you’ll use texture coordinates to specify locations in texture maps and retrieve the resulting texel values. While texture coordinates are supplied for each active texture unit, you can use any values you might like as texture coordinates (assuming matching dimensionality) for use with a sampler, including values you may have just sampled from another texture map. Passing the results of one texture access as texture coordinates into another texture access operation is generally termed a dependent texture read, indicating that the results of the second operation are dependent on the first operation.

This is easy to implement in GLSL, and illustrated in Example 15-7.

Example 15-7 Dependent Texture Reads in GLSL

Dependent Texture Reads in GLSL

Vertex Shader Specifics

You can send data from your application into a vertex program using several mechanisms:

• By using the standard OpenGL vertex data interface (those calls that are legal between a glBegin() and a glEnd()), such as glVertex*(), glNormal*(), and so on. These values can vary on a per-vertex basis and are considered to be built-in attribute variables.

•    By declaring uniform variables. These values remain constant across a geometric primitive.

•    By declaring attribute variables, which can be updated on a per-vertex basis, in addition to the standard vertex state.

Likewise, a vertex program must output some data (and optionally update other variables) for continued processing by the remaining vertex processing by the OpenGL pipeline, and possibly an accompanying fragment program.

The outputs that need to be written include:

•    gi_Position, which must be updated by the vertex program and contain the homogeneous coordinate of the vertex after modelview and projection transformation.

•    Various other built-in variables that are declared varying for passing data into the fragment pip’eline. These include colors, texture coordinates, and other per-fragment data.

•    User-defined varying variables.

Figure 15-7 illustrates the inputs and outputs of a vertex program.

GLSL Vertex Shader Input and Output Variables

Figure 15-7 GLSL Vertex Shader Input and Output Variables

Built-in Attribute Input Variables

Table 15-9 shows variables representing those that are globally available in a vertex shader. The variables reflect the current OpenGL state, as set by the corresponding routine.

Variable

Type

Specifying function

Description

gLVertex

vec4

glVertex

Vertex’s world-space coordinate

gl_Color

vec4

glCoIor

Primary color value

gl_SecondaryColor

vec4

glSecondaryColor

Secondary color value

gl_Normal

vec4

glNormal

Lighting normal

gl_MultiTexCoord«

vec4

glMu!tiTexCoord( n, … );

Texture unit n’s texture coordinates, with n = 0 … 7.

gl_FogCoord

float

glFogCoord

Fog coordinate

Table 15-9 Vertex Shader Attribute Global Variables

User-Defined Attribute Variables

User-defined attribute variables are global variables that associate values passed from the OpenGL application to a vertex shader executing within the OpenGL implementation.

Attribute variables can be defined as float, floating-point vectors (vec*), or matrices (mat*).

Hint: In general, attribute variables are implemented as vec4′s internal to OpenGL. If you have a number of single floating-point variables that you wish to use as vertex attributes, consider combining those values into one or more vec* structures. While declaring a single float is supported, it uses an entire vec4 to represent the single value.

To use user-defined attribute variables, OpenGL needs to know how to match the name of the variable you specified in your shader program with values that you pass into a shader. Similar to how OpenGL handles uniform variables, when a shader program is linked, the linker generates a table of variable names for attribute variables. To determine the maximum number of user-defined vertex attributes, call glGetIntegerv() with a parameter of GL_MAX_VERTEX_ATTRIBS.

To determine which index you need to update for the respective variable in the vertex program, you’ll call glGetAttribLocation(), with the name of your variable, and the index corresponding to that name will be returned.

GLint glGetAttribLocation(GLuint program, const char *name);

Returns the index associated with name for the shader program, name must be a null-terminated character string matching the declaration in program. If name is not an active variable, or it’s the name of a built-in attribute variable, or an error occurs, a value of -1 is returned.

For example, in a vertex shader, you might declare

tmp73ec-150_thumb

Assuming that the shader compiled and linked appropriately, you would determine the index of "displacement" by calling:

tmp73ec-151_thumb

You can also explicitly set the binding of an attribute variable to an index using the glBindAttribLocation() call; however, this must be done before the shader program is linked.

void glBindAttribLocation(GLint program, GLuint index, const char *name);

Explicitly specifies which index location name should be assigned the next time program is linked, index must De an integer between zero and GL_ MAX_VERTEX_ATrRIBS – 1, and name must be a null-terminated string. Built-in attribute variables (those beginning with gi_) will generate a GL_INVALID_OPFRATION error.

To set the value associated with the returned index, you’ll use a version of the glVertexAttrib*() function.

tmp73ec-152_thumb

Specifies the values for vertex attribute variables associated with index to values. For calls that do not explicitly set all four values, default values of 0.0 will be set for the y- and z-coordinates, and 1.0 for the w-coordinate.

Specifying values for index zero is identical to calling glVertex*(), with the same values.

The normalized version will convert integer input values into the range zero to one.

Matrices are updated by specifying consecutive values for index, values specified will be used to update the respective columns of the matrix.

While attribute variables are floating point, there’s no restriction on the type of input data used to initialize their values. Specifically, integer-type input values can be normalized into the range of zero to one, using glVertexAttrib4N*(), before being assigned into the attribute variable.

Scalar (single-value), vector, and matrix attribute values can be set using glVertexAttrib*(). For the matrix case, multiple calls are required. In particular, for a matrix of dimension n, you would call glVertexAttrib*(), with indices: index, index+1, …, index+n-1.

Vertex shaders also augment the vertex array facility.As with other types of vertex data, values for vertex attribute variables can be stored in vertex arrays and updated by calling glDrawArrays(), glArrayElement(), and so on. To specify the array to be used to update a variable’s value, call glVertexAttribPointerQ.

void gl\ertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer);

Specifies where the data values for index can be accessed, pointer is the memory address of the first set of values in the array.; size represents the number of components to be updated per vertex, type specifies the data type (GL_SHORT, GLJNT, GL_FLOAT, or GL_DOUBl E) of each element in the array, normalized indicates that the vertex data should be normalized before being stored (in the same manner as glVertexAttrib4N*()). stride is the byte offset between consecutive elements in the array. If stride is 0, the data is assumed to be tightly packed.

As with other types of vertex arrays, specifying the array is only one part of the process. Each client-side vertex array needs to enabled. Compared to using glEnableClientState(), arrays of vertex attributes are enabled by calling glEnableVertexAttribArray().

void glEnableYertexAttribArray(GLuint index); void glDisableVertexAttrib Array (GLuint index);

Specifies that the vertex array associated with variable index be enabled or disabled, index must be a value between 0 and GL_MAX_VERTEX_ ATTRIBS – h

Special Output Variables

The values shown in Table 15-10 are available for writing (and reading after being written) in a vertex shader. gi_Position specifies the vertex’s final position upon exit of the vertex shader and is required to be written in the shader.

Variable Name

Type

Description

gl_Position

vec4

Transformed vertex position (in eye coordinates)

gl_PointSize

float

Point size of vertex

gl_ClipVertex

vec4

Vertex position to be used with user-defined clipping planes. This value must be in the same coordinate system as the clipping planes: eye-coordinates, or ob j ect-coordinates

Table 15-10 Vertex Shader Special Global Variables

While you’re able to set the output vertex position to any homogenous coordinate you might like, the final value of gl_Position is usually computed within a vertex program as:

tmp73ec-153_thumb

Or, if you’re employing a multi-pass algorithm (one rendering multiple images from the same geometry) that needs to have the results of the vertex shader and fixed function pipeline be consistent, set gi_Position using the following code:

tmp73ec-154_thumb

gi_Pointsize controls the output size of a point, similar to glPointSize(), but on a per-vertex basis. To control the size of points from within vertex programs, call glEnable() with a value of GL_VERTEX_PROGRAM_POINT_ SIZE, which overrides any current point size that may have been specified.

User-defined clipping planes, as specified by glClipPlane(), can be used by writing a homogeneous coordinate into the gl_ciipvertex variable. For clipping to proceed correctly, the clipping plane specified and the coordinate written into gi_ciipvertex must be in the same coordinate space. The common space for clipping is eye-coordinates. You can transform the current vertex into eye-coordinates for clipping by executing:

tmp73ec-155_thumb

Varying Output Variables

Table 15-11 represents those variables that can be written to in a vertex shader and have their values readable in a fragment shader. The values in the fragment shader are iterated across the fragments (or samples, if multisampling) of the primitives.

Variable Name

Type

Description

gLFrontColor

vec4

Primary color to be used for front-facing primitives

gl_BackColor

vec4

Primary color to be used for back-facing primitives

gl_FrontSecondaryColor

vec4

Secondary color to be used for front-facing primitives

gl_BackSecondaryColor

vec4

Secondary color to be used for back-facing primitives

gl_TexCoord[n]

vec4

tmp73ec-156

gl_FogFragCoord

vec4

Fragment fog coordinate value

Table 15-11 Vertex Shader Varying Global Variables

A vertex shader has the capability of setting both the front- and back-face color values for a vertex. Be default, regardless of the values set from within a vertex shader, the front-facing color will be chosen. This behavior can be modified by calling glEnable(), with a value of GL_VERTEX_PROGRAM_ TWO_SIDE, which causes OpenGL to select colors based on the orientation of the underlying primitive.

Texture Mapping in Vertex Shaders

Texture mapping is available in vertex shaders, but at the time of this writing, it is not a required feature. Nonetheless, using textures in vertex shaders is identical to the process described in "Accessing Textures in GLSL".Automatic mipmap selection is not done in vertex shaders. However, you can manually select which mipmap level using the texture*Lod routines in GLSL.

To determine if your implementation is capable of using textures in vertex shaders, query GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS using gllntegerv(). If a non-zero value is returned, texturing is supported.

Fragment Shaders

Like vertex programs, your OpenGL application can send data directly to a fragment program, as well as having OpenGL provide values for use in your program. All of that data, along with the inputs into a fragment shader, result in the final color and depth value of a fragment. After execution of a fragment shader, the computed values continue through the OpenGL fragment pipeline to the fragment test and blending stages.

Figure 15-8 describes the inputs and outputs of a fragment programs.

 Fragment Shader Built-In Variables

Figure 15-8 Fragment Shader Built-In Variables

Input Values

Fragment shaders receive the iterated values of the final vertex pipeline outputs. These include the resolved primary and secondary colors, a set of texture coordinates, and a fog coordinate distance for the fragment.

Variable

Type

Description

gLColor

vec4

Primary color of the fragment

gl_SecondayColor

bool

Secondary color of the fragment

gl_TexCoord[«|

vec4

tmp73ec-158

gl_FogFragCoord

float

Fragment's fog coordinate

Table 15-12 Fragment Shader Varying Global Variables

Special Output Values

Input values are combined in a fragment program to produce the final values for fragment, as shown in Table 15-13.

Variable

Type

Description

gl_FragColor

vec4

Final color of the fragment

gl_FragDepth

float

Final depth of the fragment

gl_FragData[rc]

vec4

Data value written to the nth data buffer

Table 15-13 Fragment Shader Output Global Variables

gi_FragCoior is the final color of the fragment. While writing to gi_FragCoior isn’t required output from a fragment program, the results of the final fragment color for writing into the color buffer are undefined.

gl_FragDepth is the final depth of the fragment and is utilized in the depth test. While you cannot modify the (x, y) coordinate of a fragment, the depth value can be modified.

Finally, additional data may be written from a fragment program.

Rendering to Multiple Output Buffers

A fragment shader may output values to multiple buffers simultaneously using the gi_FragData array. Writing a value into array element gl_FragData [n] will cause the color to be written into the appropriate fragment in the buffer in element n of the array passed to glDrawBuffers().

A fragment shader may write to either gi_FragCoior or gi_FragData in a shader, but not both.

Next post:

Previous post: