Added GLSL preprocessor macro support

· by Steve · Read in about 2 min · (404 Words)

Preprocessor macros are very useful for re-using the same shader source code in small variant ways. For example (snippet from my depth shadowmap code):

#if PCF
    // use depths from prev, calculate diff
    depths += depthAdjust.xxxx;
    float final = (finalCenterDepth > shadowUV.z) ? 1.0f : 0.0f;
    final += (depths.x > shadowUV.z) ? 1.0f : 0.0f;
    final += (depths.y > shadowUV.z) ? 1.0f : 0.0f;
    final += (depths.z > shadowUV.z) ? 1.0f : 0.0f;
    final += (depths.w > shadowUV.z) ? 1.0f : 0.0f;
    final *= 0.2f;
    result = float4(vertexColour.xyz * final, 1);
#else
    result = (finalCenterDepth > shadowUV.z) ? vertexColour : float4(0,0,0,1);
#endif

The benefit here is that you can use the same shader source code in a number of different program definitions, calling each one a different program name in Ogre, thus being able to represent a number of related shader techniques with a smaller amount of coding than writing each one separately. Eihort has supported preprocessor macros in HLSL for a while now, and whilst I was porting the depth shadowmapping shaders I wanted to do the same in GLSL, so I started investigating how to do it. GLSL does support preprocessor macros, and it talks about them in the GLSL manual, but the documentation totally focuses on predefined definitions or things you might want to use within the shader source, and fails to talk about how you might set them from outside the shader itself.

In the end, after poring over the specs I concluded there wasn’t a specific compile option for this, unlike other shader languages. Defines had to be set in the source code, which was fine if you want to tweak it statically in your code, but if you want to choose one dynamically at compile time without altering your code it’s a pain. So, instead I used a feature of glShaderSourceARB, which is where you specify your GLSL source code just before compilation, in that it allows you to supply multiple source strings. So at load time, I check for any preprocessor definitions on the Ogre program definition (set using the ‘preprocessor_defines’ option, just like HLSL), and build an extra source string containing the required code to predefine these things, which I pass to glShaderSourceARB as the first string in the array, bumping the ‘real’ source to the second slot. This then behaves just like you changed the source code to alter your defines for this program instance, without actually having to. Handy. 😀