Creating a Production Quality Shader? (Uber)

Hi All,

I am new to GLSL shader writing but I have done RSL in renderman.
I am working with the Blender Game Engine implementation of GLSL. So all I really need are vertxProgs and fragmentProgs.

I would like to create a production style shader for the game engine. I consider a production style shader being a shader I would apply to every object. A production style shader should take all lights into account as well. (I have read the OpenGL only supports 8 lights in a scene)

The shader, itself, would have variables to turn on and off various features. Such as reflection, receive shadow, bump/normal, shadeless, emits etc…

I have played around with a few toon andphong shaders I have seen posted and they do produce limited results.

I am wondering how to proceed?

Should I just start with a standard phong shader and build from that or is there a better “beginning” shader that someone could offer up as a starting point?

Anyone have a CC-0 Uber shader?


What’s CC-0?

A number of us have done ubershaders. So if you have specific questions, just ask. For instance, to switch features on and off, it’s most efficient to have those be constants in the shader because then the shader can totally compile out the code for unused features as well as the “if” statements (and other conditions) that depend on them, making your shader faster to execute.

cc-0 (Creative Commons Zero License) means ok for me to use in my commercial projects.
So that is what I am asking for. Someone to post an ubershader for the whole world to use for free!

Mainly I am having problems with shadow casting. In Blender I have a phong shader that show up just fine on my mesh object using the default lights in the game engine. I place a plane under the object and I see shadows on the plane. But if I apply the phong shader to the plane object the shadow disappears. Apparently my shader can not receive shadows.

How do you code a GLSL shader to receive shadows?

Your question is very underspecified. First, unlike applying local effects (such as Phong lighting) to a surface, shadows aren’t a local effect. They are a global effect (which involve knowledge about the scene in general). Which means you need external logic providing input to your shader as to what’s in partially/totally in shadow and if so how much.

And that varies wildly depending on what shadow algorithm you want to use: not just the main type of algorithm such as shadow maps, shadow volumes, projected shadows, and other’s but specific details such as what if any type of filtering you’re going to apply, how you’re going to deal with translucent casters, volumetric shadowing, etc.

So check with your Blender docs/gurus and see what if any external shadowing information Blending can provide to your GLSL shader. If nothing, you may need to dig in and learn about the blender data model and shadow algorithms.

Ok, here is my phong shader so far.

Vertex Code:

varying vec3 N;
varying vec3 v;

void main(void)  
   v = vec3(gl_ModelViewMatrix * gl_Vertex);       
   N = normalize(gl_NormalMatrix * gl_Normal);

   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;  

Fragment Code:

varying vec3 N;
varying vec3 v;    

void main (void)  

   vec4 Iamb = vec4(0.0, 0.0, 0.0, 0.0);
   vec4 Idiff = vec4(0.0, 0.0, 0.0, 0.0);
   vec4 Ispec = vec4(0.0, 0.0, 0.0, 0.0); 
   for (int i=0;i<gl_MaxLights;i++)
       vec3 L = normalize(gl_LightSource[i] - v);   
       vec3 E = normalize(-v); // we are in Eye Coordinates, so EyePos is (0,0,0)  
       vec3 R = normalize(-reflect(L,N)); 
       //calculate Ambient Term:  
       Iamb += gl_FrontLightProduct[i].ambient;    
       //calculate Diffuse Term:  
       Idiff += gl_FrontLightProduct[i].diffuse * max(dot(N,L), 0.0);
       Idiff = clamp(Idiff, 0.0, 1.0);     
       // calculate Specular Term:
       Ispec += gl_FrontLightProduct[i].specular * pow(max(dot(R,E),0.0),0.3*gl_FrontMaterial.shininess);
       Ispec = clamp(Ispec, 0.0, 1.0); 
   // write Total Color:  
   gl_FragColor = gl_FrontLightModelProduct.sceneColor + Iamb + Idiff + Ispec;     

Blender supports a shadow type of simple which appears to be what you would call a shadow map. I have controls for map size and map bias. I understand shadow mapping, it is a picture taken from the point of view of the light. So forget all other shadow types and let’s create a shader that works with shadow maps.

How do I gain access to that information inside my fragment code? If I were in RSL I would just make a call to shadow() with a map name and return an alternate color based upon the result from shadow. Is there an equivalent to shadow in the GLSL language?

[QUOTE=Atomic;1243324]Blender supports … a shadow map. I have controls for map size and map bias. …

How do I gain access to that information inside my fragment code? If I were in RSL I would just make a call to shadow() with a map name and return an alternate color based upon the result from shadow. Is there an equivalent to shadow in the GLSL language?[/QUOTE]

Sure, the concept is exactly the same. It’s just a texture map containing depth values.

(I notice that you’re not specifying a GLSL version directive (#version 330) so I’ll pitch what I’m saying based on that to the default GLSL version – 1.20.)

First, up-top you need to declare a uniform for the shadow map that blender provides. Consult your Blender gurus/docs for what name they provide it as, and whether or not Blender enables hardware depth comparisons for that texture (i.e. uses a texture with a base type of GL_DEPTH_COMPONENT, enables GL_TEXTURE_COMPARE_MODE, and optionally enables GL_LINEAR filtering which gives you PCF). Pending the name, and assuming a 2D texture, you’ll need one of the following:

uniform sampler2DShadow ShadowTexture;    // If Blender enables HW depth comparisons/filtering
uniform sampler2D       ShadowTexture;    // If Blender does not and expects the shader to do it

You also need to dig up what they’re calling the matrix that gets passed into the shader to get you from the camera’s eye-space over into the light’s clip-space, and pass it in using a “uniform mat4 …;”. This lets you compute the shadow map texture coordinate based on the position of the current fragment.

Then you just feed it to one of the following to do the shadow texture lookup:

shadow2D() or shadow2DProj()    // If HW depth comparisons/filtering
texture2D() or texture2DProj()  // If not

to sample the shadow map. Note that in the HW depth comparisons/filtering case, you get back a 0…1 visibility value (where 0 is 0% lit – i.e. 100% shadow – and 1 is 100% lit). In the other case, you get back a depth value and have to do the depth comparison yourself in the shader.

Also note that in the case where you’re just doing a lookup for a directional light source, you don’t need the Proj versions (projective texturing), which internally do the divided-by-w for you. Of course, for point light sources you’ll need the divide-by-w, and can either use the Proj versions or do the divide-by-w yourself in the shader and just use the non-Proj versions.

(Depending on how they implemented it, point light sources might even feed in a cube shadow map texture rather than a 2D shadow map texture…)

Also note that post GLSL 1.3, the texture lookups were simplified, and you’d just call texture() or textureProj() – no more sampler types in the texture access function names.

That’ll give you basic shadow mapping. Then stack on filtering and/or acne prevention schemes (like biasing) to-taste.