This is all about simple Blinn Phong lichting with one directional light and a tangent-space-normalmap.
Ok this should be very simple but for whatever reason I can't get this right. I am pretty sure it has to do with the spaces (world space and tangent space). But I have already tried any combination. Transforming the normalmap to world space or transforming the light and view to tangent space. Both doesn't work.
In TV this shader is running on a mesh which is in lighting mode = bumpmapping tangentspace. Also the light values (colors and direction) are being set from within my program and I am 100% sure they are not messed up. So the problem is definitely in the shader.
What actually happens: The diffuse and specular are not correct. The are a bit set off and also depend somehow on the angle I look at the surface. However the light's direction is (0, -1, 0) so I should only get a specular reflection when I look straight down (my surface is flat on the ground and not rotated).
I am very sure the error is inside the pixel shader ans has to do with transforming the normals. Also one thing I have seen often is this:
normal = 2 * normalFromNormalmap -1;
What about that? Why adjusting the normal like this?
Ok here is the complete shader:
WorldIT : WORLDINVERSETRANSPOSE; // Needed to calculate the correct normal vector
// NOTE: We use 4x3 here because the 4th column of the matrix should not be used because it
// stores translate information which would mess up the normal since a normal is not affected
// by translations
float4x4 World : WORLD; // The world matrix
float3 viewPosition : VIEWPOSITION; // The camera position
// Options to be set by the program
float3 lightDirection;
float4 lightDiffuse;
float4 lightAmbient;
float4 lightSpecular;
texture texTexture : TEXTURE0;
texture texNormal : TEXTURE1;
sampler sampleTexture = sampler_state
{
Texture = (texTexture);
};
sampler sampleNormal = sampler_state
{
Texture = (texNormal);
};
// INPUT / OUTPUT STRUCTURES #########################################
struct VS_INPUT
{
float4 position : POSITION; // Vertex position in object space
float3 tangent : TANGENT;
float3 binormal : BINORMAL;
float3 normal : NORMAL;
float2 texCoord : TEXCOORD; // Vertex texture coordinates
};
struct PS_INPUT
{
float4 position : POSITION; // Pixel position in clip space
float2 texCoord : TEXCOORD0; // Pixel texture coordinates
float3 tangent : TEXCOORD1;
float3 binormal : TEXCOORD2;
float3 normal : TEXCOORD3; // Pixel normal vector
float3 view : TEXCOORD4; // Pixel view vector
};
// VERTEX SHADER #########################################
PS_INPUT VertexShaderFunction(VS_INPUT IN)
{
PS_INPUT OUT;
// Basic transformation of untransformed vertex into clip-space
OUT.position = mul(IN.position, WorldViewProj);
// No scaling or translation is done, simply assign them and let the GPU interpolate
OUT.texCoord = IN.texCoord;
// Calculate the normal vector
OUT.tangent = IN.tangent;
OUT.binormal = IN.binormal;
OUT.normal = IN.normal;
// Calculate the view vector
// transform the position from object to world space with the world matrix
float3 worldPos = mul(IN.position, World).xyz; // w coordinate is not needed
OUT.view = viewPosition - worldPos; // the view vector is the vector from the camera to the point
return OUT;
}
// PIXEL SHADER #########################################
float4 PixelShaderFunction(PS_INPUT IN) : COLOR
{
// The light direction must be normalized and inverted, surface-to-light
// Sample the textures
float4 texColor = tex2D(sampleTexture, IN.texCoord);
float4 texNormal = tex2D(sampleNormal, IN.texCoord);
// Calculate view and normal
float3 view = normalize(IN.view);
float3x3 tbn = mul(float3x3(IN.tangent, IN.binormal, IN.normal), (float4x3)World);
float3 normal = normalize(mul(texNormal.rgb, tbn));
// Calculate the half vector, the middle between the light and the view vector
float3 halfway = normalize(lightDirection + view);
// Calculate the diffuse color
float3 diffuse = saturate(dot(normal, lightDirection)) * lightDiffuse.rgb;
// Calculate the specular reflection
float3 specular = pow(saturate(dot(normal, halfway)), 20) * lightSpecular.rgb * texNormal.a;
// Combine all the color components
float3 color = saturate(lightAmbient + diffuse) * texColor + specular;
// Return the pixel
return float4(color, texColor.a);
}
// TECHNIQUES #########################################
technique render
{
pass p0
{
VertexShader = compile vs_3_0 VertexShaderFunction();
PixelShader = compile ps_3_0 PixelShaderFunction();
}
}