[DX 11] Scrambled vertex normals

Started by
2 comments, last by magicstix 12 years, 4 months ago
Hi all,
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?
Advertisement
You're not setting the element offset in your input layout description, which is the 5th member of the D3D11_INPUT_ELEMENT_DESC structure. This offset tells the input assembler how many bytes there are in each vertex before that particular element. So your TEXTURE element should have an offset of 12, since the POSITION element before it has 3 floats (and each float is 4 bytes). If you don't want to set the offset manually, you can set them all to D3D11_APPEND_ALIGNED_ELEMENT and it will work as long as the order if your input elements matches the order of the data in the actual vertex buffer.
Hi,

as an addition to the previous post :

[color=#000000]outPos[color=#666600].[color=#000000]oldNorm [color=#666600]=[color=#000000] normalize[color=#666600]([color=#660066]Pos[color=#666600].[color=#660066]Norm[color=#666600]);

OldNorm and Norm are typed as float4 and you set Pos.Norm.w to 1.0, so normalizing float4 will result incorrect normal vector. You can use float3 for your normal vectors.

Cheers!

You're not setting the element offset in your input layout description, which is the 5th member of the D3D11_INPUT_ELEMENT_DESC structure. This offset tells the input assembler how many bytes there are in each vertex before that particular element. So your TEXTURE element should have an offset of 12, since the POSITION element before it has 3 floats (and each float is 4 bytes). If you don't want to set the offset manually, you can set them all to D3D11_APPEND_ALIGNED_ELEMENT and it will work as long as the order if your input elements matches the order of the data in the actual vertex buffer.


Thanks! You win many internets. I should've caught that when I was comparing my layout to the d3d tutorials.... Copypasta syndrome, I guess. :/

This topic is closed to new replies.

Advertisement