繁体   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