Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases now! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
OpenGL 4.0 Shading Language Cookbook

You're reading from   OpenGL 4.0 Shading Language Cookbook With over 60 recipes, this Cookbook will teach you both the elementary and finer points of the OpenGL Shading Language, and get you familiar with the specific features of GLSL 4.0. A totally practical, hands-on guide.

Arrow left icon
Product type Paperback
Published in Jul 2011
Publisher Packt
ISBN-13 9781849514767
Length 340 pages
Edition 1st Edition
Tools
Arrow right icon
Toc

Table of Contents (16) Chapters Close

OpenGL 4.0 Shading Language Cookbook
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
1. Getting Started with GLSL 4.0 FREE CHAPTER 2. The Basics of GLSL Shaders 3. Lighting, Shading Effects, and Optimizations 4. Using Textures 5. Image Processing and Screen Space Techniques 6. Using Geometry and Tessellation Shaders 7. Shadows 8. Using Noise in Shaders 9. Animation and Particles Index

Compiling a shader


The GLSL compiler is built into the OpenGL library, and shaders can only be compiled within the context of a running OpenGL program. There is currently no external tool for pre-compiling GLSL shaders and/or shader programs.

Note

Recently, OpenGL 4.1 added the ability to save compiled shader programs to a file, enabling OpenGL programs to avoid the overhead of shader compilation by loading pre-compiled shader programs.

Compiling a shader involves creating a shader object, providing the source code (as a string or set of strings) to the shader object, and asking the shader object to compile the code. The process is represented by the following diagram.

Getting ready

To compile a shader, we'll need a basic example to work with. Let's start with the following simple vertex shader. Save it in a file named basic.vert.

#version 400

in vec3 VertexPosition;
in vec3 VertexColor;

out vec3 Color;

void main()
{
   Color = VertexColor;
   gl_Position = vec4( VertexPosition, 1.0 );
}

In case you're curious about what this code does, it works as a "pass-through" shader. It takes the input attributes VertexPosition and VertexColor and passes them along to the fragment shader via the output variables gl_Position and Color.

Next, we'll need to build a basic shell for an OpenGL program using any standard windowing toolkit. Examples of cross-platform toolkits include GLUT, FLTK, Qt, or wxWidgets. Throughout this text, I'll make the assumption that you can create a basic OpenGL program with your favorite toolkit. Virtually all toolkits have a hook for an initialization function, a resize callback (called upon resizing of the window), and a drawing callback (called for each window refresh). For the purposes of this recipe, we need a program that creates and initializes an OpenGL context; it need not do anything other than display an empty OpenGL window.

Finally, we need to load the shader source code into a character array named shaderCode. Don't forget to add the null character at the end! This example assumes that the variable shaderCode points to an array of GLchar that is properly terminated by a null character.

How to do it...

To compile a shader, use the following steps:

  1. Create the shader object as follows.

    GLuint vertShader = glCreateShader( GL_VERTEX_SHADER );
    if( 0 == vertShader )
    {
      fprintf(stderr, "Error creating vertex shader.\n");
      exit(1);
    }
  2. Copy the source code (perhaps from multiple locations) into the shader object.

    const GLchar * shaderCode = loadShaderAsString("basic.vert");
    const GLchar* codeArray[] = {shaderCode};
    glShaderSource( vertShader, 1, codeArray, NULL );
  3. Compile the shader.

    glCompileShader( vertShader );
  4. Verify the compilation status.

    GLint result;
    glGetShaderiv( vertShader, GL_COMPILE_STATUS, &result );
    if( GL_FALSE == result ) 
    {
    
       fprintf( stderr, "Vertex shader compilation failed!\n" );
    
       GLint logLen;
       glGetShaderiv( vertShader, GL_INFO_LOG_LENGTH, &logLen );
    
      if( logLen > 0 )
       {
           char * log = (char *)malloc(logLen);
    
          GLsizei written;
          glGetShaderInfoLog(vertShader, logLen, &written, log);
    
          fprintf(stderr, "Shader log:\n%s", log);
          free(log);
       }
    }

How it works...

The first step is to create the shader object using the function glCreateShader. The argument is the type of shader, and can be one of the following: GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, GL_TESS_EVALUATION_SHADER, or GL_TESS_CONTROL_SHADER. In this case, since we are compiling a vertex shader, we use GL_VERTEX_SHADER. This function returns the value used for referencing the vertex shader object, sometimes called the object "handle". We store that value in the variable vertShader. If an error occurs while creating the shader object, this function will return 0, so we check for that and if it occurs, we print an appropriate message and terminate.

Following the creation of the shader object, we load the source code into the shader object using the function glShaderSource. This function is designed to accept an array of strings in order to support the option of compiling multiple files at once. So before we call glShaderSource, we place a pointer to our source code into an array named sourceArray. The first argument to glShaderSource is the handle to the shader object. The second is the number of source code strings that are contained in the array. The third argument is a pointer to an array of source code strings. The final argument is an array of GLint values that contains the length of each source code string in the previous argument. In this case, we pass a value of NULL, which indicates that each source code string is terminated by a null character. If our source code strings were not null terminated then this argument must be a valid array. Note that once this function returns, the source code has been copied into OpenGL internal memory, so the memory used to store the source code can be freed.

The next step is to compile the source code for the shader. We do this by simply calling glCompileShader, and passing the handle to the shader that is to be compiled. Of course, depending on the correctness of the source code, the compilation may fail, so the next step is to check whether or not the compilation was successful.

We can query for the compilation status by calling glGetShaderiv, which is a function for querying the attributes of a shader object. In this case we are interested in the compilation status, so we use GL_COMPILE_STATUS as the second argument. The first argument is of course the handle to the shader object, and the third argument is a pointer to an integer where the status will be stored. The function provides a value of either GL_TRUE or GL_FALSE in the third argument, indicating whether or not the compilation was successful.

If the compile status is GL_FALSE, then we can query for the shader log, which will provide additional details about the failure. We do so by first querying for the length of the log by calling glGetShaderiv again with a value of GL_INFO_LOG_LENGTH. This provides the length of the log in the variable logLen, including the null termination character. We then allocate space for the log, and retrieve the log by calling glGetShaderInfoLog . The first parameter is the handle to the shader object, the second is the size of the character buffer for storing the log, the third argument is a pointer to an integer where the number of characters actually written (excluding the null terminator character) will be stored, and the fourth argument is a pointer to the character buffer for storing the log itself. Once the log is retrieved, we print it to stderr and free its memory space.

There's more...

The technique for compiling a shader is nearly identical for each shader type. The only significant difference is the argument to glCreateShader.

Of course, shader compilation is only the first step. To create a working shader program, we often have at least two shaders to compile, and then the shaders must be linked together into a shader program object. We'll see the steps involved in linking in the next recipe.

Deleting a shader object

Shader objects can be deleted when no longer needed by calling glDeleteShader . This frees the memory used by the shader and invalidates its handle. Note that if a shader object is already attached to a program object (see Linking a shader program), it will not be immediately deleted, but flagged for deletion when it is detached from the program object.

See also

The next recipe, Linking a shader program.

You have been reading a chapter from
OpenGL 4.0 Shading Language Cookbook
Published in: Jul 2011
Publisher: Packt
ISBN-13: 9781849514767
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime