A challenge in modern graphics programming is the management of complicated shaders. The huge amount of materials, lights and assorted conditions lead to a combinatorial explosion in shader code-paths.
There are many ways to cope with this problem and a lot of techniques have been developed.
Some engines like Unreal have taken the way lead by 3D modelling applications and allow designers to ‘compose’ shaders from pre-created nodes that they link in shade trees. An extensive description of the technique can be found in the paper “Abstract Shade Trees” by McGuire et al.. This way however the “Material editor” of the application usually has to be some sort of tree editor. Shaders generated this way might have performance issues if the designer didn’t pay attention but of course they are the ones that give major freedom to that said artist.
Another technique is building shaders on-the-fly from C++ code as shown in “Shader Metaprogramming” by McCool et al.. I’ve never tried such a shader definition although I find it very compelling due mostly to it’s technical implementation. You’d have to rebuild and relink C++ code on the fly to allow for interactive iterations when developing or debugging which is not very difficult to achieve but seems a bit awkward to me. The gains in code portability however should not be underestimated.
Über-shaders and SuperShaders usually build upon the preprocessor and enable/disable parts of the code via defines. The major drawback is that the ‘main’ shader in the end always becomes a giant unreadable mess of #ifdefs that is particularly unpleasant to debug.
A small variant of the SuperShader way is to use ‘static const’ variables injected by the native code and plain ‘if’s on them in the shader. All compilers I’ve seen are smart enough to compile-out any branching and essentially the static const variables work as preprocessor macros with the added bonus that if looks better than #ifdef and the code is a bit easier to read. On complex code all the SuperShader problems remain.
Dynamic shader linking
introduced in Shader Model 5 allows to have interfaces and some sort of virtual method calls in your shaders and allows for very elegant code.
I’d like to share an idea and sample implementation of an enhanced syntax over HLSL SM4 and SM5. It is heavily influenced by the idea of dynamic linking, ASTs and “Automated Combination of Real-Time Shader Programs”
with some additional features and was originally developed in order to support DirectX 10+-level hardware. Although the sample application works only on SM4 and SM5 it could relatively easily be ported to any modern shading language. On sm5 you could just use the built-in dynamic linkage feature.
In essence the program translates the ‘enhanced’ shader to plain HLSL. The translator works like a preprocessor so no AST is built on the code.