Problem with Phong+Normal map shader (reflection vector)

Started by
3 comments, last by vprat 14 years, 5 months ago
Hi, I am currently implementing per pixel phong shading + normal mapping using DX10 and HLSL. I have the diffuse + normal map component working, but I am having problems with the specular component. First a picture of my result: my problem As you can see, the specular result is as if the viewer or the light was on the left side of the cube. The diffuse result shows what is correct. As my diffuse-only result is fine, I assume the tangent space calculations are correct (maybe I am wrong). So I am left with a problem most probably with the reflection vector calculation or the view vector calculation. Below is the relevant code for the sample. I have generated a cube (but have same results with sphere and plane) where the front face (-z as normal) is generated like this:

// Normal is -Z axis
// Tangent is X axis
// Tangent.w is -1 so that the binormal is correct
//--
pVertex->vPosition	= CVector3( fWidth * ( u - 0.5f ), fHeight * ( v - 0.5f ), -0.5f * fDepth );
pVertex->vNormal	= CVector3( 0.0f, 0.0f, -1.0f );
pVertex->vTangent	= CVector4( 1.0f, 0.0f, 0.0f, -1.0f );
pVertex->vTexCoord	= CVector2( u, 1.0f - v );

My vertex shader is the following:

VS_OUTPUT output = (VS_OUTPUT) 0;		

// Some vectors in world space
//--
float4 P = mul( float4( input.position, 1), kgfx_matWorld );	// Position in world space
float3 L = normalize( kgfx_vLightPosition.xyz - P.xyz );		// Light in world space
float3 V = normalize( kgfx_vCameraPosition.xyz - P.xyz );		// View in world space
				
// Build world to tangent space transformation
//--
float3 T = normalize( mul( input.tangent, (float3x3) kgfx_matWorld ) );
float3 N = normalize( mul( input.normal, (float3x3) kgfx_matWorld ) );
float3 B = normalize( input.tangent.w * cross( N, T ) );
		
float3x3 TBN;
TBN[0] = T;
TBN[1] = B;
TBN[2] = N;
	
// Build the output
//--
output.position	= mul( P, kgfx_matViewProj );	// Position in screen space
output.texCoord	= input.texCoord;		// Copy tex coords
output.light	= normalize( mul( TBN, L ) );	// Light in texture space
output.view	= normalize( mul( TBN, V ) );	// View in texture space
    
return output;

My pixel shader is the following:

float3 L = normalize( input.light );    // Light in tangent space
float3 V = normalize( input.view );     // View in tangent space
float3 N = float3( 0, 0, 1 );	        // Default normal in tangent space to compute self shadowing
float fSelfShadow = dot( N, L );		
if ( fSelfShadow>=0 )
{
	// Get normal from normal map
	//--
	N = normalize( 2.0 * kgfx_texNormalMap.Sample( linearSampler, input.texCoord ) - 1.0 );	
}		
		
fSelfShadow = saturate( 10 * fSelfShadow );	    
		
// Light reflection vector
//--
float3 R = normalize( reflect( L, N ) );	
		
float fSpecular = pow( saturate( dot( R, V ) ), 60 );		
float fDiffuse = saturate( dot( N, L ) );		
float4 colColor = kgfx_texColorMap.Sample( linearSampler, input.texCoord );
				
output.color = fSelfShadow * ( fDiffuse * colColor + fSpecular * kgfx_colLightColor );

Advertisement
Looks like the reflected vector is upside down.
In your fragment shader try :
float3 R = - reflect( L, N ) ;//No need to normalize because L and N are already normalized.
Instead of:
float3 R = normalize( reflect( L, N ) );
You can also remove some normalizations. There are too many :)
Hi,

First of all, thanks a lot for your input.

It works indeed when inverting the reflection vector. Why is this vector inverted? I have never seen in the other tutorials any -reflect( L, N ). Is it a problem with my tangent space calculations?

Also, the shader works well without any normal mapping (N = 0, 0, 1) but not any more when I fetch the normal from the normal map.

The normal map texture is created in the format R8G8B8A8_UNORM and is a normal map taken from blender I think so should be a valid file.
Quote:Original post by vprat
It works indeed when inverting the reflection vector. Why is this vector inverted? I have never seen in the other tutorials any -reflect( L, N ). Is it a problem with my tangent space calculations?


In this tutorial the reflected vector is calculated like this:
    float D = saturate(dot(N, LightDir));         float3 R = normalize(2 * D * N - LightDir);  // R

which is equivalent (beside the saturation of dot(N,LightDir)) to:
R = normalize(- reflect(LightDir,N));
(see the definition of the reflect() function)

This is what is happening according to the code of your OP :
reflexion
--------------------------------------
As I said before, you really don't need to do as much normalizations. Try to remove all normalize() calls in your vertex shader and see what happens.

Hi,

I got it working properly. One thing was the reflect vector being inverted. Other thing was that in my sampling of the normal map I had a float being implicitly casted to a float3 but not as expected so the line:
N = normalize( 2.0 * kgfx_texNormalMap.Sample( linearSampler, input.texCoord ) - 1.0 );

was corrected into:
N = normalize( 2.0 * kgfx_texNormalMap.Sample( linearSampler, input.texCoord ) - float3( 1, 1, 1 ) );


I also removed most normalisation from the VS and some from the PS and took a normal map in the PNG format (JPEG showed the compression artifacts).

Thanks a lot for helping, very appreciated.

For those who could be interested, here is the final code:

//----------------------------------------------------------------------------// Types //----------------------------------------------------------------------------struct VS_INPUT{	float3 position		: POSITION;	float3 normal		: NORMAL;	float2 texCoord		: TEXCOORD0;	float4 tangent		: TANGENT;};struct VS_OUTPUT{	float4 position		: SV_POSITION;	float2 texCoord		: TEXCOORD0;	float3 view		: TEXCOORD1;	float3 light		: TEXCOORD2;};struct PS_OUTPUT{	float4 color		: SV_TARGET;};//----------------------------------------------------------------------------// Shaders //----------------------------------------------------------------------------VS_OUTPUT myVertexShader( VS_INPUT input, uniform bool bWireframe = false ){	VS_OUTPUT output = (VS_OUTPUT) 0;			// Some vectors in world space	//--	float4 P = mul( float4( input.position, 1), kgfx_matWorld );	// Position in world space	float3 L = kgfx_vLightPosition.xyz - P.xyz;						// Light in world space	float3 V = kgfx_vCameraPosition.xyz - P.xyz;					// View in world space					float3 T = mul( input.tangent, (float3x3) kgfx_matWorld );	float3 N = mul( input.normal, (float3x3) kgfx_matWorld );	float3 B = input.tangent.w * cross( N, T );			// Built the TBN matrix (world to texture space transform)	//--	float3x3 TBN;	TBN[0] = T;	TBN[1] = B;	TBN[2] = N;		output.position		= mul( P, kgfx_matViewProj );	// Position in screen space	output.texCoord		= input.texCoord;		// Copy tex coords	output.light		= mul( TBN, L );		// Light in texture space	output.view		= mul( TBN, V );		// View in texture space    	return output;}PS_OUTPUT myPixelShader( VS_OUTPUT input, uniform bool bWireframe = false ){	PS_OUTPUT output = (PS_OUTPUT) 0;	    	// Compute pixel value	//--	if ( bWireframe )	{		output.color = kgfx_colColor;	}	else 	{		float3 L = normalize( input.light );		float3 V = normalize( input.view );		float3 N = float3( 0, 0, 1 );			float fSelfShadow = dot( N, L );				if ( kgfx_bUseNormalMap>0 && fSelfShadow>=0 )		{			N = normalize( 2.0 * kgfx_texNormalMap.Sample( linearSampler, input.texCoord ) - float3( 1, 1, 1 ) );			}						fSelfShadow = saturate( 10 * fSelfShadow );	    				float3 R = reflect( -L, N );					float fSpecular = pow( saturate( dot( R, V ) ), 60 );				float fDiffuse = saturate( dot( N, L ) );				float4 colColor = kgfx_bUseColorMap<=0 ? kgfx_colColor : kgfx_texColorMap.Sample( linearSampler, input.texCoord );						output.color.rgb = fSelfShadow * ( fDiffuse * colColor.rgb + fSpecular * kgfx_colLightColor.rgb );		output.color.a = colColor.a;	}	return output;}

This topic is closed to new replies.

Advertisement