Chapter 7. OpenGL Shading Language API

In support of the OpenGL Shading Language, more than 30 new entry points were added to OpenGL in version 2.0. This set of API calls is referred to throughout this book as the OPENGL SHADING LANGUAGE API. In this chapter, we look at the OpenGL entry points that have been added to create, load, compile, and link shaders, as well as the entry points that have been added for passing generic vertex attributes and uniform variables to shaders. Reference pages for all of the OpenGL Shading Language API entry points are found in Appendix B.

At the end of this chapter, we discuss the application code that is needed to create and use the brick shader presented in Chapter 6. If you just can’t wait, go ahead and sneak a peek at Section 7.13, and then come back here to learn the details of the API.

Here is an overview of creating and using OpenGL shaders:

  1. Create one or more (empty) shader objects with glCreateShader.

  2. Provide source code for these shaders with glShaderSource.

  3. Compile each of the shaders with glCompileShader.

  4. Create a program object with glCreateProgram.

  5. Attach all the shader objects to the program object with glAttachShader.

  6. Link the program object with glLinkProgram.

  7. Install the executable program as part of OpenGL’s current state with glUseProgram.

  8. If the shaders use user-defined uniform variables, query the locations of these variables with glGetUniformLocation and then set their values with glUniform.

If the vertex shader uses user-defined attribute variables, the application must provide values for them, using OpenGL API calls that place attribute values in generic, numbered vertex attribute locations. Before such attribute data is passed to the shader, the index of the generic vertex attribute should be associated with an attribute variable in a vertex shader in one of two ways. Applications can create this association explicitly by calling glBindAttribLocation before linking. Alternatively, if no explicit association is made, OpenGL makes these associations automatically during linking. An application can query the assignment that was made with glGetAttribLocation. Thereafter, generic vertex attributes can be passed to a vertex shader with glVertexAttrib or with glVertexAttribPointer and glEnableVertexArrayPointer in conjunction with standard OpenGL commands to draw vertex arrays.

Obtaining Version Information

With the addition of the OpenGL Shading Language, the OpenGL version number was changed from 1.5 to 2.0. The number before the period is referred to as the major version number and the number after the period is referred to as the minor version number. This did not reflect a change in compatibility, as is often the case when a product’s major version number is changed. Instead, the OpenGL ARB believed that inclusion of a high-level shading language was a major addition to OpenGL. To call attention to this important capability, the committee decided that a change to OpenGL’s major version number was warranted.

This caused some incompatibility with applications that were written assuming that OpenGL would never have a major version number greater than 1. The OpenGL Shading Language also has a version number since it is expected that it too will have additional features added over time. Both of these values can be queried with the OpenGL function glGetString.

To write applications that will work properly in a variety of OpenGL environments and that will stand the test of time, be sure to properly query and parse the OpenGL and OpenGL Shading Language version strings. Both strings are defined as

<version number><space><vendor-specific information>

The version number is defined to be either

majorVersionNumber.minorVersionNumber

or

majorVersionNumber.minorVersionNumber.releaseNumber

where each component contains one or more digits. The vendor specification information and the release number are optional and might not appear in the version string. The version number is not a floating-point number, but a series of integers separated by periods.

To determine the OpenGL version number, call glGetString with the symbolic constant GL_VERSION. To determine the OpenGL Shading Language version, call glGetString with the symbolic constant GL_SHADING_LANGUAGE_VERSION. The shading language version that was approved at the time OpenGL 2.0 was approved was 1.10.

Listing 7.1 contains code for C functions that query and parse the OpenGL and OpenGL Shading Language version strings. Both functions assume that a valid OpenGL context already exists, and both return 0 for the major and minor number if an error is encountered. Values returned by these functions can be tested to see if the underlying implementation provides the necessary support.

Example 7.1. C functions to obtain OpenGL and OpenGL Shading Language version information

void getGlVersion(int *major, int *minor)
{
    const char *verstr = (const char *) glGetString(GL_VERSION);
    if ((verstr == NULL) || (sscanf(verstr,"%d.%d", major, minor) != 2))
    {
        *major = *minor = 0;
        fprintf(stderr, "Invalid GL_VERSION format!!!
");
    }
}

void getGlslVersion(int *major, int *minor)
{
    int gl_major, gl_minor;
    getGlVersion(&gl_major, &gl_minor);

    *major = *minor = 0;
    if(gl_major == 1)
    {
        /* GL v1.x can only provide GLSL v1.00 as an extension */
        const char *extstr = (const char *) glGetString(GL_EXTENSIONS);
        if ((extstr != NULL) &&
            (strstr(extstr, "GL_ARB_shading_language_100") != NULL))
        {
            *major = 1;
            *minor = 0;
        }
    }
    else if (gl_major >= 2)
    {
        /* GL v2.0 and greater must parse the version string */
        const char *verstr =
            (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);

        if((verstr == NULL) ||
            (sscanf(verstr, "%d.%d", major, minor) != 2))
        {
            *major = *minor = 0;
            fprintf(stderr,
                "Invalid GL_SHADING_LANGUAGE_VERSION format!!!
");
        }
    }
}

Creating Shader Objects

The design of the OpenGL Shading Language API mimics the process of developing a C or C++ application. The first step is to create the source code. The source code must then be compiled, the various compiled modules must be linked, and finally the resulting code can be executed by the target processor.

To support the concept of a high-level shading language within OpenGL, the design must provide storage for source code, compiled code, and executable code. The solution to this problem is to define two new OpenGL-managed data structures, or objects. These objects provide the necessary storage, and operations on these objects have been defined to provide functionality for specifying source code and then compiling, linking, and executing the resulting code. When one of these objects is created, OpenGL returns a unique identifier for it. This identifier can be used to manipulate the object and to set or query the parameters of the object.

The first step toward utilizing programmable graphics hardware is to create a shader object. This creates an OpenGL-managed data structure that can store the shader’s source code. The command to create a shader is

GLuint glCreateShader(GLenum shaderType)

Creates an empty shader object and returns a non-zero value by which it can be referenced. A shader object maintains the source code strings that define a shader. shaderType specifies the type of shader to be created. Two types of shaders are supported. A shader of type GL_VERTEX_SHADER is a shader that runs on the programmable vertex processor; it replaces the fixed functionality vertex processing in OpenGL. A shader of type GL_FRAGMENT_SHADER is a shader that runs on the programmable fragment processor; it replaces the fixed functionality fragment processing in OpenGL.

When created, a shader object’s GL_SHADER_TYPE parameter is set to either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER, depending on the value of shaderType.

After a shader object is created, strings that define the shader’s source code must be provided. The source code for a shader is provided as an array of strings. The command for defining a shader’s source code is

void glShaderSource(GLuint shader,                     GLsizei count,                    const GLchar **string,                    const GLint *length)

Sets the source code in shader to the source code in the array of strings specified by string. Any source code previously stored in the shader object is completely replaced. The number of strings in the array is specified by count. If length is NULL, then each string is assumed to be null terminated. If length is a value other than NULL, it points to an array containing a string length for each of the corresponding elements of string. Each element in the length array can contain the length of the corresponding string (the null character is not counted as part of the string length) or a value less than 0 to indicate that the string is null terminated. The source code strings are not scanned or parsed at this time; they are simply copied into the specified shader object. An application can modify or free its copy of the source code strings immediately after the function returns.

The multiple strings interface provides a number of benefits, including

  • A way to organize common pieces of source code (for instance, the varying variable definitions that are shared between a vertex shader and a fragment shader)

  • A way to share prefix strings (analogous to header files) between shaders

  • A way to share #define values to control the compilation process

  • A way to include user-defined or third-party library functions

Compiling Shader Objects

After the source code strings have been loaded into a shader object, the source code must be compiled to check its validity. The result of compilation remains as part of the shader object until another compilation operation occurs or until the shader object itself is deleted. The command to compile a shader object is

void glCompileShader(GLuint shader)

Compiles the source code strings that have been stored in the shader object specified by shader.

The compilation status is stored as part of the shader object’s state. This value is set to GL_TRUE if the shader was compiled without errors and is ready for use, and GL_FALSE otherwise. It can be queried by calling glGetShader with arguments shader and GL_COMPILE_STATUS.

A shader will fail to compile if it is lexically, grammatically, or semantically incorrect. Whether or not the compilation was successful, information about the compilation can be obtained from the shader object’s information log with glGetShaderInfoLog.

The OpenGL Shading Language has compilation rules that are slightly different depending on the type of shader being compiled, and so the compilation takes into consideration whether the shader is a vertex shader or a fragment shader.

Information about the compile operation can be obtained by calling glGetShaderInfoLog (described in Section 7.6) with shader, but the information log should not be used as an indication of whether the compilation was successful. If the shader object was compiled successfully, either the information log is an empty string or it contains information about the compile operation. If the shader object was not compiled successfully, the information log contains information about any lexical, grammatical, or semantic errors that occurred, along with warning messages and any other information the compiler deems pertinent.

Linking and Using Shaders

Each shader object is compiled independently. To create a program, applications need a mechanism for specifying a list of shader objects to be linked. You can specify the list of shaders objects to be linked by creating a program object and attaching to it all the shader objects needed to create the program.

To create a program object, use the following command:

GLuint glCreateProgram(void)

Creates an empty program object and returns a non-zero value by which it can be referenced. A program object is an object to which shader objects can be attached. This provides a mechanism to specify the shader objects that will be linked to create a program. It also provides a means for checking the compatibility between shaders that will be used to create a program (for instance, checking the compatibility between a vertex shader and a fragment shader). When no longer needed as part of a program object, shader objects can be detached.

After the program object has been defined, shader objects can be attached to it. Attaching simply means creating a reference to the shader object so that it will be included when an attempt to link a program object is made. This is the application’s way of describing the recipe for creating a program. The command to attach a shader object to a program object is

void glAttachShader(GLuint program,                    GLuint shader)

Attaches the shader object specified by shader to the program object specified by program. This indicates that shader will be included in link operations that are performed on program.

There is no inherent limit on the number of shader objects that can be attached to a program object. All operations that can be performed on a shader object are valid whether or not the shader object is attached to a program object. It is permissible to attach a shader object to a program object before source code has been loaded into the shader object or before the shader object has been compiled. It is also permissible to attach a shader object to more than one program object. In other words, glAttachShader simply specifies the set of shader objects to be linked.

To create a valid program, all the shader objects attached to a program object must be compiled and the program object itself must be linked. The link operation assigns locations for uniform variables, initializes user-defined uniform variables, resolves references between independently compiled shader objects, and checks to make sure the vertex and fragment shaders are compatible with one another. To link a program object, use the command

void glLinkProgram(GLuint program)

Links the program object specified by program. If any shader objects of type GL_VERTEX_SHADER are attached to program, they are used to create an executable that will run on the programmable vertex processor. If any shader objects of type GL_FRAGMENT_SHADER are attached to program, they are used to create an executable that will run on the programmable fragment processor.

The status of the link operation is stored as part of the program object’s state. This value is set to GL_TRUE if the program object was linked without errors and is ready for use and set to GL_FALSE otherwise. It can be queried by calling glGetProgram with arguments program and GL_LINK_STATUS.

As a result of a successful link operation, all active user-defined uniform variables (see Section 7.8) belonging to program are initialized to 0, and each of the program object’s active uniform variables is assigned a location that can be queried with glGetUniformLocation. Also, any active user-defined attribute variables (see Section 7.7) that have not been bound to a generic vertex attribute index are bound to one at this time.

If program contains shader objects of type GL_VERTEX_SHADER but it does not contain shader objects of type GL_FRAGMENT_SHADER, the vertex shader is linked to the implicit interface for fixed functionality fragment processing. Similarly, if program contains shader objects of type GL_FRAGMENT_SHADER but it does not contain shader objects of type GL_VERTEX_SHADER, the fragment shader is linked to the implicit interface for fixed functionality vertex processing.

glLinkProgram also installs the generated executables as part of the current rendering state if the link operation was successful and the specified program object is already currently in use as a result of a previous call to glUseProgram. If the program object currently in use is relinked unsuccessfully, its link status is set to GL_FALSE, but the previously generated executables and associated state remain part of the current state until a subsequent call to glUseProgram removes them. After they are removed, they cannot be made part of current state until the program object has been successfully relinked.

Linking of a program object can fail for a number of reasons.

  • The number of active attribute variables supported by the implementation has been exceeded.

  • The number of active uniform variables supported by the implementation has been exceeded.

  • The main function is missing for the vertex shader or the fragment shader.

  • A varying variable actually used in the fragment shader is not declared with the same type (or is not declared at all) in the vertex shader.

  • A reference to a function or variable name is unresolved.

  • A shared global is declared with two different types or two different initial values.

  • One or more of the attached shader objects has not been successfully compiled.

  • Binding a generic attribute matrix caused some rows of the matrix to fall outside the allowed maximum of GL_MAX_VERTEX_ATTRIBS.

  • Not enough contiguous vertex attribute slots could be found to bind attribute matrices.

The program object’s information log is updated at the time of the link operation. If the link operation is successful, a program is generated. It may contain an executable for the vertex processor, an executable for the fragment processor, or both. Whether the link operation succeeds or fails, the information and executables from the previous link operation will be lost. After the link operation, applications are free to modify attached shader objects, compile attached shader objects, detach shader objects, and attach additional shader objects. None of these operations affect the information log or the program that is part of the program object until the next link operation on the program object.

Information about the link operation can be obtained by calling glGetProgramInfoLog (described in Section 7.6) with program. If the program object was linked successfully, the information log is either an empty string or contains information about the link operation. If the program object was not linked successfully, the information log contains information about any link errors that occurred, along with warning messages and any other information the linker chooses to provide.

When the link operation has completed successfully, the program it contains can be installed as part of the current rendering state. The command to install the program as part of the rendering state is

void glUseProgram(GLuint program)

Installs the program object specified by program as part of current rendering state.

A program object contains an executable that will run on the vertex processor if it contains one or more shader objects of type GL_VERTEX_SHADER that have been successfully compiled and linked. Similarly, a program object contains an executable that will run on the fragment processor if it contains one or more shader objects of subtype GL_FRAGMENT_SHADER that have been successfully compiled and linked.

If program contains shader objects of type GL_VERTEX_SHADER but it does not contain shader objects of type GL_FRAGMENT_SHADER, an executable is installed on the vertex processor but fixed functionality is used for fragment processing. Similarly, if program contains shader objects of type GL_FRAGMENT_SHADER but it does not contain shader objects of type GL_VERTEX_SHADER, an executable is installed on the fragment processor but fixed functionality is used for vertex processing. If program is 0, the programmable processors are disabled, and fixed functionality is used for both vertex and fragment processing.

Successfully installing an executable on a programmable processor causes the corresponding fixed functionality of OpenGL to be disabled. Specifically, if an executable is installed on the vertex processor, the OpenGL fixed functionality is disabled as described in Section 4.1. Similarly, if an executable is installed on the fragment processor, the OpenGL fixed functionality is disabled as described in Section 4.2.

While a program object is in use, applications are free to modify attached shader objects, compile attached shader objects, attach additional shader objects, detach shader objects, delete any shader objects attached, or delete the program object itself. None of these operations affect the executables that are part of the current state. However, relinking the program object that is currently in use installs the program as part of the current rendering state if the link operation was successful. While a program object is in use, the state that controls the disabled fixed functionality can also be updated with the normal OpenGL calls.

Cleaning Up

Objects should be deleted when they are no longer needed, and deletion can be accomplished with the following commands

void glDeleteShader(GLuint shader)

Frees the memory and invalidates the name associated with the shader object specified by shader. This command effectively undoes the effects of a call to glCreateShader.

If a shader object to be deleted is attached to a program object, it will be flagged for deletion, but it will not be deleted until it is no longer attached to any program object for any rendering context (i.e., it must be detached from wherever it was attached before it can be deleted). A value of 0 for shader is silently ignored.

To determine whether a shader object has been flagged for deletion, call glGetShader with arguments shader and GL_DELETE_STATUS.

void glDeleteProgram(GLuint program)

Frees the memory and invalidates the name associated with the program object specified by program. This command effectively undoes the effects of a call to glCreateProgram.

If a program object is in use as part of a current rendering state, it will be flagged for deletion, but it will not be deleted until it is no longer part of current state for any rendering context. If a program object to be deleted has shader objects attached to it, those shader objects are automatically detached but not deleted unless they have already been flagged for deletion by a previous call to glDeleteShader.

To determine whether a program object has been flagged for deletion, call glGetProgram with arguments program and GL_DELETE_STATUS.

When a shader object no longer needs to be attached to a program object, it can be detached with the command

void glDetachShader(GLuint program, GLuint shader)

Detaches the shader object specified by shader from the program object specified by program. This command undoes the effect of the command glAttachShader.

If shader has already been flagged for deletion by a call to glDeleteShader and it is not attached to any other program object, it is deleted after it has been detached.

A programming tip that might be useful in keeping things orderly is to delete shader objects as soon as they have been attached to a program object. They won’t be deleted at this time, but they will be flagged for deletion when they are no longer referenced. To clean up later, the application only needs to delete the program object. All the attached shader objects will be automatically detached, and, because they are flagged for deletion, they will be automatically deleted at that time as well.

Query Functions

The OpenGL Shading Language API contains several functions for querying object state. To obtain information about a shader object, use the following command:

void glGetShaderiv(GLuint shader,                                    GLenum pname,                                    GLint *params)

Returns in params the value of a parameter for a specific shader object. This function returns information about a shader object. Permitted parameters and their meanings are described in Table 7.1. In this table, the value for pname is shown on the left, and the operation performed is shown on the right.

Table 7.1. Queriable shader object parameters

Parameter

Operation

GL_SHADER_TYPE

params returns a value of either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER, depending on whether shader is the name of a vertex shader object or a fragment shader object.

GL_DELETE_STATUS

params returns GL_TRUE if shader is currently flagged for deletion, and GL_FALSE otherwise.

GL_COMPILE_STATUS

params returns GL_TRUE if the last compile operation on shader was successful, and GL_FALSE otherwise.

GL_INFO_LOG_LENGTH

params returns the number of characters in the information log for shader, including the null termination character. If the object has no information log, a value of 0 is returned.

GL_SHADER_SOURCE_LENGTH

params returns the length of the concatenation of the source strings that make up the shader source for shader, including the null termination character. If no source code exists, 0 is returned.

A similar function is provided for querying the state of a program object: the status of an operation on a program object, the number of attached shader objects, the number of active attributes (see Section 7.7), the number of active uniform variables (see Section 7.8), or the length of any of the strings maintained by a program object. The command to obtain information about a program object is

void glGetProgramiv(GLuint program,                                      GLenum pname,                                      GLint *params)

Returns in params the value of a parameter for a particular program object. This function returns information about a program object. Permitted parameters and their meanings are described in Table 7.2. In this table, the value for pname is shown on the left, and the operation performed is shown on the right.

Table 7.2. Queriable program object parameters

Parameter

Operation

GL_DELETE_STATUS

params returns GL_TRUE if program is currently flagged for deletion, and GL_FALSE otherwise.

GL_LINK_STATUS

params returns GL_TRUE if the last link operation on program was successful, and GL_FALSE otherwise.

GL_VALIDATE_STATUS

params returns GL_TRUE if the last validation operation on program was successful, and GL_FALSE otherwise.

GL_INFO_LOG_LENGTH

params returns the number of characters in the information log for program, including the null termination character. If the object has no information log, a value of 0 is returned.

GL_ATTACHED_SHADERS

params returns the number of shader objects attached to program.

GL_ACTIVE_ATTRIBUTES

params returns the number of active attribute variables for program.

GL_ACTIVE_ATTRIBUTE_MAX_LENGTH

params returns the length of the longest active attribute variable name for program, including the null termination character. If no active attribute variables exist, 0 is returned.

GL_ACTIVE_UNIFORMS

params returns the number of active uniform variables for program.

GL_ACTIVE_UNIFORM_MAX_LENGTH

params returns the length of the longest active uniform variable name for program, including the null termination character. If no active uniform variables exist, 0 is returned.

The command to obtain the current shader string from a shader object is

void glGetShaderSource(GLuint shader                                              GLsizei bufSize,                                              GLsizei *length,                                              GLchar *source)

Returns a concatenation of the source code strings from the shader object specified by shader. The source code strings for a shader object are the result of a previous call to glShaderSource. The string returned by the function is null terminated.

glGetShaderSource returns in source as much of the source code string as it can, up to a maximum of bufSize characters. The number of characters actually returned, excluding the null termination character, is specified by length. If the length of the returned string is not required, a value of NULL can be passed in the length argument. The size of the buffer required to store the returned source code string can be obtained by calling glGetShader with the value GL_SHADER_SOURCE_LENGTH.

Information about the compilation operation is stored in the information log for a shader object. Similarly, information about the link and validation operations is stored in the information log for a program object. The information log is a string that contains diagnostic messages and warnings. The information log may contain information useful during application development even if the compilation or link operation was successful. The information log is typically only useful during application development, and an application should not expect different OpenGL implementations to produce identical descriptions of error. To obtain the information log for a shader object, call

void glGetShaderInfoLog(GLuint shader,                                               GLsizei maxLength,                                               GLsizei *length,                                               GLchar *infoLog)

Returns the information log for the specified shader object. The information log for a shader object is modified when the shader is compiled. The string that is returned is null terminated.

glGetShaderInfoLog returns in infoLog as much of the information log as it can, up to a maximum of maxLength characters. The number of characters actually returned, excluding the null termination character, is specified by length. If the length of the returned string is not required, a value of NULL can be passed in the length argument. The size of the buffer required to store the returned information log can be obtained by calling glGetShader with the value GL_INFO_LOG_LENGTH.

The information log for a shader object is a string that may contain diagnostic messages, warning messages, and other information about the last compile operation. When a shader object is created, its information log is a string of length 0.

To obtain the information log for a program object, call

void glGetProgramInfoLog(GLuint program,                                                  GLsizei maxLength,                                                  GLsizei *length,                                                  GLchar *infoLog)

Returns the information log for the specified program object. The information log for a program object is modified when the program object is linked or validated. The string that is returned is null terminated.

glGetProgramInfoLog returns in infoLog as much of the information log as it can, up to a maximum of maxLength characters. The number of characters actually returned, excluding the null termination character, is specified by length. If the length of the returned string is not required, a value of NULL can be passed in the length argument. The size of the buffer required to store the returned information log can be obtained by calling glGetProgram with the value GL_INFO_LOG_LENGTH.

The information log for a program object is an empty string, a string containing information about the last link operation, or a string containing information about the last validation operation. It may contain diagnostic messages, warning messages, and other information. When a program object is created, its information log is a string of length 0.

The way the API is set up, you first need to perform a query to find out the length of the the information log (number of characters in the string). After allocating a buffer of the appropriate size, you can call glGetShaderInfoLog or glGetProgramInfoLog to put the information log string into the allocated buffer. You can then print it if you want to do so. Listing 7.2 shows a C function that does all this for a shader object. The code for obtaining the information log for a program object is almost identical.

Example 7.2. C function to print the information log for an object

void printShaderInfoLog(GLuint shader)
{
    int infologLen = 0;
    int charsWritten  = 0;
    GLchar *infoLog;

    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infologLen);
    printOpenGLError();  // Check for OpenGL errors
  
    if (infologLen > 0)
    { 
        infoLog = (GLchar*) malloc(infologLen);
        if (infoLog == NULL)
        {
            printf("ERROR: Could not allocate InfoLog buffer
");
            exit(1);
        }
        glGetShaderInfoLog(shader, infologLen, &charsWritten, infoLog);
        printf("InfoLog:
%s

", infoLog);
        free(infoLog);
    }
    printOpenGLError(); // Check for OpenGL errors
}

You can obtain the program object that is currently in use by calling glGet with the symbolic constant GL_CURRENT_PROGRAM.

The command to query a list of shader objects attached to a particular program object is

void glGetAttachedShaders(GLuint program,                                                    GLsizei maxCount,                                                    GLsizei *count,                                                    GLuint *shaders)

Returns the handles of the shader objects attached to program. It returns in shaders as many of the handles of these shader objects as it can, up to a maximum of maxCount. The number of handles actually returned is specified by count. If the number of handles actually returned is not required (for instance, if it has just been obtained with glGetProgram), a value of NULL may be passed for count. If no shader objects are attached to program, a value of 0 is returned in count. The actual number of attached shaders can be obtained by calling glGetProgram with the value GL_ATTACHED_SHADERS.

Two new functions have been added to determine whether an object is a shader object or a program object. These functions may be useful if you have to process an object (for instance, to print its information log) without knowing whether it is a valid shader or program object. These two functions are defined as

GLboolean glIsShader(GLuint shader)

Returns GL_TRUE if shader is the name of a shader object. If shader is zero or a non-zero value that is not the name of a shader object, glIsShader returns GL_FALSE.

GLboolean glIsProgram(GLuint program)

Returns GL_TRUE if program is the name of a program object. If program is zero or a non-zero value that is not the name of a program object, glIsProgram returns GL_FALSE.

Specifying Vertex Attributes

One way you can pass vertex data to OpenGL is by calling glBegin, followed by some sequence of glColor/glNormal/glVertex/etc. A call to glEnd terminates this method of specifying vertex data.

These calls continue to work in the OpenGL programmable environment. As before, a call to glVertex indicates that the data for an individual vertex is complete and should be processed. However, if a valid vertex shader has been installed with glUseProgram, the vertex data is processed by that vertex shader instead of by the usual fixed functionality of OpenGL. A vertex shader can use the following built-in variables to access the standard types of vertex data passed to OpenGL:

attribute vec4 gl_Color;
attribute vec4 gl_SecondaryColor;
attribute vec3 gl_Normal;
attribute vec4 gl_Vertex;
attribute vec4 gl_MultiTexCoord0;
attribute vec4 gl_MultiTexCoord1;
attribute vec4 gl_MultiTexCoord2;
. . .
attribute vec4 gl_FogCoord;

OpenGL’s vertex-at-a-time interface is simple and powerful, but on today’s systems it is definitely not the highest-performance way of transferring vertex data to the graphics accelerator. Whenever possible, applications should use the vertex array interface instead. This interface allows you to store vertex data in arrays and set pointers to those arrays. Instead of sending one vertex at a time to OpenGL, you can send a whole set of primitives at a time. With vertex buffer objects, it is even possible that vertex arrays are stored in memory on the graphics board to exact maximum performance.

The vertex array interface also works the same way in the OpenGL programmable environment as it did previously. When a vertex array is sent to OpenGL, the vertex data in the vertex array is processed one vertex at a time, just like the vertex-at-a-time interface. If a vertex shader is active, each vertex is processed by the vertex shader rather than by the fixed functionality of OpenGL.

However, the brave new world of programmability means that applications no longer need to be limited to the standard attributes defined by OpenGL. There are many additional per-vertex attributes that applications might like to pass into a vertex shader. It is easy to imagine that applications will want to specify per-vertex data such as tangents, temperature, pressure, and who knows what else. How do we allow applications to pass nontraditional attributes to OpenGL and operate on them in vertex shaders?

The answer is that OpenGL provides a small number of generic locations for passing in vertex attributes. Each location is numbered and has room to store up to four floating-point components (i.e., it is a vec4). An implementation that supports 16 attribute locations will have them numbered from 0 to 15. An application can pass a vertex attribute into any of the generic numbered slots by using one of the following functions:

void glVertexAttrib{1|2|3|4}{s|f|d}(GLuint index, TYPE v) void glVertexAttrib{1|2|3}{s|f|d}v(GLuint index, const TYPE *v) void glVertexAttrib4{b|s|i|f|d|ub|us|ui}v(GLuint index, const TYPE *v)

Sets the generic vertex attribute specified by index to the value specified by v. This command can have up to three suffixes that differentiate variations of the parameters accepted. The first suffix can be 1, 2, 3, or 4 to specify whether v contains 1, 2, 3, or 4 components. If the second and third components are not provided, they are assumed to be 0, and if the fourth component is not provided, it is assumed to be 1. The second suffix indicates the data type of v and may specify byte (b), short (s), int (i), float (f), double (d), unsigned byte (ub), unsigned short (us), or unsigned int (ui). The third suffix is an optional v meaning that v is a pointer to an array of values of the specified data type.

This set of commands has a certain set of rules for converting data to the floating-point internal representation specified by OpenGL. Floats and doubles are mapped into OpenGL internal floating-point values as you would expect, and integer values are converted to floats by a decimal point added to the right of the value provided. Thus, a value of 27 for a byte, int, short, unsigned byte, unsigned int, or unsigned short becomes a value of 27.0 for computation within OpenGL.

Another set of entry points supports the passing of normalized values as generic vertex attributes:

void glVertexAttrib4Nub(GLuint index, TYPE v) void glVertexAttrib4N{b|s|i|f|d|ub|us|ui}v(GLuint index, const TYPE *v)

Sets the generic vertex attribute specified by index to the normalized value specified by v. In addition to N (to indicate normalized values), this command can have two suffixes that differentiate variations of the parameters accepted. The first suffix indicates the data type of v and specifies byte (b), short (s), int (i), float (f), double (d), unsigned byte (ub), unsigned short (us), or unsigned int (ui). The second suffix is an optional v meaning that v is a pointer to an array of values of the specified data type.

N in a command name indicates that, for data types other than float or double, the arguments will be linearly mapped to a normalized range in the same way as data provided to the integer variants of glColor or glNormal—that is, for signed integer variants of the functions, the most positive, representable value maps to 1.0, and the most negative representable value maps to –1.0. For the unsigned integer variants, the largest representable value maps to 1.0, and the smallest representable value maps to 0.

Attribute variables are allowed to be of type mat2, mat3, or mat4. Attributes of these types can be loaded with the glVertexAttrib entry points. Matrices must be loaded into successive generic attribute slots in column major order, with one column of the matrix in each generic attribute slot. Thus, to load a mat4 attribute, you would load the first column in generic attribute slot i, the second in slot i + 1, the third in slot i + 2, and the fourth in slot i + 3.

With one exception, generic vertex attributes are just that—generic. They pass additional color values, tangents, binormals, depth values, or anything. The exception is that the generic vertex attribute with index 0 indicates the completion of a vertex just like a call to glVertex.

A glVertex2, glVertex3, or glVertex4 command is completely equivalent to the corresponding glVertexAttrib command with an index argument of 0. There are no current values for generic vertex attribute 0 (an error is generated if you attempt to query its current value). This is the only generic vertex attribute with this property; calls to set other standard vertex attributes can be freely mixed with calls to set any of the other generic vertex attributes. You are also free to mix calls to glVertex and glVertexAttrib with index 0.

The vertex array API has been similarly extended to allow generic vertex attributes to be specified as vertex arrays. The following call establishes the vertex array pointer for a generic vertex attribute:

void glVertexAttribPointer(GLuint index,                                                 GLint size,                                                 GLenum type,                                                 GLboolean normalized,                                                 GLsizei stride,                                                 const GLvoid *pointer)

Specifies the location and data format of an array of generic vertex attribute values to use when rendering. The generic vertex attribute array to be specified is indicated by index. size specifies the number of components per attribute and must be 1, 2, 3, or 4. type specifies the data type of each component (GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT, or GL_DOUBLE). stride specifies the byte stride from one attribute to the next, allowing attribute values to be intermixed with other attribute values or stored in a separate array. A value of 0 for stride means that the values are stored sequentially in memory with no gaps between successive elements. If set to GL_TRUE, normalize specifies that values stored in an integer format are to be mapped to the range [–1.0,1.0] (for signed values) or [0.0,1.0] (for unsigned values) when they are accessed and converted to floating point. Otherwise, values are converted to floats directly without normalization. pointer is the memory address of the first generic vertex attribute in the vertex array.

After the vertex array information has been specified for a generic vertex attribute array, the array needs to be enabled. When enabled, the generic vertex attribute data in the specified array is provided along with other enabled vertex array data when vertex array drawing commands such as glDrawArrays are called. To enable or disable a generic vertex attribute array, use the commands

void glEnableVertexAttribArray(GLuint index) void glDisableVertexAttribArray(GLuint index)

Enables or disables the generic vertex attribute array specified by index. By default, all client-side capabilities are disabled, including all generic vertex attribute arrays. If enabled, the values in the generic vertex attribute array are accessed and used for rendering when calls are made to vertex array commands such as glArrayElement, glDrawArrays, glDrawElements, glDrawRangeElements, glMultiDrawArrays, or glMultiDrawElements.

This solves the question of how generic vertex data is passed into OpenGL, but how do we access that data from within a vertex shader? We don’t want to refer to these numbered locations in our shader, because this approach is not very descriptive and is prone to errors. The OpenGL Shading Language API provides two ways for associating generic vertex indices with vertex shader attribute variables.

The first way is to let the linker assign the bindings automatically. In this case, the application would need to query OpenGL after linking to determine the generic vertex indices that were assigned and then would use these indices when passing the attributes to OpenGL.

The second way is for the application to choose the index value of the generic vertex attribute to be used and explicitly bind it to a specific attribute variable in the vertex shader by using the following function before linking occurs:

void glBindAttribLocation(GLuint program,                                                GLuint index,                                                const GLchar *name)

Associates a user-defined attribute variable in the program object specified by program with a generic vertex attribute index. The name of the user-defined attribute variable is passed as a null terminated string in name. If name was bound previously, that information is lost. Thus, you cannot bind one user-defined attribute variable to multiple indices, but you can bind multiple user-defined attribute variables to the same index. The generic vertex attribute index to be bound to this variable is specified by index. When program is made part of current state, values provided through the generic vertex attribute index modify the value of the user-defined attribute variable specified by name.

If name refers to a matrix attribute variable, index refers to the first column of the matrix. Other matrix columns are then automatically bound to locations index+1 for a matrix of type mat2; index+1 and index+2 for a matrix of type mat3; and index+1, index+2, and index+3 for a matrix of type mat4.

Applications are not allowed to bind any of the standard OpenGL vertex attributes with this command because they are bound automatically when needed. Any attribute binding that occurs after the program object has been linked does not take effect until the next time the program object is linked.

glBindAttribLocation can be called before any vertex shader objects are attached to the specified program object. It is also permissible to bind an attribute variable name that is never used in a vertex shader to a generic attribute index.

Applications are allowed to bind more than one vertex shader attribute name to the same generic vertex attribute index. This is called ATTRIBUTE ALIASING, and it is allowed only if just one of the aliased attributes is active in the executable program or if no path through the shader consumes more than one attribute of a set of attributes aliased to the same location. Another way of saying this is that more than one attribute name may be bound to a generic attribute index if, in the end, only one name is used to access the generic attribute in the vertex shader. The compiler and linker are allowed to assume that no aliasing is done and are free to employ optimizations that work only in the absence of aliasing. OpenGL implementations are not required to do error checking to detect attribute aliasing. Because there is no way to bind standard attributes, it is not possible to alias generic attributes with conventional ones.

The binding between an attribute variable name and a generic attribute index can be specified at any time with glBindAttribLocation. Attribute bindings do not go into effect until glLinkProgram is called, so any attribute variables that need to be bound explicitly for a particular use of a shader should be bound before the link operation occurs. After a program object has been linked successfully, the index values for attribute variables remain fixed (and their values can be queried) until the next link command occurs. To query the attribute binding for a named vertex shader attribute variable, use glGetAttribLocation. It returns the binding that actually went into effect the last time glLinkProgram was called for the specified program object. Attribute bindings that have been specified since the last link operation are not returned by glGetAttribLocation.

GLint glGetAttribLocation(GLuint program,                                                 const GLchar *name)

Queries the previously linked program object specified by program for the attribute variable specified by name and returns the index of the generic vertex attribute that is bound to that attribute variable. If name is a matrix attribute variable, the index of the first column of the matrix is returned. If the named attribute variable is not an active attribute in the specified program object or if name starts with the reserved prefix “gl_”, a value of –1 is returned.

Using these functions, we can create a vertex shader that contains a user-defined attribute variable named Opacity that is used directly in the lighting calculations. We can decide that we want to pass per-vertex opacity values in generic attribute location 1 and set up the proper binding with the following line of code:

glBindAttribLocation(myProgram, 1, "Opacity");

Subsequently, we can call glVertexAttrib to pass an opacity value at every vertex:

glVertexAttrib1f(1, opacity);

The glVertexAttrib calls are all designed for use between glBegin and glEnd. As such, they offer replacements for the standard OpenGL calls such as glColor, glNormal, and so on. But as we have already pointed out, vertex arrays should be used if graphics performance is a concern.

The jargon in this section can get a little confusing, so let’s look at a diagram to make sure we have things straight. Figure 7.1 illustrates how commands to set standard vertex attributes modify the values of built-in attribute variables defined by the OpenGL Shading Language. The mappings between commands to set standard attributes (color, normal, vertex, etc.) and the built-in attribute variables (gl_Color, gl_Normal, gl_Vertex, etc.) are done automatically, and they are done in a way that doesn’t conflict with the use of any generic attribute location that will be used. Each of these calls except glVertex also sets the current state for that attribute. (The value provided in a call to glVertex is never saved as part of current state.) The value for a built-in attribute variable is automatically updated when a call is made to set the value of the corresponding standard vertex attribute.

Mapping of standard vertex attribute commands to built-in attribute variables

Figure 7.1. Mapping of standard vertex attribute commands to built-in attribute variables

Now let’s look at the case of generic vertex attributes as illustrated in Figure 7.2. A user-defined attribute variable must be bound to a generic vertex attribute index. This binding can be done with glBindAttribLocation, or it can happen implicitly at link time.

Mapping of generic vertex attribute commands to user-defined attribute variables

Figure 7.2. Mapping of generic vertex attribute commands to user-defined attribute variables

Let’s assume we have a vertex shader that uses three user-defined attribute variables: Opacity, Binormal, and MyData. These are shown on the right side of Figure 7.2. These user-defined attribute variables can each be bound to a generic vertex attribute index as follows:

glBindAttribLocation(myProgram, 1, "Opacity");
glBindAttribLocation(myProgram, 2, "Binormal");
glBindAttribLocation(myProgram, 3, "MyData");

This sets up the mapping so that values written into generic vertex attribute locations 1, 2, and 3 will modify the values of the attribute variables Opacity, Binormal, and MyData in the vertex shader. Generic vertex attribute 0 can be bound to a user-defined attribute variable, or its value can be obtained through the built-in attribute variable gl_Vertex. The diagram shows that generic vertex attribute index N is not currently bound to any user-defined attribute variable.

As mentioned, each of the generic attribute locations has enough room for four floating-point components. Applications are permitted to store 1, 2, 3, or 4 components in each location. A vertex shader may access a single location by using a user-defined attribute variable that is a float, a vec2, a vec3, or a vec4. It may access two consecutive locations by using a user-defined attribute variable that is a mat2, three using a mat3, and four using a mat4.

The bindings between generic attribute index values and user-defined attribute variables (i.e., the arrows on the right side of Figure 7.2) are part of the state maintained within a program object, whereas the contents of the attribute array itself is considered current attribute state (except for the generic vertex attribute with index 0). The application can provide a different program object and specify different names and mappings for attribute variables in the vertex shader, and if no calls have been made to update the attribute values in the interim, the attribute variables in the new vertex shader get the values left behind by the previous one.

Attribute variables that can be accessed when a vertex shader is executed are called ACTIVE ATTRIBUTES. To obtain information about an active attribute, use the following command:

void glGetActiveAttrib(GLuint program,                                         GLuint index,                                         GLsizei bufSize,                                         GLsizei *length,                                         GLint *size,                                         GLenum *type,                                         GLchar *name)

Returns information about an active attribute variable in the program object specified by program. The number of active attributes in a program object can be obtained by calling glGetProgram with the value GL_ACTIVE_ATTRIBUTES. A value of 0 for index causes information about the first active attribute variable to be returned. Permissible values for index range from 0 to the number of active attributes minus 1.

A vertex shader can use built-in attribute variables, user-defined attribute variables, or both. Built-in attribute variables have a prefix of “gl_” and reference conventional OpenGL vertex attributes (e.g., gl_Vertex, gl_Normal, etc.; see Section 4.1.1 for a complete list.) User-defined attribute variables have arbitrary names and obtain their values through numbered generic vertex attributes. An attribute variable (either built-in or user-defined) is considered active if it is determined during the link operation that it can be accessed during program execution. Therefore, program should have previously been the target of a call to glLinkProgram, but it is not necessary for it to have been linked successfully.

The size of the character buffer needed to store the longest attribute variable name in program can be obtained by calling glGetProgram with the value GL_ACTIVE_ATTRIBUTE_MAX_LENGTH. This value should be used to allocate a buffer of sufficient size to store the returned attribute name. The size of this character buffer is passed in bufSize, and a pointer to this character buffer is passed in name.

glGetActiveAttrib returns the name of the attribute variable indicated by index, storing it in the character buffer specified by name. The string returned is null terminated. The actual number of characters written into this buffer is returned in length, and this count does not include the null termination character. If the length of the returned string is not required, a value of NULL can be passed in the length argument.

The type argument returns a pointer to the attribute variable’s data type. The symbolic constants GL_FLOAT, GL_FLOAT_VEC2, GL_FLOAT_VEC3, GL_FLOAT_VEC4, GL_FLOAT_MAT2, GL_FLOAT_MAT3, and GL_FLOAT_MAT4 may be returned. The size argument returns the size of the attribute in units of the type returned in type.

The list of active attribute variables may include both built-in attribute variables (which begin with the prefix “gl_”) as well as user-defined attribute variable names.

This function returns as much information as it can about the specified active attribute variable. If no information is available, length is 0 and name is an empty string. This situation could occur if this function is called after a link operation that failed. If an error occurs, the return values length, size, type, and name are unmodified.

The glGetActiveAttrib command can be useful in an environment in which shader development occurs separately from application development. If some attribute-naming conventions are agreed to between the shader writers and the application developers, the latter could query the program object at runtime to determine the attributes that are actually needed and could pass those down. This approach can provide more flexibility in the shader development process.

To query the state of a particular generic vertex attribute, call one of the following commands:

void glGetVertexAttribfv(GLuint index,                                             GLenum pname,                                             GLfloat *params)void glGetVertexAttribiv(GLuint index,                                             GLenum pname,                                             GLint *params)void glGetVertexAttribdv(GLuint index,                                             GLenum pname,                                             GLdouble *params)

Returns in params the value of a generic vertex attribute parameter. The generic vertex attribute to be queried is specified by index, and the parameter to be queried is specified by pname. Parameters and return values are summarized in Table 7.3. All the parameters except GL_CURRENT_VERTEX_ATTRIB represent client-side state.

Table 7.3. Generic vertex attribute parameters

Parameter

Operation

GL_VERTEX_ATTRIB_ARRAY_ENABLED

params returns a single value that is non-zero (true) if the vertex attribute array for index is enabled and 0 (false) if it is disabled. The initial value is GL_FALSE.

GL_VERTEX_ATTRIB_ARRAY_SIZE

params returns a single value, the size of the vertex attribute array for index. The size is the number of values for each element of the vertex attribute array, and it is 1, 2, 3, or 4. The initial value is 4.

GL_VERTEX_ATTRIB_ARRAY_STRIDE

params returns a single value, the array stride (number of bytes between successive elements) for the vertex attribute array for index. A value of 0 indicates that the array elements are stored sequentially in memory. The initial value is 0.

GL_VERTEX_ATTRIB_ARRAY_TYPE

params returns a single value, a symbolic constant indicating the array type for the vertex attribute array for index. Possible values are GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT, and GL_DOUBLE. The initial value is GL_FLOAT.

GL_VERTEX_ATTRIB_ARRAY_NORMALIZED

params returns a single value that is nonzero (true) if fixed-point data types for the vertex attribute array indicated by index are normalized when they are converted to floating point and 0 (false) otherwise. The initial value is GL_FALSE.

GL_CURRENT_VERTEX_ATTRIB

params returns four values that represent the current value for the generic vertex attribute specified by index. Generic vertex attribute 0 is unique in that it has no current state, so an error is generated if index is 0. The initial value for all other generic vertex attributes is (0, 0, 0, 1).

void glGetVertexAttribPointerv(GLuint index,                                                          GLenum pname,                                                          GLvoid **pointer)

Returns pointer information. index is the generic vertex attribute to be queried, pname is a symbolic constant specifying the pointer to be returned, and params is a pointer to a location in which to place the returned data. The only accepted value for pname is GL_VERTEX_ATTRIB_ARRAY_POINTER. This causes params to return a single value that is a pointer to the vertex attribute array for the generic vertex attribute specified by index.

Specifying Uniform Variables

As described in the previous section, attribute variables provide frequently modified data to the vertex shader. Less frequently changing data can be specified using uniform variables. Uniform variables are declared within a shader and can be loaded directly by the application. This lets applications provide any type of arbitrary data to a shader. Applications can modify these values as often as every primitive in order to modify the behavior of the shader (although performance may suffer if this is done). Typically, uniform variables are used to supply state that stays constant for many primitives.

The OpenGL Shading Language also defines a number of built-in variables that track OpenGL state. Applications can continue using OpenGL to manage state through existing OpenGL calls and can use these built-in uniform variables in custom shaders. Of course, if you want something that isn’t already supported directly by OpenGL, it is a simple matter to define your own uniform variable and supply the value to your shader.

When a program object is made current, built-in uniform variables that track OpenGL state are initialized to the current value of that OpenGL state. Subsequent calls that modify an OpenGL state value cause the built-in uniform variable that tracks that state value to be updated as well.

The basic model for specifying uniform variables is different from the model for specifying attribute variables. As discussed in the preceding section, for attribute variables, the application can specify the attribute location before linking occurs. In contrast, the locations of uniform variables cannot be specified by the application. Instead, they are always determined by OpenGL at link time. As a result, applications always need to query the uniform location after linking occurs.

To update the value of a user-defined uniform variable, an application needs to determine its location and then specify its value. The locations of uniform variables are assigned at link time and do not change until the next link operation occurs. Each time linking occurs, the locations of uniform variables may change, and so the application must query them again before setting them. The locations of the user-defined uniform variables in a program object can be queried with the following command:

GLint glGetUniformLocation(GLuint program,                                                      const GLchar *name)

Returns an integer that represents the location of a specific uniform variable within a program object. name must be a null terminated string that contains no white space. name must be an active uniform variable name in program that is not a structure, an array of structures, or a subcomponent of a vector or a matrix. This function returns –1 if name does not correspond to an active uniform variable in program or if name starts with the reserved prefix “gl_”.

Uniform variables that are structures or arrays of structures can be queried with glGetUniformLocation for each field within the structure. The array element operator “[]” and the structure field operator “.” can be used in name to select elements within an array or fields within a structure. The result of using these operators is not allowed to be another structure, an array of structures, or a subcomponent of a vector or a matrix. Except if the last part of name indicates a uniform variable array, the location of the first element of an array can be retrieved with the name of the array or with the name appended by “[0]”.

The actual locations assigned to uniform variables are not known until the program object is linked successfully. After linking has occurred, the command glGetUniformLocation can obtain the location of a uniform variable. This location value can then be passed to glUniform to set the value of the uniform variable or to glGetUniform in order to query the current value of the uniform variable. After a program object has been linked successfully, the index values for uniform variables remain fixed until the next link command occurs. Uniform variable locations and values can only be queried after a link if the link was successful.

Loading of user-defined uniform values is only possible for the program object that is currently in use. All user-defined uniform variables are initialized to 0 when a program object is successfully linked. User-defined uniform values are part of the state of a program object. Their values can be modified only when the program object is part of current rendering state, but the values of uniform variables are preserved as the program object is swapped in and out of current state. The following commands load uniform variables into the program object that is currently in use:

void glUniform{1|2|3|4}{f|i}(GLint location, TYPE v)

Sets the user-defined uniform variable or uniform variable array specified by location to the value specified by v. The suffix 1, 2, 3, or 4 indicates whether v contains 1, 2, 3, or 4 components. This value should match the number of components in the data type of the specified uniform variable (e.g., 1 for float, int, bool; 2 for vec2, ivec2, bvec2, etc.). The suffix f indicates that floating-point values are being passed, and the suffix i indicates that integer values are being passed; this type should also match the data type of the specified uniform variable. The i variants of this function should be used to provide values for uniform variables defined as int, ivec2, ivec3, and ivec4, or arrays of these. The f variants should be used to provide values for uniform variables of type float, vec2, vec3, or vec4, or arrays of these. Either the i or the f variants can be used to provide values for uniform variables of type bool, bvec2, bvec3, and bvec4 or arrays of these. The uniform variable is set to false if the input value is 0 or 0.0f, and it is set to true otherwise.

void glUniform{1|2|3|4}{f|i}v(GLint location,                                                    GLuint count,                                                    const TYPE v)

Sets the user-defined uniform variable or uniform variable array specified by location to the values specified by v. These commands pass a count and a pointer to the values to be loaded into a uniform variable or a uniform variable array. A count of 1 should be used for modifying the value of a single uniform variable, and a count of 1 or greater can be used to modify an array. The number specified in the name of the command specifies the number of components for each element in v, and it should match the number of components in the data type of the specified uniform variable (e.g., 1 for float, int, bool; 2 for vec2, ivec2, bvec2, etc.). The v in the command name indicates that a pointer to a vector of values is being passed. The f and i suffixes are defined in the same way as for the nonvector variants of glUniform.

For uniform variable arrays, each element of the array is considered to be of the type indicated in the name of the command (e.g., glUniform3f or glUniform3fv can be used to load a uniform variable array of type vec3). The number of elements of the uniform variable array to be modified is specified by count.

void glUniformMatrix{2|3|4}fv(GLint location,                                                       GLuint count,                                                       GLboolean transpose,                                                       const GLfloat *v)

Sets the user-defined uniform matrix variable or uniform matrix array variable specified by location to the values specified by v. The number in the command name is interpreted as the dimensionality of the matrix. The number 2 indicates a 2 × 2 matrix (i.e., 4 values), the number 3 indicates a 3 × 3 matrix (i.e., 9 values), and the number 4 indicates a 4 × 4 matrix (i.e., 16 values). If transpose is GL_FALSE, each matrix is assumed to be supplied in column major order. If transpose is GL_TRUE, each matrix is assumed to be supplied in row major order. The count argument specifies the number of matrices to be passed. A count of 1 should be used for modifying the value of a single matrix, and a count greater than 1 can be used to modify an array of matrices.

glUniform1i and glUniform1iv are the only two functions that can be used to load uniform variables defined as sampler types (see Section 7.9). Attempting to load a sampler with any other function results in an error.

Errors can also be generated by glUniform for any of the following reasons:

  • If there is no current program object

  • If location is an invalid uniform variable location for the current program object

  • If the number of values specified by count would exceed the declared extent of the indicated uniform variable or uniform variable array

  • Other than the preceding exceptions noted, if the type and size of the uniform variable as defined in the shader do not match the type and size specified in the name of the command used to load its value

In all of these cases, the indicated uniform variable will not be modified.

When the location of a user-defined uniform variable has been determined, the following command can be used to query its current value:

void glGetUniformfv(GLuint program,                                      GLint location,                                      GLfloat *params)void glGetUniformiv(GLuint program,                                      GLint location,                                      GLint *params)

Return in params the value(s) of the specified uniform variable. The type of the uniform variable specified by location determines the number of values returned. If the uniform variable is defined in the shader as a bool, int, or float, a single value is returned. If it is defined as a vec2, ivec2, or bvec2, two values are returned. If it is defined as a vec3, ivec3, or bvec3, three values are returned, and so on. To query values stored in uniform variables declared as arrays, call glGetUniform for each element of the array. To query values stored in uniform variables declared as structures, call glGetUniform for each field in the structure. The values for uniform variables declared as a matrix are returned in column major order.

The locations assigned to uniform variables are not known until the program object is linked. After linking has occurred, the command glGetUniformLocation can obtain the location of a uniform variable. This location value can then be passed to glGetUniform to query the current value of the uniform variable. After a program object has been linked successfully, the index values for uniform variables remain fixed until the next link command occurs. The uniform variable values can only be queried after a link if the link was successful.

The location of a uniform variable cannot be used for anything other than specifying or querying that particular uniform variable. Say you declare a uniform variable as a structure that has three fields in succession that are defined as floats. If you call glGetUniformLocation to determine that the first of those three floats is at location n, do not assume that the next one is at location n + 1. It is possible to query the location of the ith element in an array. That value can then be passed to glUniform to load one or more values into the array, starting at the ith element of the array. It is not possible to take i and add an integer N and use the result to try to modify element i + N in the array. The location of array element i + N should be queried specifically before any attempt to set its value. These location values do not necessarily represent real memory locations. Applications that assume otherwise will not work.

For example, consider the following structure defined within a shader:

uniform struct
{
    struct
    {
        float a;
        float b[10];
    } c[2];
    vec2 d;
} e;

and consider the API calls that attempt to determine locations within that structure:

loc1 = glGetUniformLocation(progObj, "e.d");         // is valid
loc2 = glGetUniformLocation(progObj, "e.c[0]");      // is not valid
loc3 = glGetUniformLocation(progObj, "e.c[0].b") ;   // is valid
loc4 = glGetUniformLocation(progObj, "e.c[0].b[2]"); // is valid

The location loc2 cannot be retrieved because e.c[0] references a structure.

Now consider the commands to set parts of the uniform variable:

glUniform2f(loc1, 1.0f, 2.0f);     // is valid
glUniform2i(loc1, 1, 2);           // is not valid
glUniform1f(loc1, 1.0f);           // is not valid
glUniform1fv(loc3, 10, floatPtr);  // is valid
glUniform1fv(loc4, 10, floatPtr);  // is not valid
glUniform1fv(loc4, 8, floatPtr);   // is valid

The second command in the preceding list is invalid because loc1 references a uniform variable of type vec2, not ivec2. The third command is invalid because loc1 references a vec2, not a float. The fifth command in the preceding list is invalid because it attempts to set values that will exceed the length of the array.

Uniform variables (either built in or user defined) that can be accessed when a shader is executed are called ACTIVE UNIFORMS. You can think of this as though the process of compiling and linking is capable of deactivating uniform variables that are declared but never used. This provides more flexibility in coding style—modular code can define lots of uniform variables, and those that can be determined to be unused are typically optimized away.

To obtain the list of active uniform variables from a program object, use glGetActiveUniform. This command can be used by an application to query the uniform variables in a program object and set up user interface elements to allow direct manipulation of all the user-defined uniform values.

void glGetActiveUniform(GLuint program,                                              GLuint index,                                              GLsizei bufSize,                                              GLsizei *length,                                              GLint *size,                                              GLenum *type,                                              GLchar *name)

Returns information about an active uniform variable in the program object specified by program. The number of active uniform variables can be obtained by calling glGetProgram with the value GL_ACTIVE_UNIFORMS. A value of 0 for index selects the first active uniform variable. Permissible values for index range from 0 to the number of active uniform variables minus 1.

Shaders may use built-in uniform variables, user-defined uniform variables, or both. Built-in uniform variables have a prefix of “gl_” and reference existing OpenGL state or values derived from such state (e.g., gl_Fog, gl_ModelViewMatrix, etc., see Section 4.3 for a complete list.) User-defined uniform variables have arbitrary names and obtain their values from the application through calls to glUniform. A uniform variable (either built in or user defined) is considered active if it is determined during the link operation that it can be accessed during program execution. Therefore, program should have previously been the target of a call to glLinkProgram, but it is not necessary for it to have been linked successfully.

The size of the character buffer required to store the longest uniform variable name in program can be obtained by calling glGetProgram with the value GL_ACTIVE_UNIFORM_MAX_LENGTH. This value should be used to allocate a buffer of sufficient size to store the returned uniform variable name. The size of this character buffer is passed in bufSize, and a pointer to this character buffer is passed in name.

glGetActiveUniform returns the name of the uniform variable indicated by index, storing it in the character buffer specified by name. The string returned is null terminated. The actual number of characters written into this buffer is returned in length, and this count does not include the null termination character. If the length of the returned string is not required, a value of NULL can be passed in the length argument.

The type argument returns a pointer to the uniform variable’s data type. One of the following symbolic constants may be returned: GL_FLOAT, GL_FLOAT_VEC2, GL_FLOAT_VEC3, GL_FLOAT_VEC4, GL_INT, GL_INT_VEC2, GL_INT_VEC3, GL_INT_VEC4, GL_BOOL, GL_BOOL_VEC2, GL_BOOL_VEC3, GL_BOOL_VEC4, GL_FLOAT_MAT2, GL_FLOAT_MAT3, GL_FLOAT_MAT4, GL_SAMPLER_1D, GL_SAMPLER_2D, GL_SAMPLER_3D, GL_SAMPLER_CUBE, GL_SAMPLER_1D_SHADOW, or GL_SAMPLER_2D_SHADOW.

If one or more elements of an array are active, the name of the array is returned in name, the type is returned in type, and the size parameter returns the highest array element index used, plus one, as determined by the compiler and linker. Only one active uniform variable will be reported for a uniform array.

Uniform variables that are declared as structures or arrays of structures are not returned directly by this function. Instead, each of these uniform variables is reduced to its fundamental components containing the “.” and “[]” operators such that each of the names is valid as an argument to glGetUniformLocation. Each of these reduced uniform variables is counted as one active uniform variable and is assigned an index. A valid name cannot be a structure, an array of structures, or a subcomponent of a vector or matrix.

The size of the uniform variable is returned in size. Uniform variables other than arrays have a size of 1. Structures and arrays of structures are reduced as described earlier, such that each of the names returned will be a data type in the earlier list. If this reduction results in an array, the size returned is as described for uniform arrays; otherwise, the size returned is 1.

The list of active uniform variables may include both built-in uniform variables (which begin with the prefix “gl_”) as well as user-defined uniform variable names.

This function returns as much information as it can about the specified active uniform variable. If no information is available, length is 0, and name is an empty string. This situation could occur if this function is called after a link operation that failed. If an error occurs, the return values length, size, type, and name are unmodified.

Using glGetActiveUniform, the application developer can programmatically query the uniform variables actually used in a shader and automatically create a user interface that allows the end user to modify those uniform variables. If among the shader writers there were some convention concerning the names of uniform variables, the user interface could be even more specific. For instance, any uniform variable name that ended with “Color” would be edited with the color selection tool. This function can also be useful when mixing and matching a set of vertex and fragment shaders designed to play well with each other, using a subset of known uniform variables. It can be much safer and less tedious to programmatically determine which uniform variables to send down than to hardcode all the combinations.

Samplers

glUniform1i and glUniform1iv load uniform variables defined as sampler types (i.e., uniform variables of type sampler1D, sampler2D, sample3D, samplerCube, sampler1DShadow, or sampler2DShadow). They may be declared within either vertex shaders or fragment shaders.

The value contained in a sampler is used within a shader to access a particular texture map. The value loaded into the sampler by the application should be the number of the texture unit to be used to access the texture. For vertex shaders, this value should be less than the implementationdependent constant GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, which can be queried with glGet. For fragment shaders, this value should be less than the implementation-dependent constant GL_MAX_TEXTURE_IMAGE_UNITS.

The suffix on the sampler type indicates the texture type to be accessed: 1D, 2D, 3D, cube map, 1D shadow, or 2D shadow. In OpenGL, a texture object of each of the first four texture types can be bound to a single texture unit, and this suffix allows the desired texture object to be chosen. A 1D shadow sampler is used to access the 1D texture when depth comparisons are enabled, and a 2D shadow sampler is used to access the 2D texture when depth comparisons are enabled. If two uniform variables of different sampler types contain the same value, an error is generated when the next rendering command is issued. Attempting to load a sampler with any command other than glUniform1i or glUniform1iv results in an error being generated.

From within a shader, samplers should be considered an opaque data type. The current API provides a way of specifying an integer representing the texture image unit to be used. In the future, the API may be extended to allow a sampler to refer directly to a texture object.

Samplers that can be accessed when a program is executed are called ACTIVE SAMPLERS. The link operation fails if it determines that the number of active samplers exceeds the maximum allowable limits. The number of active samplers permitted on the vertex processor is specified by GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, the number of active samplers permitted on the fragment processor is specified by GL_MAX_TEXTURE_IMAGE_UNITS, and the number of active samplers permitted on both processors combined is GL_COMBINED_TEXTURE_IMAGE_UNITS.

More detail on using samplers within a shader is provided in Section 10.1.

Multiple Render Targets

Another feature added to OpenGL in version 2.0 was the ability to render into multiple buffers simultaneously. The OpenGL Shading Language makes provisions for this capability by including a fragment shader output variable defined as an array called gl_FragData. The size of this array is implementation dependent, but must be at least 1. The elements of this array are defined to be of type vec4.

With this capability, applications can develop fragment shaders that compute multiple values for each fragment and store them in offscreen memory. These values can be accessed during a future rendering pass. Among other things, this lets applications implement complex multipass algorithms and use the graphics hardware for general-purpose computation.

To set up OpenGL for rendering into multiple target buffers, use

void glDrawBuffers(GLsizei n,                                     const GLenum *bufs)

Defines an array of buffers into which fragment color values or fragment data will be written. If no fragment shader is active, rendering operations generate only one fragment color per fragment and it is written into each of the buffers specified by bufs. If a fragment shader is active and it writes a value to the output variable gl_FragColor, then that value is written into each of the buffers specified by bufs. If a fragment shader is active and it writes a value to one or more elements of the output array variable gl_FragData[], then the value of gl_FragData[0] is written into the first buffer specified by bufs, the value of gl_FragData[1] is written into the second buffer specified by bufs, and so on up to gl_FragData[n-1]. The draw buffer used for gl_FragData[n] and beyond is implicitly set to be GL_NONE.

The symbolic constants contained in bufs are defined in Table 7.4. Except for GL_NONE, none of the symbolic constants may appear more than once in bufs. The maximum number of draw buffers supported is implementation dependent and can be queried by calling glGet with the argument GL_MAX_DRAW_BUFFERS. The number of auxiliary buffers can be queried by calling glGet with the argument GL_AUX_BUFFERS.

Table 7.4. Buffer names for use with the glDrawBuffers call

Parameter

Operation

GL_NONE

The fragment color/data value is not written into any color buffer.

GL_FRONT_LEFT

The fragment color/data value is written into the front-left color buffer.

GL_FRONT_RIGHT

The fragment color/data value is written into the front-right color buffer.

GL_BACK_LEFT

The fragment color/data value is written into the back-left color buffer.

GL_BACK_RIGHT

The fragment color/data value is written into the back-right color buffer.

GL_AUXi

The fragment color/data value is written into auxiliary buffer i.

An error is generated if glDrawBuffers specifies a buffer that does not exist in the current GL context. If more than one buffer is selected for drawing, blending and logical operations are computed and applied independently for each element of gl_FragData and its corresponding buffer. Furthermore, the alpha value (i.e., the fourth component) of gl_FragData[0] is used to determine the result of the alpha test. Operations such as scissor, depth, and stencil tests (if enabled) may cause the entire fragment (including all of the values in the gl_FragData array) to be discarded without any updates to the framebuffer.

Development Aids

A situation that can be difficult to diagnose is one in which a program may fail to execute because of the value of a sampler variable. These variables can be changed anytime between linking and program execution. To ensure robust behavior, OpenGL implementations must do some runtime checking just before the shader is executed (i.e., when a rendering operation is about to occur). At this point, the only way to report an error is to set the OpenGL error flag, and this is not usually something that applications check at this performance-critical juncture.

To provide more information when these situations occur, the OpenGL Shading Language API defines a new function that can be called to perform this runtime check explicitly and provide diagnostic information.

void glValidateProgram(GLuint program)

Checks whether the executables contained in program can execute given the current OpenGL state. The information generated by the validation process is stored in program’s information log. The validation information may consist of an empty string, or it may be a string containing information about how the current program object interacts with the rest of current OpenGL state. This function provides a way for OpenGL implementors to convey more information about why the current program is inefficient, suboptimal, failing to execute, and so on.

The status of the validation operation is stored as part of the program object’s state. This value is set to GL_TRUE if the validation succeeded and GL_FALSE otherwise. It can be queried by calling glGetProgram with arguments program and GL_VALIDATE_STATUS. If validation is successful, program is guaranteed to execute given the current state. Otherwise, program is guaranteed to not execute.

This function is typically useful only during application development. The informational string stored in the information log is completely implementation-dependent. Therefore, an application should not expect different OpenGL implementations to produce identical information strings.

Because the operations described in this section can severely hinder performance, they should be used only during application development and removed before shipment of the production version of the application.

Implementation-Dependent API Values

Some of the features we’ve described in previous sections have implementation-dependent limits. All of the implementation-dependent values in the OpenGL Shading Language API are defined in the list that follows, and all of them can be queried with glGet.

  • GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS—Defines the total number of hardware units that can be used to access texture maps from the vertex processor and the fragment processor combined. The minimum legal value is 2.

  • GL_MAX_DRAW_BUFFERS—Defines the maximum number of buffers that can be simultaneously written into from within a fragment shader using the special output variable gl_FragData. This constant effectively defines the size of the gl_FragData array. The minimum legal value is 1.

  • GL_MAX_FRAGMENT_UNIFORM_COMPONENTS—Defines the number of components (i.e., floating-point values) that are available for fragment shader uniform variables. The minimum legal value is 64.

  • GL_MAX_TEXTURE_COORDS—Defines the number of texture coordinate sets that are available. The minimum legal value is 2.

  • GL_MAX_TEXTURE_IMAGE_UNITS—Defines the total number of hardware units that can be used to access texture maps from the fragment processor. The minimum legal value is 2.

  • GL_MAX_VARYING_FLOATS—Defines the number of floating-point variables available for varying variables. The minimum legal value is 32.

  • GL_MAX_VERTEX_ATTRIBS—Defines the number of active vertex attributes that are available. The minimum legal value is 16.

  • GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS—Defines the number of hardware units that can be used to access texture maps from the vertex processor. The minimum legal value is 0.

  • GL_MAX_VERTEX_UNIFORM_COMPONENTS—Defines the number of components (i.e., floating-point values) that are available for vertex shader uniform variables. The minimum legal value is 512.

Application Code for Brick Shaders

Each shader is going to be a little bit different. Each vertex shader may use a different set of attribute variables or different uniform variables, attribute variables may be bound to different generic vertex attribute index values, and so on. One of the demo programs whose source code is available for download from the 3Dlabs Web site is called ogl2brick. It is a small, clear example of how to create and use a vertex shader and a fragment shader. The code in ogl2brick was derived from an earlier demo program called ogl2demo, written primarily by Barthold Lichtenbelt with contributions from several others. In ogl2brick an “install” function installs the brick shaders that were presented in Chapter 6. We discuss that shader installation function, but first we define a simple function that make it a little easier to set the values of uniform variables.

GLint getUniLoc(GLuint program, const GLchar *name)
{
    GLint loc;
    
    loc = glGetUniformLocation(program, name);

    if (loc == -1)
        printf("No such uniform named "%s"
", name);

    printOpenGLError();  // Check for OpenGL errors
    return loc;
}

Shaders are passed to OpenGL as strings. For our shader installation function, we assume that each of the shaders has been defined as a single string, and pointers to those strings are passed to the following function. This function does all the work to load, compile, link, and install our brick shaders. The function definition and local variables for this function are declared as follows:

int installBrickShaders(const GLchar *brickVertex,
                        const GLchar *brickFragment)
{
    GLuint brickVS, brickFS, brickProg;   // handles to objects
    GLint  vertCompiled, fragCompiled;    // status values
    GLint  linked;

The argument brickVertex contains a pointer to the string containing the source code for the brick vertex shader, and the argument brickFragment contains a pointer to the source code for the brick fragment shader. Next, we declare variables to refer to three OpenGL objects: a shader object that stores and compiles the brick vertex shader, a second shader object that stores and compiles the brick fragment shader, and a program object to which the shader objects will be attached. Flags to indicate the status of the compile and link operations are defined next.

The first step is to create two empty shader objects, one for the vertex shader and one for the fragment shader:

brickVS = glCreateShader(GL_VERTEX_SHADER);
brickFS = glCreateShader(GL_FRAGMENT_SHADER);

Source code can be loaded into the shader objects after they have been created. The shader objects are empty, and we have a single null terminated string containing the source code for each shader, so we can call glShaderSource as follows:

glShaderSource(brickVS, 1, &brickVertex, NULL);
glShaderSource(brickFS, 1, &brickFragment, NULL);

The shaders are now ready to be compiled. For each shader, we call glCompileShader and then call glGetShader to see what transpired. glCompileShader sets the shader object’s GL_COMPILE_STATUS parameter to GL_TRUE if it succeeded and GL_FALSE otherwise. Regardless of whether the compilation succeeded or failed, we print the information log for the shader. If the compilation was unsuccessful, this log will have information about the compilation errors. If the compilation was successful, this log may still have useful information that would help us improve the shader in some way. You would typically check the info log only during application development or after running a shader for the first time on a new platform. The function exits if the compilation of either shader fails.

glCompileShader(brickVS);
printOpenGLError();  // Check for OpenGL errors
glGetShaderiv(brickVS, GL_COMPILE_STATUS, &vertCompiled);
printShaderInfoLog(brickVS);

glCompileShader(brickFS);
printOpenGLError();  // Check for OpenGL errors
glGetShaderiv(brickFS, GL_COMPILE_STATUS, &fragCompiled);
printShaderInfoLog(brickFS);

if (!vertCompiled || !fragCompiled)
    return 0;

This section of code uses the printShaderInfoLog function that we defined previously.

At this point, the shaders have been compiled successfully, and we’re almost ready to try them out. First, the shader objects need to be attached to a program object so that they can be linked.

brickProg = glCreateProgram();
glAttachShader(brickProg, brickVS);
glAttachShader(brickProg, brickFS);

The program object is linked with glLinkProgram. Again, we look at the information log of the program object regardless of whether the link succeeded or failed. There may be useful information for us if we’ve never tried this shader before.

glLinkProgram(brickProg);
printOpenGLError();  // Check for OpenGL errors
glGetProgramiv(brickProg, GL_LINK_STATUS, &linked);
printProgramInfoLog(brickProg);

if (!linked)
    return 0;

If we make it to the end of this code, we have a valid program that can become part of current state simply by calling glUseProgram:

glUseProgram(brickProg);

Before returning from this function, we also want to initialize the values of the uniform variables used in the two shaders. To obtain the location that was assigned by the linker, we query the uniform variable by name, using the getUniLoc function defined previously. Then we use that location to immediately set the initial value of the uniform variable.

glUniform3f(getUniLoc(brickProg, "BrickColor"), 1.0, 0.3, 0.2);
glUniform3f(getUniLoc(brickProg, "MortarColor"), 0.85, 0.86, 0.84);
glUniform2f(getUniLoc(brickProg, "BrickSize"), 0.30, 0.15);
glUniform2f(getUniLoc(brickProg, "BrickPct"), 0.90, 0.85);
glUniform3f(getUniLoc(brickProg, "LightPosition"), 0.0, 0.0, 4.0);

return 1;
}

When this function returns, the application is ready to draw geometry that will be rendered with our brick shaders. The result of rendering some simple objects with this application code and the shaders described in Chapter 6 is shown in Figure 6.6. The complete C function is shown in Listing 7.3.

Example 7.3. C function for installing brick shaders

int installBrickShaders(const GLchar *brickVertex,
                        const GLchar *brickFragment)
{
    GLuint brickVS, brickFS, brickProg;   // handles to objects
    GLint  vertCompiled, fragCompiled;    // status values
    GLint  linked;

// Create a vertex shader object and a fragment shader object

brickVS = glCreateShader(GL_VERTEX_SHADER);
brickFS = glCreateShader(GL_FRAGMENT_SHADER);

// Load source code strings into shaders

glShaderSource(brickVS, 1, &brickVertex, NULL);
glShaderSource(brickFS, 1, &brickFragment, NULL);

// Compile the brick vertex shader and print out
// the compiler log file.

glCompileShader(brickVS);
printOpenGLError();  // Check for OpenGL errors
glGetShaderiv(brickVS, GL_COMPILE_STATUS, &vertCompiled);
printShaderInfoLog(brickVS);

// Compile the brick fragment shader and print out
// the compiler log file.

glCompileShader(brickFS);
printOpenGLError();  // Check for OpenGL errors
glGetShaderiv(brickFS, GL_COMPILE_STATUS, &fragCompiled);
printShaderInfoLog(brickFS);

if (!vertCompiled || !fragCompiled)
    return 0;

// Create a program object and attach the two compiled shaders

brickProg = glCreateProgram();
glAttachShader(brickProg, brickVS);
glAttachShader(brickProg, brickFS);

// Link the program object and print out the info log

glLinkProgram(brickProg);
printOpenGLError();  // Check for OpenGL errors
glGetProgramiv(brickProg, GL_LINK_STATUS, &linked);
printProgramInfoLog(brickProg);

if (!linked)
    return 0;

// Install program object as part of current state

glUseProgram(brickProg);

// Set up initial uniform values

   glUniform3f(getUniLoc(brickProg, "BrickColor"), 1.0, 0.3, 0.2);
   glUniform3f(getUniLoc(brickProg, "MortarColor"), 0.85, 0.86, 0.84);
   glUniform2f(getUniLoc(brickProg, "BrickSize"), 0.30, 0.15);
   glUniform2f(getUniLoc(brickProg, "BrickPct"), 0.90, 0.85);
   glUniform3f(getUniLoc(brickProg, "LightPosition"), 0.0, 0.0, 4.0);

   return 1;
}

Summary

The set of function calls added to OpenGL to create and manipulate shaders is actually quite small. The interface mimics the software development process followed by a C/C++ programmer. To install and use OpenGL shaders, do the following:

  1. Create one or more (empty) shader objects with glCreateShader.

  2. Provide source code for these shaders with glShaderSource.

  3. Compile each of the shaders with glCompileShader.

  4. Create a program object with glCreateProgram.

  5. Attach all the shader objects to the program object with glAttachShader.

  6. Link the program object with glLinkProgram.

  7. Install the executable program as part of OpenGL’s current state with glUseProgram.

  8. If the shaders use user-defined uniform variables, query the locations of these variables with glGetUniformLocation and then set their values with glUniform.

User-defined attribute variables can be explicitly associated with a generic vertex attribute index with glBindAttribLocation, or such associations can be assigned implicitly at link time and queried with glGetAttribLocation. Generic vertex attribute values can then be supplied by the application a vertex at a time with one of the variants of glVertexAttrib or as vertex arrays by using glVertexAttribPointer and glEnableVertexArrayPointer in conjunction with standard OpenGL commands to draw vertex arrays.

A number of query functions obtain information about shader and program objects.

Further Information

Reference pages for the OpenGL Shading Language API can be found in Appendix B. Source code for the example in this chapter can be found at the 3Dlabs developers Web site. More complex source code examples and a variety of shaders can be found there as well.

  1. 3Dlabs developer Web site. http://developer.3dlabs.com.

  2. Ebert, David S., John Hart, Bill Mark, F. Kenton Musgrave, Darwyn Peachey, Ken Perlin, and Steven Worley, Texturing and Modeling: A Procedural Approach, Third Edition, Morgan Kaufmann Publishers, San Francisco, 2002. http://www.texturingandmodeling.com

  3. Kessenich, John, Dave Baldwin, and Randi Rost, The OpenGL Shading Language, Version 1.10, 3Dlabs, April 2004. http://www.opengl.org/documentation/spec.html

  4. OpenGL Architecture Review Board, Dave Shreiner, J. Neider, T. Davis, and M. Woo, OpenGL Programming Guide, Fifth Edition: The Official Guide to Learning OpenGL, Version 2, Addison-Wesley, Reading, Massachusetts, 2005.

  5. OpenGL Architecture Review Board, OpenGL Reference Manual, Fourth Edition: The Official Reference to OpenGL, Version 1.4, Editor: Dave Shreiner, Addison-Wesley, Reading, Massachusetts, 2004.

  6. Segal, Mark, and Kurt Akeley, The OpenGL Graphics System: A Specification (Version 2.0), Editor (v1.1): Chris Frazier, (v1.2–1.5): Jon Leech, (v2.0): Jon Leech and Pat Brown, Sept. 2004. http://www.opengl.org/documentation/spec.html

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
44.210.151.5