I've been picking up DX11 and I've started working on a per pixel lighting shader. The shader appears to be working, but for whatever reason, my surface normals are getting scrambled.
In running the shader through pix and stepping through it, it seems my surface normal is being set to the vertex position. I've also noticed that the texture coordinate (which is currently unused and set to zero on the input) is also getting set to the vertex position's X and Y coordinates.
I've tried running the code on two different machines, one with an nvidia card that only supports DX10 (gtx 260) and one with an ati chip that supports DX11 (radeon 5450), and I'm seeing the same behavior, so I'm confident it isn't a driver issue.
Here's the relevant shader code:
cbuffer ConstantBuffer : register( b0 )
{
matrix World;
matrix View;
matrix Projection;
float4 vLightDir[2];
float4 vLightColor[2];
float4 vOutputColor;
}
struct VS_INPUT
{
float4 Pos : POSITION;
float2 Tex : TEXTURE;
float4 Col : COLOR;
float4 Norm : NORMAL;
};
struct PS_INPUT
{
float4 Pos : SV_POSITION;
float4 worldPos : WORLD_POSITION;
float2 Tex : TEXTURE;
float4 Col : COLOR;
float4 Norm : NORMAL;
float4 oldNorm : NORMAL1;
};
//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
PS_INPUT VS( VS_INPUT Pos )
{
PS_INPUT outPos = (PS_INPUT)0;
outPos.Pos = mul(Pos.Pos, World);
outPos.worldPos = mul(Pos.Pos, World);
outPos.Pos = mul( outPos.Pos, View);
outPos.Pos = mul( outPos.Pos, Projection);
outPos.Col = Pos.Col;
outPos.Tex = Pos.Tex;
outPos.Norm.xyz = normalize(mul(Pos.Norm.xyz, (float3x3)World));
outPos.Norm.w = 1.0;
outPos.oldNorm = normalize(Pos.Norm);
return outPos;
}
float dPLight(float3 lightPos, float3 pos3D, float3 normal)
{
float3 lightDir = normalize(pos3D - lightPos);
return dot(-lightDir, normal);
}
//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS( PS_INPUT Pos ) : SV_Target
{
float4 newColor = float4(saturate(Pos.oldNorm));
//newColor *= saturate(dPLight(vLightDir[0].xyz, Pos.worldPos.xyz, Pos.Norm));
//newColor += 0.3;
return newColor; // Yellow, with Alpha = 1
}
And here's the C++ snippet i'm using to create the layout:
// Define the input layout
D3D11_INPUT_ELEMENT_DESC layout2[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXTURE", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0},
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0},
};
UINT numElements2 = ARRAYSIZE( layout2 );
// Create the input layout
hr = g_pd3dDevice->CreateInputLayout( layout2, numElements2, p3DSVSBlob->GetBufferPointer(),
p3DSVSBlob->GetBufferSize(), &g_p3DSVertexLayout );
//... Draw call later in the code...
stride = sizeof(Vertex_3DS);
g_pImmediateContext->IASetInputLayout( g_p3DSVertexLayout );
g_pImmediateContext->IASetVertexBuffers(0, 1, &g_pGroundVertexBuffer, &stride, &offset);
g_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
g_pImmediateContext->VSSetShader(g_p3DSVShader, NULL, 0);
g_pImmediateContext->PSSetShader(g_p3DSPShader, NULL,0);
g_pImmediateContext->VSSetConstantBuffers( 0, 1, &g_spConstantBuffer );
g_pImmediateContext->PSSetConstantBuffers(0,1, &g_spConstantBuffer);
g_pImmediateContext->Draw(4,0);
I originally noticed the problem in my 3ds file loader and thought I was calculating surface normals wrong, but I also created a 2 triangle ground quad with a single surface normal pointing straight up and found that it too was getting its surface normals set to the vertex position. I've verified in the local buffers before i call D3D11DEVICE::CreateBuffer, but somewhere along the way things are getting buggered up.
What am I doing wrong?