Managing Cg Shaders Feb 22, 2006
Games today are using an increasing the number of pixel & vertex shaders to take advantage of newer graphics cards. I'll describe some methods that can be used to manage all these different shaders. Now I admit that I don't consider myself a shader specialist, and I don't keep myself up to date by reading papers and manuals, so maybe there will be better ways to do what I mention, but I think this is a topic to be aware of.
General
I have 3 directories, one for vertex shaders and one for pixel shaders, one for post-processing pixel shaders. I have a CgManager c++ class, and when initialized it checks these directories and loads all the Cg files found in them. There's also a class for managing complete shaders, CShader (Textures + Vertex shader + Pixel shader + shader variables like specular exponent) When rendering an object the attached CShader makes a call like: CGManager->SetShaders("Mesh", "Normal"); Then if needed the vertex & pixel shaders are bound and the uniform variables are set. It's important to be able to reload the Cg shaders within the program, so you can see the results of altering a shader right away. I have a shortcut key to reload changed Cg files. The CGManager saves the last modified date on loading, and on reload if a file's date is changed, it destroys the associated cg program, then loads the new code. Template Code
A method to consider if you find yourself having to copy/paste/alter cg shader code, creating multiple similar shaders, is having the program generate shader code itself. I don't really do this, rather I allow templated tags in the code that are replaced with mutiple pieces of code. I chose this just because it's simpler, if I need more later I might try a more advanced generator. A couple examples of where this can be useful: Below is an example of a templated Cg function. When my CGManager reads in the source, it checks for the tags [TEX_FUNCS] and [GET_DIFFUSE], and replaces them with the proper Cg code so that overlayed diffuse textures( with multiple blend types) are automatically available without manual rewriting. It might be easier to do the same kind of templating using static booleans and if statements (eg. static const bool g_bOverlay = true; ). I also once tried using uniform variables and if statements, since that would be easier than generating variations, but I noticed a speed hit. I don't know if that will always be the case. [TEX_FUNCS] half4 main(half4 lColor : COLOR0, half2 uv : TEXCOORD0, half2 uv2 : TEXCOORD3, half3 LV : TEXCOORD1, uniform sampler2D diffuseMap : TEXUNIT0, uniform sampler2D normalMap : TEXUNIT1, uniform sampler2D diffuseMap2 : TEXUNIT2, uniform half3 ambientColor, uniform half3 fogColor) : COLOR { half3 N = (half3)tex2D(normalMap, uv).xyz * 2.0 -1.0; half NdotL = saturate ( dot(N, LV.xyz) ); half3 colorMap = [GET_DIFFUSE]; half3 C = (lColor.xyz * NdotL * colorMap.xyz) + (ambientColor * colorMap.xyz); C = C * lColor.a; return half4(C, 1); } |