Lighting done in tangent space?

Started by
4 comments, last by terryeverlast 7 years, 11 months ago

Instead of doing lighting in world space, we can transform the eye and light vector from world space into tangent space and do all the lighting calculations in that space. Modify the normal mapping shader to do the lighting calculations in tangent space.

so I edited the normalsampletoworldspace taking the transpose of the TBN matrix


float3 NormalSampleToWorldSpace(float3 normalMapSample, float3 unitNormalW, float3 tangentW)
{
	// Uncompress each component from [0,1] to [-1,1].
	float3 normalT = 2.0f*normalMapSample - 1.0f;

	// Build orthonormal basis.
	float3 N = unitNormalW;
	float3 T = normalize(tangentW - dot(tangentW, N)*N);
	float3 B = cross(N, T);

	float3x3 TBN = float3x3(T, B, N);
	float3x3 test = transpose(TBN);
	// Transform from tangent space to world space.
	float3 bumpedNormalW = mul(normalT, test);

	return bumpedNormalW;
}

Also changed the toEye vector like

float3 toEye = bumpedNormalW2*(gEyePosW - pin.PosW);

so the transposed TBN TIMES toEye.

I also mulitiplied the lightvec by bumpedNormalW2. my images are showing up dark(the first one is without normal map, second is with normal map)

one.jpgtwo.png

Is there anything else that needs to be done to make the lighting in tangent space. what seems to be the problem?

here is the orignianl shader code and then edited to make tangent space normal mapping


float4 PS(VertexOut pin, 
          uniform int gLightCount, 
		  uniform bool gUseTexure, 
		  uniform bool gAlphaClip, 
		  uniform bool gFogEnabled, 
		  uniform bool gReflectionEnabled) : SV_Target
{
	// Interpolating normal can unnormalize it, so normalize it.
	pin.NormalW = normalize(pin.NormalW);

	// The toEye vector is used in lighting.
	float3 toEye = gEyePosW - pin.PosW;

	// Cache the distance to the eye from this surface point.
	float distToEye = length(toEye);

	// Normalize.
	toEye /= distToEye;
	
    // Default to multiplicative identity.
    float4 texColor = float4(1, 1, 1, 1);
    if(gUseTexure)
	{
		// Sample texture.
		texColor = gDiffuseMap.Sample( samLinear, pin.Tex );

		if(gAlphaClip)
		{
			// Discard pixel if texture alpha < 0.1.  Note that we do this
			// test as soon as possible so that we can potentially exit the shader 
			// early, thereby skipping the rest of the shader code.
			clip(texColor.a - 0.1f);
		}
	}

	//
	// Normal mapping
	//

	float3 normalMapSample = gNormalMap.Sample(samLinear, pin.Tex).rgb;
	float3 bumpedNormalW = NormalSampleToWorldSpace(normalMapSample, pin.NormalW, pin.TangentW);
	 
	//
	// Lighting.
	//

	float4 litColor = texColor;
	if( gLightCount > 0  )
	{  
		// Start with a sum of zero. 
		float4 ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
		float4 diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
		float4 spec    = float4(0.0f, 0.0f, 0.0f, 0.0f);

		// Sum the light contribution from each light source.  
		[unroll]
		for(int i = 0; i < gLightCount; ++i)
		{
			float4 A, D, S;
		
			ComputeDirectionalLight(gMaterial, gDirLights[i], bumpedNormalW, toEye, 
				A, D, S);

			ambient += A;
			diffuse += D;
			spec    += S;
		}

		litColor = texColor*(ambient + diffuse) + spec;

		if( gReflectionEnabled )
		{
			float3 incident = -toEye;
			float3 reflectionVector = reflect(incident, bumpedNormalW);
			float4 reflectionColor  = gCubeMap.Sample(samLinear, reflectionVector);

			litColor += gMaterial.Reflect*reflectionColor;
		}
	}
 
	//
	// Fogging
	//

	if( gFogEnabled )
	{
		float fogLerp = saturate( (distToEye - gFogStart) / gFogRange ); 

		// Blend the fog color and the lit color.
		litColor = lerp(litColor, gFogColor, fogLerp);
	}

	// Common to take alpha from diffuse material and texture.
	litColor.a = gMaterial.Diffuse.a * texColor.a;

    return litColor;
}


float4 PS(VertexOut pin, 
          uniform int gLightCount, 
		  uniform bool gUseTexure, 
		  uniform bool gAlphaClip, 
		  uniform bool gFogEnabled, 
		  uniform bool gReflectionEnabled) : SV_Target
{
	// Interpolating normal can unnormalize it, so normalize it.
	pin.NormalW = normalize(pin.NormalW);
	float3 normalMapSample2 = gNormalMap.Sample(samLinear, pin.Tex).rgb;
	float3 bumpedNormalW2 = NormalSampleToWorldSpace(normalMapSample2, pin.NormalW, pin.TangentW);
	// The toEye vector is used in lighting.
	float3 toEye = bumpedNormalW2*(gEyePosW - pin.PosW);

	// Cache the distance to the eye from this surface point.
	float distToEye = length(toEye);

	// Normalize.
	toEye /= distToEye;
	
    // Default to multiplicative identity.
    float4 texColor = float4(1, 1, 1, 1);
    if(gUseTexure)
	{
		// Sample texture.
		texColor = gDiffuseMap.Sample( samLinear, pin.Tex );

		if(gAlphaClip)
		{
			// Discard pixel if texture alpha < 0.1.  Note that we do this
			// test as soon as possible so that we can potentially exit the shader 
			// early, thereby skipping the rest of the shader code.
			clip(texColor.a - 0.1f);
		}
	}

	//
	// Normal mapping
	//

	float3 normalMapSample = gNormalMap.Sample(samLinear, pin.Tex).rgb;
	float3 bumpedNormalW = NormalSampleToWorldSpace(normalMapSample, pin.NormalW, pin.TangentW);
	 
	//
	// Lighting.
	//

	float4 litColor = texColor;
	if( gLightCount > 0  )
	{  
		// Start with a sum of zero. 
		float4 ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
		float4 diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
		float4 spec    = float4(0.0f, 0.0f, 0.0f, 0.0f);
		
		// Sum the light contribution from each light source.  
		[unroll]
		for(int i = 0; i < gLightCount; ++i)
		{
			float4 A, D, S;
			ComputeDirectionalLight(gMaterial, gDirLights[i], bumpedNormalW, toEye, 
				A, D, S,bumpedNormalW);

			ambient += A;
			diffuse += D;
			spec    += S;
		}

		litColor = texColor*(ambient + diffuse) + spec;

		if( gReflectionEnabled )
		{
			float3 incident = -toEye;
			float3 reflectionVector = reflect(incident, bumpedNormalW);
			float4 reflectionColor  = gCubeMap.Sample(samLinear, reflectionVector);

			litColor += gMaterial.Reflect*reflectionColor;
		}
	}
 
	//
	// Fogging
	//

	if( gFogEnabled )
	{
		float fogLerp = saturate( (distToEye - gFogStart) / gFogRange ); 

		// Blend the fog color and the lit color.
		litColor = lerp(litColor, gFogColor, fogLerp);
	}

	// Common to take alpha from diffuse material and texture.
	litColor.a = gMaterial.Diffuse.a * texColor.a;

    return litColor;
}
Advertisement

What's NormalSampleToWorldSpace supposed to be doing? If you want to do your lighting in tangent space, you don't need to transform your sampled tangent space normal.

You need to transform every light direction into tangent space, and also your eye vector.


float3 toEye = bumpedNormalW2*(gEyePosW - pin.PosW);
This needs to be converting the world space eye position/direction to tangent space. I'm not sure what's going on here, but the eye direction shouldn't be some scale of a normal.

Ok. Update so far.

I left alone the NormalSampleToWorldSpace, my mistake.

I then


float3 normalMapSample = gNormalMap.Sample(samLinear, pin.Tex).rgb;
	float3 bumpedNormalW = NormalSampleToWorldSpace(normalMapSample, pin.NormalW, pin.TangentW);
	 float3 wittbn = mul(bumpedNormalW,(float3x3)gWorldInvTranspose);

And multiplied the Eye and Light Vector by wittbn.

Still same lighting problrm.The function NormalSampleToWorldSpace can be seen here:


//---------------------------------------------------------------------------------------
// Transforms a normal map sample to world space.
//---------------------------------------------------------------------------------------
float3 NormalSampleToWorldSpace(float3 normalMapSample, float3 unitNormalW, float3 tangentW)
{
	// Uncompress each component from [0,1] to [-1,1].
	float3 normalT = 2.0f*normalMapSample - 1.0f;

	// Build orthonormal basis.
	float3 N = unitNormalW;
	float3 T = normalize(tangentW - dot(tangentW, N)*N);
	float3 B = cross(N, T);

	float3x3 TBN = float3x3(T, B, N);

	// Transform from tangent space to world space.
	float3 bumpedNormalW = mul(normalT, TBN);

	return bumpedNormalW;
}

My directional light code goes


void ComputeDirectionalLight(Material mat, DirectionalLight L, 
                             float3 normal, float3 toEye,
					         out float4 ambient,
						     out float4 diffuse,
						     out float4 spec
							 in float3 testing)
{
	// Initialize outputs.
	ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
	diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
	spec    = float4(0.0f, 0.0f, 0.0f, 0.0f);

	// The light vector aims opposite the direction the light rays travel.
	float3 lightVec = -L.Direction * testing; //the light vector

	// Add ambient term.
	ambient = mat.Ambient * L.Ambient;	

	// Add diffuse and specular term, provided the surface is in 
	// the line of site of the light.
	
	float diffuseFactor = dot(lightVec, normal);

	// Flatten to avoid dynamic branching.
	[flatten]
	if( diffuseFactor > 0.0f )
	{
		float3 v         = reflect(-lightVec, normal);
		float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);
					
		diffuse = diffuseFactor * mat.Diffuse * L.Diffuse;
		spec    = specFactor * mat.Specular * L.Specular;
	}
}

The wittbn gets pumped into the ComputeDirectionalLight as testing

The normal are not being shaded right and teh picture is messed up and dark

SO what what i multiply agaisnt the eye and light vector to make the computation on normals in tangent space.

would i just take the inverse or transform of the tangent space in world space times(*) eye and light vector?

Would I take the normal and tangent from the vertex shader and keep them in local space and not in world space?

The only thing that's going to be in tangent space already is your normal map sample. You shouldn't be transforming the sampled normal by anything, except to maybe decompress it. Every other value needs to be transformed to tangent space using the interpolated tangent/bitangent/normal vectors -- which is sort of a hassle and why in many cases it's easier to do the single transform of normals from tangent space to world space with everything else.

This is because every pixel is going to have its own interpolated tangent space. There's only one world space though, so everything can share the light directions and positions etc. in world space.

What's your reason for doing the lighting in tangent space anyway? It's not going to be hugely expensive or anything but kind of odd if everything was working before in world space.

Its a exercise in Introduction to 3D Game Programming with Direct3D 11.0 by Frank Luna book.

Anyways...what ive done so far is multiply my Tangent Vector by a viewspace matrix instead of being in world space in the vertex shader


vout.TangentW = mul(vin.TangentL, (float3x3)gWorldView);

and my eye vector like



	float3 N = pin.NormalV;
	float3 T = normalize(pin.TangentV - dot(pin.TangentV, N)*N);
	float3 B = cross(N, T);

	float3x3 TBN = float3x3(T, B, N);
	float3x3 TBN3 = transpose(TBN);
	

	// The toEye vector is used in lighting.
	float3 toEye = mul(gEyePosW - pin.PosW,TBN3);

my image looks messed up like below

pole.jpg

Am I going in the right path so far?

This topic is closed to new replies.

Advertisement