Graphics – MGP – Intro to Shaders

10 06 2010

Intro
Before we can go any further into the project we need to introduce the concept of shaders and the modification of the Fixed Functionality Pipeline.

In OpenGL the normal pipeline looks like that in the image below. Vertices, normals and colours go in one end, are transformed into their final location (depending on camera, matrix transformations etc) and then the colour of each pixel is determined before being put on the screen.

OpenGL Pipeline Simplified

In our use of shaders we are changing two stages of this pipeline by replacing the fixed functionality with little programs of our own. The two areas that we can replace are marked in green in the diagram.

Replacing the FFP
We are going to use GLSL shaders which means they are written in the GLSL language and supplied to the graphics driver as code at runtime. The graphics driver then compiles them into a shader and returns a reference for us to use.

To create the reference we need to follow the steps below:

  1. Create handles for our shaders
  2. Send the source code for each to the graphics driver
  3. Compile the shaders
  4. Check for compilation problems
  5. Tell our shader about the Fragment and Vertex shaders
  6. Link shader
  7. Validate shader
  8. Check for linker errors

The code to do this is as follows:

// references we will need
GLuint shaderHandle, vsHandle, fsHandle;
GLint result;
const GLChar* vsSource = "…";
const GLChar* fsSource = "…";

// compile the vertex shader part
vsHandle = glCreateShader (GL_VERTEX_SHADER_ARB); // Step 1
glShaderSource (vsHandle, 1, &vsSource, NULL); // Step 2
glCompileShader (vsHandle); // Step 3
glGetShaderiv (vsHandle, GL_COMPILE_STATUS, &result); // Step 4
if( result == GL_FALSE )
    ; // ohoh - need to handle the fail case (print what is wrong etc)

// compile the fragment shader part
fsHandle = glCreateShader (GL_FRAGMENT_SHADER_ARB); // Step 1
glShaderSource (fsHandle, 1, &fsSource, NULL); // Step 2
glCompileShader (fsHandle); // Step 3
glGetShaderiv (fsHandle, GL_COMPILE_STATUS, &result); // Step 4
if( result == GL_FALSE )
    ; // ohoh - need to handle the fail case (print what is wrong etc)

// link them together
shaderHandle = glCreateProgram (); // Step 1
glAttachShader (shaderHandle, vsHandle); // Step 5
glAttachShader (shaderHandle, fsHandle); // Step 5
glLinkProgram (shaderHandle); // Step 6
glValidateProgram (shaderHandle);	// Step 7
glGetShaderiv (shaderHandle, GL_LINK_STATUS, &result); // Step 8
if( result == GL_FALSE )
    ; // ohoh - need to handle the fail case (print what is wrong etc)

And that is pretty much it. There is a whole bunch of extra error checking you can do and you can even get and print the compilation errors if you want but the above is the simplest case.

Now that you have a proper shader you need to be able to turn it on and off. How this works is that you can enable a particular shader much like you would lighting and then each draw command submitted until the shader is turned off will use the new functionality.

As such the proper way to use it would be something like:

glUseProgram (shaderHandle); // Turn it on
// Do some drawing
glUseProgram(0); // Turn it off

Writing a (really) simple shader
The above code explains how to create a new shader and how to use to draw but assumes you have some code to submit to the graphics driver. Here we will describe the simplest possible shader and a small variation that lets you use the normal at each point as the colour (for visualization much like the image in the previous graphics post).

If all you want is to replicate the fixed funtionality the following fragment and vertex shaders will do the trick

// Vertex shader
void main ()
{
	gl_Normal = gl_NormalMatrix * normalize(gl_Normal);
	gl_Position = ftransform();
}
// Fragment shader
void main ()
{
	gl_FragColor = gl_FrontColor;
}

This is pretty boring though (and does even less than the normal pipeline) so lets change the fragment shader a bit to use the normal instead of the color.

// Fragment shader
void main ()
{
	gl_FragColor = vec4( gl_Normal.xyz, 1.0);
}

This will use the x, y and z components as the red, green and blue components respectively.

And here is that image again to show where we are at:

Normals

For further reading into what functionality is available when creating these shaders the GLSL spec is the place to go: OpenGL.org

Advertisements

Actions

Information

One response

8 09 2010
Graphics – MGP – Glass « NSH + Graphics

[…] our object look like it is made of glass. Luckily we have already been introduced to shaders in a previous post so all that remains to be done is to describe how we can incorporate our new environment/cube map […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




%d bloggers like this: