My current shader framwork is similar to yours, but i'm not using string comparison.

My base class, CObject, contains an integer varible named shaderID. When an object is created, shaderID is set the the current number of shaders, numShaders, kept by the current engine context's GLSL manager (CGlslManager). After setting shaderID, NewShader is called (which is a memeber of CGlslManager). This sets programs[numShaders] to a new instance of CGlsl, which loads and compiles the specified shader. numShaders is then incremented. When drawing an object, I just have to use glslManager->programs[obj->shaderID], which points to the shader in the list associated with the current object. Unfortunatly, this method pretty much assumes that you don't want to switch an objects shader at runtime.

For bumpmapping I just use a dot3 normal map. Not sure where your getting a two texture requirement (unless were talking about two different things).

My shaders:

vertex shader:

Code:

varying vec3 T;
varying vec3 B;
varying vec3 N;
varying vec3 v;
varying vec2 varTexCoord;
void main(void)
{
v = vec3(gl_ModelViewMatrix * gl_Vertex);
T = normalize(vec3(gl_MultiTexCoord4.xyz)); //nvidia workaround
N = normalize(gl_NormalMatrix * gl_Normal);
B = normalize(cross(T,N));
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
varTexCoord = vec2(gl_MultiTexCoord0);
}

fragment shader:

Code:

uniform sampler2D baseMap;
uniform sampler2D bumpMap;
uniform sampler2D glossMap;
uniform float bumpiness;
uniform float glossiness;
varying vec3 T;
varying vec3 B;
varying vec3 N;
varying vec3 v;
varying vec2 varTexCoord;
varying vec2 varLightCoord;
float saturate( float inValue)
{
return clamp(inValue, 0.0, 1.0);
}
vec4 light()
{
vec4 color;
vec3 vBump;
vBump = (2.0*(texture2D(bumpMap, varTexCoord).xyz))- 1.0;
vec3 smooth = vec3(0.5, 0.5, 1.0);
vBump = mix( smooth, vBump , bumpiness );
vBump = normalize(vBump );
vec3 light;
vec3 L = normalize(gl_LightSource[0].position.xyz - v);
light.x = dot(T,L);
light.y = dot(B,L);
light.z = dot(N,L);
float distSqr = dot(light,light);
light*=distSqr;
vec3 E = normalize(-v);
float dist = length(light);
float atten = clamp(1.0 - (-(1.0/1000.0)) * sqrt(distSqr), 0.0, 1.0);
//calculate Ambient Term:
vec4 Iamb = gl_LightSource[0].ambient*gl_FrontMaterial.ambient;
//calculate Diffuse Term:
vec4 Idiff = (gl_LightSource[0].diffuse * max(dot(vBump,light), 0.0))*texture2D(baseMap, varTexCoord);
// calculate Specular Term:
vec4 Ispec = (texture2D(baseMap, varTexCoord)*gl_FrontMaterial.specular) * saturate(4.0*dot(reflect(-E,vBump),light)-3.0);
Ispec *= (Ispec+(Idiff*vec4(0.25,0.25,0.25,1.0)))*(texture2D(glossMap, varTexCoord)*glossiness);
color = gl_FrontMaterial.emission+Iamb+atten*(gl_FrontMaterial.ambient + Idiff + Ispec);
return color;
}
void main (void)
{
gl_FragColor = light();
}

This works pretty good albeit the lighting is probably inaccurate. I'm almost positive I'm doing something wrong in that shader somewhere.

It only requires five inputs, (baseMap, bumpMap, glossMap, bumpiness, and glossiness). The rest is pulled from OpenGL built-in types (such as light info and color materials).