Sign in to follow this  
Gondolin

Tangent Space Normal Mapping

Recommended Posts

I'm having issues getting tangent space normal mapping to render perfectly. I can get the lighting vector to correctly transform based on an objects inverse world matrix. I can also get the normal-based lighting to work. However, I can't put these two things together. Either the lighting works, but doesn't transform (i.e. object rotates, but lighting on it does not) or I can get the lighting to transform but it doesn't look good (looks like its being lit too evenly or something). Here's some screen shots showing what I mean: Hear the normal mapping looks good, but doesn't transform when rotated. Looks good, doesn't transform. This time I have fixed the transform problem, but now the normal mapping looks dull, flat, and undetailed. Looks good, doesn't transform. Here is the current effect code (corresponding to the second screen shot):
matrix ViewMatrix;
matrix ProjMatrix;
float3 Eye;

vector AmbientLight = {0.2f, 0.2f, 0.2f, 1.0f};
float3 LightPosition = {0.0f, 0.0f, 0.0f};

texture DiffuseTexture;
matrix DiffuseTransform;
texture CloudsTexture;
matrix CloudsTransform;
texture NormalTexture;
matrix NormalTransform;

vector DiffuseColor;
vector AmbientColor;
vector SpecularColor;
vector EmissiveColor;
float SpecularPower;

matrix WorldMatrix;
matrix InverseWorld;


//--------------------------------------------------------------------------------------
// Texture Samplers
//--------------------------------------------------------------------------------------
sampler DiffuseSampler = sampler_state
{
	Texture = <DiffuseTexture>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
	AddressU = Clamp;
	AddressV = Clamp;
};

sampler CloudsSampler = sampler_state
{
	Texture = <CloudsTexture>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
	AddressU = Clamp;
	AddressV = Clamp;
};

sampler NormalSampler = sampler_state
{
	Texture = <NormalTexture>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

//--------------------------------------------------------------------------------------
// Input/Output Structures
//--------------------------------------------------------------------------------------
struct VS_INPUT
{
	float3 position : POSITION;
	float3 normal : NORMAL;
	float3 tangent : TANGENT;
	float3 binormal : BINORMAL;
	float2 tex : TEXCOORD0;
};

struct VS_OUTPUT
{
	vector position : POSITION;
	float2 texDiff : TEXCOORD0;
	float2 texNorm : TEXCOORD1;
	float2 texClds : TEXCOORD2;
	float3 lightDir : TEXCOORD3;
	float3 halfway : TEXCOORD4;
};


//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
VS_OUTPUT RenderVS(VS_INPUT input)
{
    VS_OUTPUT output = (VS_OUTPUT)0;
	
    //Transform vertex into clip space
	output.position = mul(mul(mul(float4(input.position, 1), WorldMatrix), ViewMatrix), ProjMatrix);
	
	//Tangent space transform
	float3x3 TangentSpace;
	TangentSpace[0] = input.tangent;
	TangentSpace[1] = input.binormal;
	TangentSpace[2] = input.normal;
					 
    //Transform texture coordinates
    output.texDiff = mul(vector(input.tex.x, input.tex.y, 1.0f, 1.0f), DiffuseTransform).xy; 
    output.texClds = mul(vector(input.tex.x, input.tex.y, 1.0f, 1.0f), CloudsTransform).xy;
    output.texNorm = mul(vector(input.tex.x, input.tex.y, 1.0f, 1.0f), NormalTransform).xy;
  
	//Lighting vectors
	output.lightDir = normalize(mul(float4(LightPosition, 1), InverseWorld) - input.position);
	output.halfway = normalize(mul(float4(Eye, 1), InverseWorld) - input.position);
	output.halfway = normalize(output.halfway + output.lightDir);
	output.lightDir = mul(TangentSpace, output.lightDir);
	output.halfway = mul(TangentSpace, output.halfway);
	 
    return output;    
}


//--------------------------------------------------------------------------------------
// Blend Functions
//--------------------------------------------------------------------------------------

float4 DefaultBlend(float4 src, float4 dest)
{
	src *= src.a;
	dest *= 1 - src.a;
	return src + dest;
}


//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 RenderPS(VS_OUTPUT input) : COLOR0
{
	//Normal mapping
	float4 normal_map = tex2D(NormalSampler, input.texNorm);
	float3 normal = 2 * normal_map.rgb - 1;
	
	//Diffuse color
	float4 diffuse = tex2D(DiffuseSampler, input.texDiff) * DiffuseColor;
	
	//Specular lighting
	float4 specular = SpecularColor * saturate(pow(dot(normal, input.halfway), SpecularPower));
	specular *= normal_map.a;

	//Combine diffuse and specular
	diffuse = saturate(diffuse + specular);
	
	//Clouds
	diffuse = DefaultBlend(tex2D(CloudsSampler, input.texClds), diffuse);
	
	//Diffuse lighting
	diffuse *= saturate(dot(normal, input.lightDir));
	
    //Compute final color
    return diffuse + (AmbientLight * AmbientColor) + EmissiveColor;
}


//--------------------------------------------------------------------------------------
// Technique
//--------------------------------------------------------------------------------------

technique Render
{
    pass P0
    {
        VertexShader = compile vs_2_0 RenderVS();
        PixelShader  = compile ps_2_0 RenderPS();
        
        AlphaBlendEnable = False;
    }
}

Does someone spot anything obviously wrong with this implimentation, or have any insight on why the second screen shot normal mapping looks the way it does?

Share this post


Link to post
Share on other sites
I'm not sure this would help, but try transforming the tangent space vectors (tangent,binormal,normal) using the inversetranspose world matrix (not only the inverse) and build your tangent space matrix from these transformed vectors (adjusting the rest of the code accordingly). At least you would get a more clear and easy-to-follow solution. Let me know if this helps...

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this