Normal map issue and theory

Started by
3 comments, last by dxCUDA 11 years, 11 months ago
I am having a long standing issue with the computation of my normal map within the shader(HLSL) file. As you can see there are corrupt triangles on the mesh at various and sometimes, symmetric points.

I think my issue may relate to interpolating vertex attributes across each triangle, when I calculate the input texture data; I cannot be sure however.

Thanks

whhhhyyy.jpg


//--------------------------------------------------------------------------------------
// Lighting
//--------------------------------------------------------------------------------------
//transformation matrices
matrix World;
matrix View;
matrix Projection;
//TEXTURE VARIABLES
//--------------------------------------------------------------------------------------
//color map texture
Texture2D tex2D;
//bump map texture
Texture2D bump2D;

//texture sampler state
SamplerState linearSampler
{
Filter = min_mag_mip_linear;
AddressU = Wrap;
AddressV = Wrap;
};
//LIGHTING STRUCTURES AND VARIABLES
//--------------------------------------------------------------------------------------
struct DirectionalLight
{
float4 color;
float3 dir;
};
struct Material
{
float Ka, Kd, Ks, A;
};
//lighting vars
DirectionalLight light;
Material material;
float4 ambientLight;
float3 eye;
float4x4 gTexMtx;
//RASTERIZER STATES
//--------------------------------------------------------------------------------------
RasterizerState rsSolid
{
FillMode = Solid;
CullMode = NONE;
FrontCounterClockwise = false;
};
//VERTEX AND PIXEL SHADER INPUTS
//--------------------------------------------------------------------------------------
struct VS_INPUT
{
float4 p : POSITION;
float2 t : TEXCOORD;
float3 n : NORMAL;
float3 tangent : TANGENT;
};

struct PS_INPUT_PP_BLINNPHONG
{
float4 p : SV_POSITION;
float2 t : TEXCOORD;
float3 n : TEXCOORD1;
float3 h : TEXCOORD2;
float3 tangent : TANGENT;
};
//--------------------------------------------------------------------------------------
// Blinn-Phong Lighting Reflection Model
//--------------------------------------------------------------------------------------
float4 calcBlinnPhongLighting( Material M, float4 LColor, float3 N, float3 L, float3 H )
{
float4 Ia = M.Ka * ambientLight;
float4 Id = M.Kd * saturate( dot(N,L) );
float4 Is = M.Ks * pow( saturate(dot(N,H)), M.A );

return Ia + (Id + Is) * LColor;
}
//--------------------------------------------------------------------------------------
// PER PIXEL LIGHTING
//--------------------------------------------------------------------------------------
PS_INPUT_PP_BLINNPHONG VS_PIXEL_LIGHTING_BLINNPHONG( VS_INPUT input )
{
PS_INPUT_PP_BLINNPHONG output;

//set position into clip space
input.p = mul( input.p, World );
output.p = mul( input.p, View );
output.p = mul( output.p, Projection );
//set texture coords
output.t = input.t;
// Output vertex attributes for interpolation across triangle.
//output.t = mul(float4(input.t, 0.0f, 1.0f), gTexMtx);
//set required lighting vectors for interpolation
float3 V = normalize( eye - (float3) input.p );
output.n = normalize( mul(float4(input.n, 0.0f ), World) );
output.h = normalize( -light.dir + V );

// Calculate the tangent vector against the world matrix only and then normalize the final value.
//output.tangent = mul(input.tangent, (float3x3)World);
output.tangent = mul(float4(input.tangent, 0.0f), World);
output.tangent = normalize(output.tangent);
return output;
}
float4 PS_PIXEL_LIGHTING_BLINNPHONG( PS_INPUT_PP_BLINNPHONG input ) : SV_Target
{

//renormalize interpolated vectors
input.n = normalize( input.n );
input.h = normalize( input.h );

//calculate lighting
float4 I = calcBlinnPhongLighting( material, light.color, input.n, -light.dir, input.h );

//with texturing
return I * tex2D.Sample(linearSampler, input.t);
}
float4 PS_NORMAL_MAP( PS_INPUT_PP_BLINNPHONG input) : SV_Target
{
float4 textureColor;
float4 bumpMap;
float3 bumpNormal;
float3 bitangent;
float lightIntensity;
float4 color;
textureColor = tex2D.Sample(linearSampler, input.t);
bumpMap = bump2D.Sample(linearSampler, input.t);

//renormalize interpolated vectors
input.h = normalize( input.h );
// Expand the range of the normal value from (0, +1) to (-1, +1).
bumpMap = (2.0f * bumpMap) - 1.0f;
input.n = normalize( input.n );
input.tangent = normalize(input.tangent - dot(input.tangent, input.n)*input.n);
bitangent = cross(input.n, input.tangent);
//Tex space matrix
float3x3 texSpace = float3x3(input.tangent, bitangent, input.n);
//Convert normal from normal map to texture space and store in input.normal
bumpNormal = normalize(mul(bumpMap, texSpace));
input.n = normalize(mul(bumpMap, texSpace));
//lightIntensity = saturate(dot(bumpedNormal, -light.dir));
lightIntensity = saturate(dot(bumpNormal, -light.dir));
//calculate lighting
float4 I = calcBlinnPhongLighting( material, light.color, input.n, -light.dir, input.h );
//with texturing
return lightIntensity * I * textureColor;
//return I * textureColor;
}
//--------------------------------------------------------------------------------------
// Techniques
//--------------------------------------------------------------------------------------
technique10 RENDER_PL_BLINNPHONG
{
pass P0
{
SetVertexShader( CompileShader( vs_4_0, VS_PIXEL_LIGHTING_BLINNPHONG() ) );
SetGeometryShader( NULL );
//SetPixelShader( CompileShader( ps_4_0, PS_PIXEL_LIGHTING_BLINNPHONG() ) );
SetPixelShader( CompileShader( ps_4_0, PS_NORMAL_MAP() ) );
SetRasterizerState( rsSolid );
}

}
Advertisement
Well the first thing that stands out is the assumption in your shader that the bitangent can be calculated from nothing more than tangent and normal.
If I read your shader correctly you're not taking the sign into account. This would indeed lead to such visual errors.

I would expect a pixel shader transformation more along the lines of this one:

http://wiki.blender.org/index.php/Dev:Shading/Tangent_Space_Normal_Maps#Pixel_Shader_Transformation
Thank you for your reply, this concept has helped me gain a better understanding of the process and technical specifics such as this. I'll work this into my shader, hopefully this resolves the issue.
The sign is determined by whether or not the texture coordinates of your triangle are clockwise (CW) or counterclockwise (CCW).
When you build your per vertex tangent spaces, during the averaging process, it is important that you do not accumulate tangent spaces from triangles
that have CW texture coordinates onto vertex tangent spaces that belong to triangles with CCW texture coordinates and vice versa.
As far as tangent space sharing goes the two sets of triangles must remain disjoint which implies you need to split vertices
shared by adjacent triangles where one is CW and the other is CCW.

Alternatively, you can use the tangent space generation implementation available here --> http://wiki.blender.org/index.php/Dev:Shading/Tangent_Space_Normal_Maps
It will return the spaces in an unindexed layout but you can use the basic welder which there is also a link to.
If you use this then you will not have to perform the splits yourself.
I think I am going to go back to calculating the bitangent and tangent in the main code, rather than the shader. The main motivation for me to accomplish what I wanted, was to try and do all my calculations within the shader, but I realise it not possible. So I will do it how I used to and apply this:

tangent[a].w = (Dot(Cross(n, t), tan2[a]) < [color=purple][font=CourierNew]0[/font].0F) ? -[color=purple][font=CourierNew]1[/font].0F : [color=purple][font=CourierNew]1[/font].0F;

to fix this problem I used to have, whereby the tangent would be overlapping, I believe:
image.png

This topic is closed to new replies.

Advertisement