[英]Adding normal information for the shader in DirectX 11 - normals not visible in shader?
[英]Normals are not transfered to DirectX 11 shader correctly - random, time-dependent values?
今天,我試圖將法線貼圖添加到DirectX 11應用程序中。
出問題了。 我決定在場景對象上輸出法線信息而不是顏色,以“查看”問題所在。 使我驚訝的是法線的值變化非常快 (每幀顏色閃爍)。 而且我確定在程序執行期間不會使用它們的值(頂點的位置保持穩定,但法線不穩定)。
這是t1
和t2
處某些幀的兩個屏幕:
我的頂點結構:
struct MyVertex{//vertex structure
MyVertex() : weightCount(0), normal(0,0,0){
//textureCoordinates.x = 1;
//textureCoordinates.y = 1;
}
MyVertex(float x, float y, float z, float u, float v, float nx, float ny, float nz)
: position(x, y, z), textureCoordinates(u, v), normal(0,0,0), weightCount(0){
}
DirectX::XMFLOAT3 position;
DirectX::XMFLOAT2 textureCoordinates;
DirectX::XMFLOAT3 normal = DirectX::XMFLOAT3(1.0f, 0.0f, 0.0f);
//will not be sent to shader (and used only by skinned models)
int startWeightIndex;
int weightCount; //=0 means that it's not skinned vertex
};
對應的頂點布局:
layout[0] = { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 };
layout[1] = { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 };
layout[2] = { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0 };
頂點緩沖區:
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));
bd.Usage = D3D11_USAGE_DEFAULT; //D3D11_USAGE_DYNAMIC
bd.ByteWidth = sizeof(MyVertex) * structure->getVerticesCount();
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = structure->vertices;
if(device->CreateBuffer(&bd, &InitData, &buffers->vertexBuffer) != S_OK){
return false;
}
輸出法線的着色器“作為顏色”(當然,如果我將output.normal
設置為float3(1,1,1)
,則對象保持白色):
struct Light
{
float3 diffuse;
float3 position;
float3 direction;
};
cbuffer cbPerObject : register(b0)
{
float4x4 WVP;
float4x4 World;
float4 difColor;
bool hasTexture;
bool hasNormMap;
};
cbuffer cbPerFrame : register(b1)
{
Light light;
};
Texture2D ObjTexture;
Texture2D ObjNormMap;
SamplerState ObjSamplerState;
TextureCube SkyMap;
struct VS_INPUT
{
float4 position : POSITION;
float2 tex : TEXCOORD;
float3 normal : NORMAL;
};
struct VS_OUTPUT
{
float4 Pos : SV_POSITION;
float4 worldPos : POSITION;
float3 normal : NORMAL;
float2 TexCoord : TEXCOORD;
float3 tangent : TANGENT;
};
VS_OUTPUT VS(VS_INPUT input)
{
VS_OUTPUT output;
//input.position.w = 1.0f;
output.Pos = mul(input.position, WVP);
output.worldPos = mul(input.position, World);
output.normal = input.normal;
output.tangent = mul(input.tangent, World);
output.TexCoord = input.tex;
return output;
}
float4 PS(VS_OUTPUT input) : SV_TARGET
{
return float4(input.normal, 1.0);
}
//--------------------------------------------------------------------------------------
// Techniques
//--------------------------------------------------------------------------------------
technique10 RENDER
{
pass P0
{
SetVertexShader( CompileShader( vs_4_0, VS() ) );
SetPixelShader( CompileShader( ps_4_0, PS() ) );
SetBlendState( SrcAlphaBlendingAdd, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
}
}
我在哪里弄錯了? 也許代碼中還有其他地方可能導致這種奇怪的行為(某些鎖定,緩沖區,不知道...)?
編輯:
如413X所建議,我已經運行DirectX診斷程序:
奇怪的是,在小型預覽中,屏幕看起來與程序中的相同。 但是,當我調查該幀(截圖)時,我得到了完全不同的顏色:
另外,這有些奇怪-我選擇了藍色像素,說它是黑色的(右側):
編輯2:
根據catflier的要求,我發布了一些其他代碼。
渲染和緩沖區綁定:
//set the object world matrix
DirectX::XMMATRIX objectWorldMatrix = DirectX::XMMatrixIdentity();
DirectX::XMMATRIX rotationMatrix = DirectX::XMMatrixRotationQuaternion(
DirectX::XMVectorSet(object->getOrientation().getX(), object->getOrientation().getY(), object->getOrientation().getZ(), object->getOrientation().getW())
);
irectX::XMMATRIX scaleMatrix = (
object->usesScaleMatrix()
? DirectX::XMMatrixScaling(object->getHalfSize().getX(), object->getHalfSize().getY(), object->getHalfSize().getZ())
: DirectX::XMMatrixScaling(1.0f, 1.0f, 1.0f)
);
DirectX::XMMATRIX translationMatrix = DirectX::XMMatrixTranslation(object->getPosition().getX(), object->getPosition().getY(), object->getPosition().getZ());
objectWorldMatrix = scaleMatrix * rotationMatrix * translationMatrix;
UINT stride = sizeof(MyVertex);
UINT offset = 0;
context->IASetVertexBuffers(0, 1, &buffers->vertexBuffer, &stride, &offset); //set vertex buffer
context->IASetIndexBuffer(buffers->indexBuffer, DXGI_FORMAT_R16_UINT, 0); //set index buffer
//set the constants per object
ConstantBufferStructure constantsPerObject;
//set matrices
DirectX::XMFLOAT4X4 view = myCamera->getView();
DirectX::XMMATRIX camView = XMLoadFloat4x4(&view);
DirectX::XMFLOAT4X4 projection = myCamera->getProjection();
DirectX::XMMATRIX camProjection = XMLoadFloat4x4(&projection);
DirectX::XMMATRIX worldViewProjectionMatrix = objectWorldMatrix * camView * camProjection;
constantsPerObject.worldViewProjection = XMMatrixTranspose(worldViewProjectionMatrix);
constantsPerObject.world = XMMatrixTranspose(objectWorldMatrix);
//draw objects's non-transparent subsets
for(int i=0; i<structure->subsets.size(); i++){
setColorsAndTextures(structure->subsets[i], constantsPerObject, context); //custom method that insert data into constantsPerObject variable
//bind constants per object to constant buffer and send it to vertex and pixel shaders
context->UpdateSubresource(constantBuffer, 0, NULL, &constantsPerObject, 0, 0);
context->VSSetConstantBuffers(0, 1, &constantBuffer);
context->PSSetConstantBuffers(0, 1, &constantBuffer);
context->RSSetState(RSCullDefault);
int start = structure->subsets[i]->getVertexIndexStart();
int count = structure->subsets[i]->getVertexIndexAmmount();
context->DrawIndexed(count, start, 0);
}
光柵化器:
void RendererDX::initCull(ID3D11Device * device){
D3D11_RASTERIZER_DESC cmdesc;
ZeroMemory(&cmdesc, sizeof(D3D11_RASTERIZER_DESC));
cmdesc.FillMode = D3D11_FILL_SOLID;
cmdesc.CullMode = D3D11_CULL_BACK;
#ifdef GRAPHIC_LEFT_HANDED
cmdesc.FrontCounterClockwise = true;
#else
cmdesc.FrontCounterClockwise = false;
#endif
cmdesc.CullMode = D3D11_CULL_NONE;
//cmdesc.FillMode = D3D11_FILL_WIREFRAME;
HRESULT hr = device->CreateRasterizerState(&cmdesc, &RSCullDefault);
}
編輯3:
調試器輸出(語義上有一些不匹配嗎?):
D3D11錯誤:ID3D11DeviceContext :: DrawIndexed:輸入匯編程序-頂點着色器鏈接錯誤:階段之間的簽名不兼容。 輸入級需要語義/索引(NORMAL,0)作為輸入,但輸出級不提供。 [執行錯誤#342:DEVICE_SHADER_LINKAGE_SEMANTICNAME_NOT_FOUND]
D3D11錯誤:ID3D11DeviceContext :: DrawIndexed:輸入匯編程序-頂點着色器鏈接錯誤:階段之間的簽名不兼容。 為輸出級和輸入級之間的硬件寄存器不匹配定義了語義“ TEXCOORD”。 [執行錯誤#343:DEVICE_SHADER_LINKAGE_REGISTERINDEX]
D3D11錯誤:ID3D11DeviceContext :: DrawIndexed:輸入匯編程序-頂點着色器鏈接錯誤:階段之間的簽名不兼容。 當每個簽名的語義“ TEXCOORD”必須相同時,它們具有不同的最低精度級別。 [執行錯誤#3146050:DEVICE_SHADER_LINKAGE_MINPRECISION]
我很確定您的字節未對齊。 我認為float是4個字節,然后float4是16個字節。 它希望是16字節對齊的。 因此請注意:
layout[0] = { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 };
layout[1] = { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 };
layout[2] = { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0 };
價值; 0、12、20。 (AlignedByteOffset)是值開始的位置。 那意味着什么; 位置從0開始。Texcoord在float3的末尾開始,這會給您錯誤的結果。 因為在着色器內部查看:
struct VS_INPUT
{
float4 position : POSITION;
float2 tex : TEXCOORD;
float3 normal : NORMAL;
};
並在float3 + float2處為Normal。 因此,通常來說,您想使內容更一致。 甚至“填充”來填充空格,以使所有變量保持16個字節對齊。
但是為了您更簡單。 您想要將該語句切換為:
layout[0] = { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 };
layout[1] = { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 };
layout[2] = { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 };
現在會發生什么? 好吧,事物會自動對齊,但是可能會不太理想。 但是關於着色器的一件事,請嘗試使其保持16字節對齊。
您上傳的數據結構與您的輸入布局聲明不匹配。
因為您的頂點數據結構是:
struct MyVertex{//vertex structure
MyVertex() : weightCount(0), normal(0,0,0){
//textureCoordinates.x = 1;
//textureCoordinates.y = 1;
}
MyVertex(float x, float y, float z, float u, float v, float nx, float ny, float nz)
: position(x, y, z), textureCoordinates(u, v), normal(0,0,0), weightCount(0){
}
DirectX::XMFLOAT3 position;
DirectX::XMFLOAT2 textureCoordinates;
DirectX::XMFLOAT3 normal = DirectX::XMFLOAT3(1.0f, 0.0f, 0.0f);
//will not be sent to shader (and used only by skinned models)
int startWeightIndex;
int weightCount; //=0 means that it's not skinned vertex
};
startWeightIndex和weightCount將被復制到您的頂點緩沖區中(即使它們不包含任何有用的內容)。
如果選中sizeof(MyVertex),則大小為40。
現在,讓我們看一下您的輸入布局聲明(是否使用自動偏移量無關緊要):
layout[0] = { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 };
layout[1] = { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 };
layout[2] = { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0 };
從這里看到的內容,您正在聲明(12 + 8 + 12)= 32字節的數據結構,這當然與您的頂點大小不匹配。
因此,第一個頂點將被正確提取,但是下一個頂點將開始使用無效數據(因為輸入匯編器不知道您的數據結構大於您為其指定的數據結構)。
修復它的兩種方法:
1 /去除頂點聲明
在那種情況下,您可以修改頂點結構以匹配輸入聲明(為簡潔起見,我刪除了構造函數:
struct MyVertex
{//vertex structure
DirectX::XMFLOAT3 position;
DirectX::XMFLOAT2 textureCoordinates;
DirectX::XMFLOAT3 normal = DirectX::XMFLOAT3(1.0f, 0.0f, 0.0f);
};
現在,您的頂點結構與您的聲明完全匹配,因此可以正確獲取頂點。
2 /調整您的輸入布局聲明:
在這種情況下,您可以更改布局以確保聲明了緩沖區中包含的所有數據,以便輸入匯編程序可以將其考慮在內(請參見下文)
現在,您的聲明變為:
layout[0] = { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 };
layout[1] = { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 };
layout[2] = { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0 };
layout[3] = { "STARTWEIGHTINDEX", 0, DXGI_FORMAT_R32_SINT, 0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0 };
layout[4] = { "WEIGHTCOUNT", 0, DXGI_FORMAT_R32_SINT, 0, 36, D3D11_INPUT_PER_VERTEX_DATA, 0 };
因此,這意味着您將結構包含的所有數據通知給輸入匯編器。
在這種情況下,即使您的頂點着色器不需要數據(如您指定的完整數據聲明),Input匯編程序也將安全地忽略STARTWEIGHTINDEX和WEIGHTCOUNT,但會尊重您的整個結構填充。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.