8.3. Shader Debugging
Shader development tools are in their infancy, so debugging shaders can be a difficult task. Here are a few practical tips that may be helpful as you try to debug your shaders.
8.3.1. Use the Vertex Shader Output
To determine whether vertex shader code is behaving as expected, you can use conditionals to test intermediate values to see if a value is something unexpected. If it is, you can modify one of the shader's output values so that a visually distinct change occurs. For instance, if you think that the value foo should never be greater than 5.0, you can set the color values that are being passed to the fragment shader to black or pink or neon green if the value of foo exceeds 5.0. If that's not distinctive enough and you've already computed the transformed homogeneous position, you can do something like this:
if (foo > 5.0) gl_Position += 1.0;
This code adds 1 to each component of the transformed position for which foo was greater than 5. When it is executed, you should see the object shift on the screen. With this approach, you can systematically check your assumptions about the intermediate values being generated by the vertex shader.
8.3.2. Use the Fragment Shader Output
Fragment shaders can produce a fragment color, a fragment depth, or an array of fragment data values. You can use the discard keyword to prevent the computed fragment value from updating the frame buffer. The depth value may not be helpful during debugging, but you can either color-code your fragment colors or use the discard keyword to discard fragments with certain qualities. These techniques provide you with visual feedback about what's going on within the shader.
For instance, if you're not quite sure if your 2D texture coordinates span the whole range from 0 to 1.0, you could put an if test in the shader and discard fragments with certain qualities. You can discard all the fragments for which both s and t texture coordinates are greater than 0.5 or for which either coordinate is greater than 0.99, and so on. The model will be drawn with "missing" pixels where fragments were discarded. The discard keyword is quite useful because it can appear anywhere in a fragment shader. You can put the discard statement near the beginning of a complex fragment shader and gradually move it down in the code as you verify that things are working properly.
Similarly, you can assign values to gl_FragColor that convey debugging information. If you have a mathematical function in your shader that is expected to range from [0,1] and average 0.5, you can assign solid green to gl_FragColor if the value is less than 0, solid red if it is between 0 and 0.5, solid blue if it is between 0.5 and 1.0, and solid white if it is greater than 1.0. This kind of debugging information can quickly tell you whether a certain computation is going astray.
8.3.3. Use Simple Geometry
For debugging texture computations, it may be useful to render a single large rectangle with identity matrices for the modeling, viewing, and projection matrices and to look carefully at what is occurring. Use a distinct texture image, for example, color bars or a grayscale ramp, so that you can visually verify that the texturing operation is occurring as you expect it to.