簡體   English   中英

法線沒有正確傳輸到DirectX 11着色器-隨機的,與時間有關的值?

[英]Normals are not transfered to DirectX 11 shader correctly - random, time-dependent values?

今天,我試圖將法線貼圖添加到DirectX 11應用程序中。

出問題了。 我決定在場景對象上輸出法線信息而不是顏色,以“查看”問題所在。 使我驚訝的是法線的值變化非常快 (每幀顏色閃爍)。 而且我確定在程序執行期間不會使用它們的值(頂點的位置保持穩定,但法線不穩定)。

這是t1t2處某些幀的兩個屏幕:

法線輸出

我的頂點結構:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM