Hi,
I will try to be short and only provide clear information about my shader problem, it's probably a newbie error, but I have spend my day on it without solution...
On my small 3D engine, it seem that I have a problem rendering UpY normal with my shader.
Let me explain :
- I load a FBX containing a plane composed of 2 triangles, the plane is correctly shaded.
- I generate the same plane, the plane is all black. PixelShader return black on every plane pixel.
The only difference I see it's that FBX file are loaded with up side on the Z axis (so vertex normals are (0, 0, 1)) and then I rotate them with a rotation matrix. When my plane is manually generated with Y as up axis, my normal are (0, 1, 0).
If I generate my plane like in the FBX file, it render correctly.
If I change the normal to something like (0, 1, 1), it render correctly.
If I load complex mesh, like a Teapot or so, they render correctly (probably cause there is no vertex with straigh Y normal).
It really seem that the normal (0, 1, 0) is the problem. If I load a FBX cube, 3 face are all black too.
I am still beginner and haven't totally writed the HLSL file by myself, I have put together snipped from here and there. So I am not able to find wah't wrong with it, maybe somone can help me ?
Here is the files :
Common.HLSL
struct ParallelLight
{
float3 Direction;
float4 AmbientColor;
float4 DiffuseColor;
float4 SpecularColor;
};
// PerObject constant buffer
cbuffer cbPerObject : register(b0)
{
// WorldViewProjection matrix
float4x4 WorldViewProjection;
// We need the world matrix so that we can
// calculate the lighting in world space
float4x4 World;
// Inverse transpose of world, used for
// bringing normals into world space, especially
// necessary where non-uniform scaling has been applied
float4x4 WorldInverseTranspose;
// Matrix to take world coordinates to view/projection
// Used in the domain shader
float4x4 ViewProjection;
// Matrix to take a world position back to local
float4x4 WorldInverse;
};
cbuffer cbPerFrame : register(b1)
{
ParallelLight ParallelLightArray[10];
//PointLight gPointLight[10];
//SpotLight gSpotLight[10];
float3 CameraPosition;
};
cbuffer cbPerMaterial : register (b2)
{
float4 MaterialAmbient;
float4 MaterialDiffuse;
float4 MaterialSpecular;
float4 MaterialEmissive;
float MaterialSpecularPower;
bool HasTexture;
float4x4 UVTransform;
};
struct VS_IN
{
float4 Position : POSITION;
float3 Normal : NORMAL;
float4 Tangent : TANGENT;
float4 Color : COLOR0;
float2 TextureUV : TEXCOORD0;
};
struct VS_OUT
{
float4 Position : SV_Position;
// Interpolation of combined vertex and material diffuse
float4 Diffuse : COLOR;
// Interpolation of vertex UV texture coordinate
float2 TextureUV: TEXCOORD0;
// We need the WorldNormal for displacement etc..
float3 WorldNormal : NORMAL;
float3 WorldPosition : WORLDPOS;
// To camera vector in tangent space
float3 ToCameraTangentSpace : TEXCOORD3;
// Tangent/Surface space normal for tangent space calculations
float3 Normal: TEXCOORD5;
// To light vector in tangent space
float3 ToLightTangentSpace: TEXCOORD6;
};
// Create a TBN matrix for tangent operations
float3x3 CreateTBNMatrix(float3 normal, float4 tangent)
{
// Ensure tangent is orthogonal to normal vector - Gram-Schmidt orthogonalize
float3 T = tangent.xyz;
T = normalize(T - normal * dot(normal, T));
// Create the Bitangent (tangent.w contains handedness)
float3 bitangent = cross(normal, T) * tangent.w;
// Create the TBN matrix
return float3x3(T, bitangent, normal);
}
VertexShader.HLSL
#include "Common.hlsl"
VS_OUT VS(VS_IN vIn)
{
VS_OUT result = (VS_OUT)0;
// Change the position vector to be 4 units for matrix transformation
vIn.Position.w = 1.0;
result.Position = mul(vIn.Position, WorldViewProjection);
result.Diffuse = vIn.Color * MaterialDiffuse;
// Apply material UV transformation
result.TextureUV = mul(float4(vIn.TextureUV.x, vIn.TextureUV.y, 0, 1), (float4x2)UVTransform).xy;
// We use the inverse transpose of the world so that if there is non uniform
// scaling the normal is transformed correctly. We also use a 3x3 so that
// the normal is not affected by translation (i.e. a vector has the same direction
// and magnitude regardless of translation)
result.WorldNormal = mul(vIn.Normal, (float3x3)WorldInverseTranspose);
result.Normal = vIn.Normal;
result.WorldPosition = mul(vIn.Position, World).xyz;
// Create TBN matrix - note: the transpose of TBN is the equivalent of inverse because it is orthogonal
float3x3 worldToTangent = mul((float3x3)WorldInverse, transpose(CreateTBNMatrix(result.Normal, vIn.Tangent)));
// Calculate ToCamera/ToLight
result.ToCameraTangentSpace = normalize(mul(CameraPosition - result.WorldPosition, worldToTangent));
result.ToLightTangentSpace = normalize(mul(-ParallelLightArray[0].Direction, worldToTangent));
return result;
}
PixelShader.HLSL
#include "Common.hlsl"
Texture2D Texture0 : register(t0);
SamplerState Sampler : register(s0);
//
// combines a float3 RGB value with an alpha value into a float4
//
float4 CombineRGBWithAlpha(float3 rgb, float a)
{
return float4(rgb.r, rgb.g, rgb.b, a);
}
//
// lambert lighting function
//
float3 LambertLighting(
float3 lightNormal,
float3 surfaceNormal,
float3 materialAmbient,
float3 lightAmbient,
float3 lightColor,
float3 pixelColor
)
{
// compute amount of contribution per light
float diffuseAmount = saturate(dot(lightNormal, surfaceNormal));
float3 diffuse = diffuseAmount * lightColor * pixelColor;
// combine ambient with diffuse
return saturate((materialAmbient * lightAmbient) + diffuse);
}
//
// specular contribution function
//
float3 SpecularContribution(
float3 toEye,
float3 lightNormal,
float3 surfaceNormal,
float3 materialSpecularColor,
float materialSpecularPower,
float lightSpecularIntensity,
float3 lightColor
)
{
// compute specular contribution
float3 vHalf = normalize(lightNormal + toEye);
float specularAmount = saturate(dot(surfaceNormal, vHalf));
specularAmount = pow(specularAmount, max(materialSpecularPower, 0.0001f)) * lightSpecularIntensity;
float3 specular = materialSpecularColor * lightColor * specularAmount;
return specular;
}
float4 PS(VS_OUT pIn) : SV_Target
{
// we need to normalize incoming vectors
float3 normal = normalize(pIn.Normal);
float3 toEye = normalize(pIn.ToCameraTangentSpace);
float3 toLight = normalize(pIn.ToLightTangentSpace);
// BEGIN GENERATED CODE
float3 local0 = SpecularContribution(toEye, toLight, normal, MaterialSpecular.rgb, MaterialSpecularPower, 8, ParallelLightArray[0].DiffuseColor.rgb);
float3 local2 = LambertLighting(toLight, normal, MaterialAmbient.rgb, ParallelLightArray[0].AmbientColor.rgb, ParallelLightArray[0].DiffuseColor.rgb, pIn.Diffuse.rgb);
float3 local3 = local0 + local2;
float4 fragment = CombineRGBWithAlpha(local3, pIn.Diffuse.a);
// END GENERATED CODE
if (fragment.a == 0.0f) discard;
return fragment;
}