Using GLSL Shaders (OpenGL Programming)

Example 15-2 shows a simple vertex shader. It transforms a point’s position by simulating acceleration due to gravity. While not every detail will make sense at the moment, hopefully you’ll be able to understand the basic flow of what’s going on.

Example 15-2 A Sample GLSL Vertex Shader

A Sample GLSL Vertex Shader

OpenGL / GLSL Interface

Writing shaders for use with OpenGL programs is similar to using a compiler-based language like "C." You have a compiler analyze your program, check it for errors, and then translate it into object code. Next, a collection of object files are combined together in a linking phase into an executable program. Using GLSL shaders in your program is a similar process, except that the compiler and linker are part of the OpenGL driver.

Figure 15-6 illustrates the steps required to create GLSL shader objects and link them together to create an executable shader program.

Shader Creation Flowchart


Figure 15-6 Shader Creation Flowchart

When you want to use a vertex or fragment shader in your application, you’ll need to do the following sequence of steps:

For each shader object:

1.    Create a shader object.

2.    Compile your shader source into the object.

3.    Verify that your shader successfully compiled.

Then, to link multiple shader objects into a shader program, you’ll:

1.    Create a shader program.

2.    Attach the appropriate shader objects to the shader program.

3.    Link the shader program.

4.    Verify that the shader link phase completed successfully.

5.    Use the shader for vertex or fragment processing.

Why create multiple shader objects? Just as you might reuse a function in different programs, the same idea applies to GLSL programs. Common routines that you create might be usable in multiple shaders. Instead of having to compile several large shaders with lots of common code, you’ll merely link the appropriate shader objects together into a shader program.

To create a shader object, call glCreateShader().

GLuint glCreateShader(GLenum type);

Allocates a shader object, type must be either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER. The return value is eitner a non-zero integer or zero, if an error occurred.

Once you have created a shader object, you need associate the source code of the shader with that object created by glCreateShader(). This is done by calling glShaderSourceQ.

void glShaderSource(GLuint shader, GLsizei count, const GLchar **string, const GLint * lengthy.

Associates the source of a shader with a shader object shader, string is an array of count GLchar strings that compose the shader’s source. The character strings in string may be optionally null-terminated, length can be one of three values. If length is NULL, then it’s assumed that each string provided in string is null-terminated. Otherwise, length has count elements, each of which specifies the length of the corresponding entry in string. If the value of an element in the array length is a positive integer, the value represents the number of characters in the corresponding string element. If the value is negative for particular elements, then that entry in string is assumed to be null-terminated.

To compile a shader object’s source, use glCompileShaderQ.

void glCompileShader.GLuint shader);

Compiles the source code for shader. The results of the compilation can be queried by calling glGetShaderiv() with an argument of GL_COMPILE_ STATUS.

Similar to when you compile a "C" program, you need to determine if the compilation finished successfully. A call to glGetShaderiv(), with an argument of GL_COMPILE_STATUS, will return the status of the compilation phase. If GL_TRUE is returned, the compilation succeeded, and the object can be linked into a shader program. If the compilation failed, you can determine what the error was by retrieving the compilation log. glGetShaderlnfoLogO will return an implementation-specific set of messages describing the compilation errors. The current size of the error log can be queried by calling glGetShaderiv() with an argument of GL_INFO_ LOG_LENGTH.

void glGetSnaderInfoLog(GL uint shader, GLsizei bufSize, GLsizei *length, char *infoLog);

Returns the log associated with the last compilation of a shader object. shader specifies which shader object to query The log is returned as a null-terminated character string of length characters in the buffer infoLog. The maximum return size of the log is specified in bufSize.

If length is NULL, then no string was returned.

Once you have created and compiled all of the necessary shader objects, you will need to link them together to create an executable shader program. This process is similar in nature to creating shader objects. First, you’ll need to create a shader program to which you can attach the shader objects. Using glCreateProgram(), a shader program will be returned for further processing.

GLuint glCreateProgram();

Allocates a shader program.

Once you have your shader program, you’ll need to populate it with the necessary shader objects to create the executable program. This is accomplished by attaching a shader object to the program by calling gl AttachShader ().

void glAttachShader(GLuint piogratn, GLuint shader);

Associates the shader object, shader, with the shader program, program. A shader object can be attached to a shader program at any time, although its functionality will only be available after a successful link of the shader program. A shader object can be attached to multiple shader programs simultaneously.

For parity, if you need to remove a shader object from a program to modify the shader’s operation, detach the shader object by calling glDetachShader() with the appropriate shader object identifier.

void glDetachShader(GLuint program, GLuint shader);

Removes the association of a shader object, shader, from the shader program, program. If shader is detached from program and has been marked for deletion (by calling glDeleteShaderQ), it is deleted at that time.

After all the necessary shader objects have been attached to the shader program, you will need to link the objects for an executable program. This is accomplished by calling glLinkProgramQ.

void glLinkProgram(GLuint program);

Processes all shader objects attached to program to generate a completed shader program. The result of the linking operation can be queried by calling glGetProgramiv() with GL_LINK_STATUS. GL_TRUE is returned for a successful link; GL_FALSE is returned otherwise.

As with shader objects, there’s a chance that the linking phase may fail due to errors in the attached shader objects. You can query the result of the link operation’s success by calling glGetProgramiv() with an argument of GL_ LINK_STATUS. If GL_TRUE was returned, the link was successful, and you’re able to specify the shader program for use in processing vertices or fragments. If the link failed, represented by GL_FALSE being returned, then you can determine the cause of the failure by retrieving the program link information log by calling glGetProgramLog().

void glGetProgramlnfoLogiGLuint program, GLsizei bufSize, GLsizei *length, char *infoLog);

Returns the log associated with the last compilation of a shader program. program specifies which shader program to query. The log is returned as a null-terminated character string of length characters in the buffer infoLog. The maximum return size of the log is specified in buftize.

If length is NULL, then no string was returned.

After a successful program link, you can engage the vertex or fragment program by calling glUseProgram() with the program’s object handle. To revert back to using the fixed-function pipeline, pass zero as the program object.

void glUseProgram(GLuint program);

Uses program for either vertex or fragment processing, depending upon the type of shader created with glCreateShader(). If program is zero, no shader is used for processing and OpenGL reverts to fixed-function operation.

While a program is in use, it can have new shader objects attached, compile attached shader objects, or detach previously attached objects. It may also be relinked. If the link phase is successful, the newly linked shader program replaces the previously active program. If the link fails, the currently bound shader program remains active and is not replaced until either a new program is specified with glUseProgramO or the program is successfully relinked.

Example 15-3 illustrates creating and linking a vertex shader in GLSL. The process is virtually identical for fragment shaders.

Example 15-3 Creating and Liking GLSL shaders

Creating and Liking GLSL shaders

 

Creating and Liking GLSL shaders

To release the resources related to shader objects and programs, you can delete those objects by calling the respective deletion function. If either the shader program or object is currently active when it’s deleted, the object is only marked for deletion. It is deleted when the program is no longer in use, or the shader object is detached from all shader program objects.

void glDeleteShader(GLuint shader);

Deletes shader. If shader is currently linked to one or more active shader programs, the object is tagged for deletion and deleted once the shader program is no longer being used by any shader piogram.

void glDeleteProgram( GLuint program);

Deletes program immediately if not currently in use in any context, or tagged for deletion when the program is no longer in use by any contexts.

To determine if an identifier is currently in use as either a shader program or object, calling glIsProgram() or glIsShader() will return a boolean value indicating if the identifier is in use.

GLboolean glIsProgram(GLuint program);

Returns GL_TRUE if program is the name of a shader program. If program is zero, or non-zero and not the name of a shader object, GL_FALSE is returned.

GLboolean glIsShader(GLuint shader);

Returns GL_TRUE if shader is the name of a shader object. If shader is zero, or non-zero and not the name of a shader object, GL_FALSF is returned.

To aid in shader development, the OpenGL call glValidateProgram() can help verify that a shader will execute given the current OpenGL state. Depending upon the underlying OpenGL implementation, program validation may also return hints about performance characteristics or other useful information specific to that shader’s execution for that OpenGL implementation. You would validate a program in the same manner you would compile it by merely calling glValidateProgram() once all of the necessary shader objects were attached to the shader program. Similarly, you can query the results of the validation step by calling glGetProgramiv() with an argument of GL_VAI IDATL_STATUS.

void gl Validate Program(GLuint program);

Validates program against the current OpenGL state settings. After validation, the value of GL_VALIDATE_STATUS will be set to either GL_ TRUE, indicating that the program will execute in the current OpenGL environment, or GL_FALSE otherwise. The value of GL._VALIDATE_ STATUS status can be queried by calling glGetProgramiv().

Next post:

Previous post: