Jump to content
  • Advertisement
Sign in to follow this  
terryeverlast

Lighting done in tangent space?

This topic is 806 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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;
}
Edited by terryeverlast

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

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

Edited by terryeverlast

Share this post


Link to post
Share on other sites

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?

Edited by terryeverlast

Share this post


Link to post
Share on other sites

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.

Edited by Dingleberry

Share this post


Link to post
Share on other sites

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?

Edited by terryeverlast

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!