Yep - I agree.
It’s a tricky subject though - you’ll find people that’ll tell you either story. I’m definitely in the “more shaders, fewer if-statements” camp…but it does depend on the nature of the art, how old the graphics cards your users might have, etc.
I try hard to keep all of my meshes up over 512 vertices - which is in the “sweet spot” for most GPU’s. My art guys use all sorts of sneaky tricks to be sure to have no more than one mesh per real-world object…and we group objects together where possible if they are really low polygon count.
I sometimes sort by shader type - but most often it’s enough to make sure that if I have 30 objects of type A and 30 of type B, that I render all of the A’s before all of B’s and not go A,B,A,A,B,A,B,B,A,B… or whatever. For most of the kinds of scenes I draw, that cuts the amount of shader switching time down to almost negligable proportions.
If the code on either side of the ‘if’ is very small and contains no texture lookups - then it’s not so horribly expensive. What makes me flinch is when someone will say “if high quality lighting is needed then do some fancy normal mapped thing - else use a pre-lit texture instead”. Not realizing that far from optimizing their code - they just made it do BOTH the normal map lookup and all the fancy calculations AND the pre-lit lighting lookup and calculation - only to throw away the results from whichever one wasn’t needed!!
I’ve seen commercial game programmers (who were not graphics-savvy) do six varieties of lighting and shading in one shader with ‘if’ statements to pick the most efficient one under different situations because they found that their lighting was too slow with only two versions. They were blown away when I removed all but the most expensive lighting code path of all…and their renderer went four times faster!
The GPU may look like a general-purpose CPU - but it’s really not. Understanding why things like ‘dependent texture read’ (where the output of one texture map can influence which texel is read from a second texture map) is so bad is an important part of writing good shader code. Very often, things that would be really efficient in C++ are a disaster in GLSL - and vice versa.
However, GPU’s are getting gradually better - and gradually more general-purpose - and perhaps in 5 or 10 years, we’ll be able to ignore all of this arcane stuff and just write code.
– Steve