My current shading solution is relatively simple - the idea is to provide two generic paths that are toggled by branching in the shader:
- ShadeFull(), computes all terms and by default does specular & texture lookups, even if the maps are unbound or empty; multitexturing is branched to up to 2 TUs
- ShadeBasic(), does basic lighting, but doesn't do any other lookups other than basic texturing
Things get a lot more interesting, however, when adding support for full multitexturing and effects like detail and bump mapping. These are really easy to do as branches in the shader, but could also be branched on the CPU by creating separate shaders for major pipelines. Since I'll be generating the main shading component from pre-written modules on the fly anyway, it wouldn't be overly difficult to implement this either with minimal impact on maintenance.
Sorting geometry based on any number of surface properties on the CPU isn't going to be an issue, since I'll be making use of a low poly environment with tessellation. However, at the end of the day I'm having trouble coming up with a balanced and robust approach.
As there are plenty of industry-savvy people around to have tackled these choices before, I'd appreciate some insight :)