GLSL shading

The bird's eye view

For each vertex:

OpenGL runs your vertex program

For each primitive (tri,quad,etc):

OpenGL runs your geometry program

For each pixel:

OpenGL runs your fragment program

Here's the nuts and bolts of it

Command stream (OpenGL commands)

Vertex stage

Clipping, culling

Geometry stage


Fragment stage

Fragment merge: blending, stencil test, depth test, scissor test

Fragment output

CPU vs GPU compute model

Hardware implementation

Its very complicated and even more of a black box than CPUs. Nothing to see here, moving along.

CPU vs GPU compute model

Software implementation

I've created a simplified software rendering pipeline in C++ that generates
identical images using software or OpenGL, so you can see exactly what's
going on under the hood... If you're so inclined!

CPU vs GPU compute model

What are the important differences?

Writing GLSL code

Here's the starting point

/* passthrough vertex stage */
void main()
   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

/* passthrough fragment stage */
void main()
   gl_FragColor = gl_Color;

After you bind this, what will you see? Probably nothing exciting. Keep in mind that using your own shader will effectively tell OpenGL to ignore most of the fixed function pipeline!

Writing GLSL code

Now what?

/* preturb vertex positions by sin function?? */
uniform float time;
void main()
   mat4 scale = mat4(1.0);
   scale[1][1] = 0.5 + cos(time * 1.5);
   scale[0][0] = 0.5 + sin(time * 0.5);
   gl_Position = gl_ModelViewProjectionMatrix * scale * gl_Vertex;

/* invert the color?? */
void main()
   vec3 inverted = vec3(1.0, 1.0, 1.0) - gl_Color.rgb;
   gl_FragColor = vec4(inverted, 1.0);

Writing your own shaders affords great power to alter the pipline, but it also means you're responsible for sending the all right data.
For instance the shader pipeline above doesn't support texture mapping!

Writing GLSL code

GLSL datatype qualifiers

GLSL version qualifier

Newer version of GLSL (1.50+) requires "in" and "out" qualifiers as well. Details to come...

Pay attention to the version qualifier, it may prevent or create bugs based upon code syntax

#version 150
#version 150 compatibility

Writing GLSL code

GLSL native datatypes, constant uniforms, and built-in values

You can also "swizzle" vector components like so:

vec4 red = vec4(1.0, 0.2, 0.2, 1.0);
vec3 green = red.yxy;	// = vec3(0.2, 1.0, 0.2)
vec4 blue = green.zzyy;	// = vec4(0.2, 0.2, 1.0, 1.0)
float alpha = blue.w;	// = (float) 1.0

Writing GLSL code

Inputs, Outputs, what?

You supply the inputs to the vertex program (data and variables).

Your vertex program outputs must match your fragment program inputs.

These values are marked by the qualifer "varying" (in OpenGL < 1.4) or "in" and "out" (with GLSL >= 1.4)

Writing GLSL code

Some GLSL standard vertex shader variables


Writing GLSL code

Some GLSL standard vertex shader variables

Vertex Outputs (ALSO Fragment Inputs)

Writing GLSL code

Some GLSL standard fragment shader variables


Fragment program MUST write to gl_FragColor or gl_FragData[x] to compile.

Writing GLSL code

Some GLSL built-in uniform values

Writing GLSL code

Texture coordinates

Texture coordinates are normalized (to: 0-1). So if you want the color of an image at a pixel, you must sample it using: pixel_location / image_size
Or if that's annoying, you can use the texelFetch function. There are many variations, but it will accept integer pixel coordintes.

Writing GLSL code



Miscellaneous notes

Don’t use short circuit conditional evaluations

bool early_exit = false;
if (early_exit && texture2DRect(tex0, pos).x > 0.0) {
   // do something here ...

Does the texture fetch happen? Probably, though it's implementation-dependent. Bottom line: don't rely upon sequential logic in branching statements.

Debugging GLSL code

Suppose your code compiles, but what you see makes no sense

There are no true debugging features built into GLSL. So you're basically limited to writing color outputs.

  1. Swap out any complicated code for simple code
  2. Create branches based on expected values and behvaior
  3. output really obvious colors (like magenta) based upon your branching
  4. Recursively do the above until you don't see the image you expect

Still having troubles?

  1. Double check your blend modes, stencil testing, alpha testing, depth testing, texture bindings, etc
  2. Check for OpenGL errors using glGetError()
  3. Reset your shaders to pass through shaders for sanity check