Hey hello there,
Here you have a shader that does exactly what you want:
//
// Green Gandalf's Bumpmapper v1.0
//
// Created 27 June 2006, Modified 26 July 2009.
//
// Bumpmapping using a texture and normal map with a single directional light source
// plus ambient light and specular reflection.
// Uses full normalization in the pixel shader.
//
// Corrected to adjust tangents and binormals to give seamless
// results on DBPro spheres (and tidied up a bit).
//3-nov-2010 MPL3D mod:include light map
matrix wvp : WorldViewProjection;
matrix mworld : World;
matrix winv: WorldInverse;
float4 eyePos : CameraPosition;
texture baseTexture < string ResourceName = ""; >;
texture nMapTexture < string ResourceName = ""; >;
//3-nov-2010 MPL3D mod:include light map
texture lMapTexture < string ResourceName = ""; >;
sampler baseSample = sampler_state
{ texture = <baseTexture>;
mipFilter = linear;
magFilter = linear;
minFilter = linear;
};
sampler nMapSample = sampler_state
{ texture = <nMapTexture>;
mipFilter = linear;
magFilter = linear;
minFilter = linear;
};
//3-nov-2010 MPL3D mod:include light map
sampler lMapSample = sampler_state
{ texture = <lMapTexture>;
mipFilter = linear;
magFilter = linear;
minFilter = linear;
};
float4 lightDir = {-1.0, 0.0, 0.0, 1.0};
float4 ambiColor = {0.2, 0.2, 0.2, 1.0};
float4 lightColor = {1.0, 1.0, 1.0, 1.0};
float specLevel = 0.5;
float specExpon=10.0;
struct VSInput
{ float4 pos : position;
float2 UV : texcoord0;
float3 tangent : tangent;
float3 binormal : binormal;
float3 normal : normal;
};
struct VSOutput
{ float4 pos : position;
float2 UV : texcoord0;
float3 light : texcoord1;
float3 view : texcoord2;
};
VSOutput GGBumpVShader(VSInput In, VSOutput Out)
{ Out.pos = mul(In.pos, wvp);
Out.UV = In.UV;
// adjust tangents and binormals - GG fix for seams
float3 n = normalize(In.normal);
float3 b = normalize(In.binormal);
float3 t = normalize(In.tangent);
//smooth out the tangents and binormals with the normals
float3 b2 = normalize(cross( n, t ));
b2 *= sign(dot(b2, b));
float3 t2 = normalize(cross( n, b2 ));
t2 *= sign(dot(t2, t));
float3 t3 = normalize(cross( n, b ));
t3 *= sign(dot(t3, t ));
float3 b3 = normalize(cross( n, t3 ));
b3 *= sign(dot(b3, b));
t = normalize((t2+t3)*0.5);
b = normalize((b2+b3)*0.5);
float3x3 TSM = {t, b, n};
// float3x3 TSM = {In.tangent, In.binormal, In.normal};
TSM = transpose(TSM);
float3 temp = -mul(lightDir.xyz, winv);
Out.light = mul(temp, TSM);
//GG CHANGE
//temp = mul(eyePos, winv) - In.pos;
//temp = (mul(float4 (eyePos, 1), winv) - In.pos).xyz;
temp = (mul(eyePos, winv) - In.pos).xyz;
Out.view = mul(temp, TSM);
return Out;
}
struct PSInput
{ float2 UV : texcoord0;
float3 light : texcoord1;
float3 view : texcoord2;
};
struct PSOutput { float4 col : color; };
PSOutput GGBumpPShader(PSInput In, PSOutput Out)
{ float4 baseColour = tex2D(baseSample, In.UV);
float3 normal = 2 * tex2D(nMapSample, In.UV) - 1.0;
float3 tempLightDir = normalize(In.light);
float3 tempViewDir = normalize(In.view);
float diffuse = saturate(dot(normal, tempLightDir));
float3 reflect = 2 * diffuse * normal - tempLightDir;
float specular = diffuse * specLevel * pow(saturate(dot(reflect, tempViewDir)), specExpon);
Out.col = baseColour * (ambiColor + diffuse * lightColor) + specular * lightColor;
//3-nov-2010 MPL3D mod:include light map
baseColour = tex2D(lMapSample, In.UV);
Out.col += baseColour;
return Out;
}
technique t0
{ pass p0
{ VertexShader = compile vs_2_0 GGBumpVShader();
PixelShader = compile ps_2_0 GGBumpPShader();
}
}
It is probably not the best code out there but it works. It is based on a regular normal map shader by
Green Gandalf (many thanks GG

)
To use it, you must take some things into account:
Texture stages are like follow:
0 - diffuse map
1 - normal map
2 - light map
Now the variables, you must feed:
Feed per frame or as the ship moves:
Light direction: float4 lightDir = {-1.0, 0.0, 0.0, 1.0};
Set only once, as needed, or leave as they are, they have default values:
Ambient light: float4 ambiColor = {0.2, 0.2, 0.2, 1.0};
Light color: float4 lightColor = {1.0, 1.0, 1.0, 1.0};
Specularity level: float specLevel = 0.5;
Specularity factor: float specExpon = 10.0;
Improvements could be made by using the normal map alpha channel as the light map source, that would save a texture. Oh well, current way is easier to implement and easier to have the textures changed.
Just remember:
-Load the object (ajem)
-Load the shader
-Load the textures
-Texture each layer of the object
-Apply the shader to the object
-Set the shader variables if needed
-Refresh the needed shader variables once per loop
-Enjoy the coding all along with the results
Good luck!