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 Shading Language Cookbook

You're reading from   OpenGL 4 Shading Language Cookbook Build high-quality, real-time 3D graphics with OpenGL 4.6, GLSL 4.6 and C++17

Arrow left icon
Product type Paperback
Published in Sep 2018
Publisher Packt
ISBN-13 9781789342253
Length 472 pages
Edition 3rd Edition
Languages
Tools
Arrow right icon
Authors (2):
Arrow left icon
David A Wolff David A Wolff
Author Profile Icon David A Wolff
David A Wolff
David Wolff David Wolff
Author Profile Icon David Wolff
David Wolff
Arrow right icon
View More author details
Toc

Table of Contents (13) Chapters Close

Preface 1. Getting Started with GLSL FREE CHAPTER 2. Working with GLSL Programs 3. The Basics of GLSL Shaders 4. Lighting and Shading 5. Using Textures 6. Image Processing and Screen Space Techniques 7. Using Geometry and Tessellation Shaders 8. Shadows 9. Using Noise in Shaders 10. Particle Systems and Animation 11. Using Compute Shaders 12. Other Books You May Enjoy

Linking a shader program

Once we have compiled our shaders and before we can actually install them into the OpenGL pipeline, we need to link them together into a shader program. Among other things, the linking step involves making the connections between input variables from one shader to output variables of another, and making the connections between the input/output variables of a shader to appropriate locations in the OpenGL environment.

Linking involves steps that are similar to those involved in compiling a shader. We attach each shader object to a new shader program object and then tell the shader program object to link (making sure that the shader objects are compiled before linking):

Getting ready

For this recipe, we'll assume that you've already compiled two shader objects whose handles are stored in the vertShader and fragShader variables.

For this and a few other recipes in this chapter, we'll use the following source code for the fragment shader:

#version 460 
 
in vec3 Color; 
out vec4 FragColor; 
 
void main() { 
  FragColor = vec4(Color, 1.0); 
} 

For the vertex shader, we'll use the source code from the previous recipe, Compiling a shader.

How to do it...

In our OpenGL initialization function, and after the compilation of shader objects referred to by vertShader and fragShader, perform the following steps:

  1. Create the program object using the following code:
GLuint programHandle = glCreateProgram(); 
if( 0 == programHandle ) 
{ 
  std::cerr << "Error creating program object." << std::endl; 
  exit(EXIT_FAILURE); 
} 
  1. Attach the shaders to the program object as follows:
glAttachShader( programHandle, vertShader ); 
glAttachShader( programHandle, fragShader ); 
  1. Link the program:
glLinkProgram( programHandle );

  1. Verify the link status:
GLint status; 
glGetProgramiv( programHandle, GL_LINK_STATUS, &status ); 
if( GL_FALSE == status ) {
  std::cerr << "Failed to link shader program!" << std::endl;
  GLint logLen; 
  glGetProgramiv(programHandle, GL_INFO_LOG_LENGTH, &logLen); 
  if( logLen > 0 ) { 
    std::string(logLen, ' ');
    GLsizei written;
    glGetProgramInfoLog(programHandle, logLen, &written, &log[0]); 
    std::cerr << "Program log: " << std::endl << log;
  } 
} 
  1. If linking is successful, we can install the program into the OpenGL pipeline with glUseProgram:
else
  glUseProgram( programHandle );

Regardless of whether the link was successful, it is a good idea to clean up our shader objects. Once the program is linked, they are not needed anymore:

// Detach and delete shader objects
glDetachShader(programHandle, vertShader);
glDetachShader(programHandle, fragShader);
glDeleteShader(vertShader);
glDeleteShader(fragShader);

How it works...

We start by calling glCreateProgram to create an empty program object. This function returns a handle to the program object, which we store in a variable named programHandle. If an error occurs with program creation, the function will return 0. We check for that, and if it occurs, we print an error message and exit.

Next, we attach each shader to the program object using glAttachShader. The first argument is the handle to the program object, and the second is the handle to the shader object to be attached.

Then, we link the program by calling glLinkProgram, providing the handle to the program object as the only argument. As with compilation, we check for the success or failure of the link, with the subsequent query.

We check the status of the link by calling glGetProgramiv. Similar to glGetShaderiv, glGetProgramiv allows us to query various attributes of the shader program. In this case, we ask for the status of the link by providing GL_LINK_STATUS as the second argument. The status is returned in the location pointed to by the third argument, in this case named status.

The link status is either GL_TRUE or GL_FALSE, indicating the success or failure of the link. If the value of the status is GL_FALSE, we retrieve and display the program information log, which should contain additional information and error messages. The program log is retrieved by the call to glGetProgramInfoLog. The first argument is the handle to the program object, the second is the size of the buffer to contain the log, the third is a pointer to a GLsizei variable where the number of bytes written to the buffer will be stored (excluding the null terminator), and the fourth is a pointer to the buffer that will store the log. The buffer can be allocated based on the size returned by the call to glGetProgramiv with the GL_INFO_LOG_LENGTH parameter. The string that is provided in log will be properly null terminated.

Finally, if the link is successful, we install the program into the OpenGL pipeline by calling glUseProgram, providing the handle to the program as the argument.

It is a good idea to detach and delete the shader object, regardless of whether the link is successful. However, if the shader objects might be needed to link another program, you should detach it from this program and skip deletion until later.

With the simple fragment shader from this recipe and the vertex shader from the previous recipe compiled, linked, and installed into the OpenGL pipeline, we have a complete OpenGL pipeline and are ready to begin rendering. Drawing a triangle and supplying different values for the Color attribute yields an image of a multi-colored triangle where the vertices are red, green, and blue, and inside the triangle, the three colors are interpolated, causing a blending of colors throughout:

For details on how to render the triangle, see Chapter 2, Working with GLSL Programs.

There's more...

You can use multiple shader programs within a single OpenGL program. They can be swapped in and out of the OpenGL pipeline by calling glUseProgram to select the desired program.

Shader input/output variables

You may have noticed that the Color variable is used to send data from the vertex shader to the fragment shader. There is an output variable (out vec3) in the vertex shader and an input variable (in vec3) in the fragment shader, both with the same name. The value that the fragment shader receives is a value that is interpolated from the values of the corresponding output variable for each of the vertices (hence the blended colors in the earlier image). This interpolation is automatically done by hardware rasterizer before the execution of the fragment stage. 

When linking a shader program, OpenGL makes the connections between input and output variables in the vertex and fragment shaders (among other things). If a vertex shader's output variable has the same name and type as a fragment shader's input variable, OpenGL will automatically link them together.

It is possible to connect (link) variables that do not have the same name or type by using layout qualifiers. With a layout qualifier, we can specify the location for each variable specifically. For example, suppose that I used this set of output variables in my vertex shader:

layout (location=0) out vec4 VertColor;
layout (location=1) out vec3 VertNormal;

I could use these variables in the fragment shader:

layout (location=0) in vec3 Color;
layout (location=1) in vec3 Normal;

Despite the fact that these have different names (and for Color, types), they will be connected by the linker when the program is linked due to the fact that they are assigned the same locations. In this example, VertColor will be linked to Color, and VertNormal will be linked to Normal. This makes things more convenient. We're not required to use the same names for input/output variables, which gives us the flexibility to use names that might be more descriptive in each shader stage. More importantly, it is part of a larger framework, called separate shader objects. A full example of separate shader objects can be found in the Using program pipelines recipe.

In fact, this use of layout qualifiers to specify variable locations is required when compiling to SPIR-V (see the Loading an SPIR-V shader program recipe).

Deleting a shader program

If a program is no longer needed, it can be deleted from OpenGL memory by calling glDeleteProgram, providing the program handle as the only argument. This invalidates the handle and frees the memory used by the program. Note that if the program object is currently in use, it will not be immediately deleted, but will be flagged for deletion when it is no longer in use.

Also, the deletion of a shader program detaches the shader objects that were attached to the program but does not delete them unless those shader objects have already been flagged for deletion by a previous call to glDeleteShader. Therefore, as mentioned before, it is a good idea to detach and delete them immediately, as soon as the program is linked, to avoid accidentally leaking shader objects.

See also

  • The chapter01/scenebasic.cpp file in the example code
  • The Compiling a shader recipe
  • The Using program pipelines recipe
  • The Loading an SPIR-V shader program recipe

You have been reading a chapter from
OpenGL 4 Shading Language Cookbook - Third Edition
Published in: Sep 2018
Publisher: Packt
ISBN-13: 9781789342253
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 €18.99/month. Cancel anytime