简体   繁体   English

法线没有正确传输到DirectX 11着色器-随机的,与时间有关的值?

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

Today I was trying to add normal maps to my DirectX 11 application. 今天,我试图将法线贴图添加到DirectX 11应用程序中。

Something went wrong. 出问题了。 I've decided to output the normals' information instead of color on scene objects to "see" where lies the problem. 我决定在场景对象上输出法线信息而不是颜色,以“查看”问题所在。 What surprised me is that the normals' values changes very fast (the colors are blinking each frame). 使我惊讶的是法线的值变化非常快 (每帧颜色闪烁)。 And I'm sure that I don't manipulate with their values during program execution (the position of vertices stays stable, but the normals do not). 而且我确定在程序执行期间不会使用它们的值(顶点的位置保持稳定,但法线不稳定)。

Here are two screens for some frames at t1 and t2 : 这是t1t2处某些帧的两个屏幕:

法线输出

My vertex structure: 我的顶点结构:

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
};

The corresponding vertex layout: 对应的顶点布局:

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 };

Vertex buffer: 顶点缓冲区:

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;
}

And the shader that output normals "as color" (of course, if I set output.normal to float3(1,1,1) , objects stays white): 输出法线的着色器“作为颜色”(当然,如果我将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 );
    }    
}

Where have I made an mistake? 我在哪里弄错了? Maybe there are other places in code that can cause that strange behavior (some locking, buffers, dunno...)? 也许代码中还有其他地方可能导致这种奇怪的行为(某些锁定,缓冲区,不知道...)?


edit: 编辑:

As 413X suggested, I've run the DirectX Diagnostic: 413X所建议,我已经运行DirectX诊断程序:

在此处输入图片说明

What is strange that on the small preview, the screen looks the same as in program. 奇怪的是,在小型预览中,屏幕看起来与程序中的相同。 But when I investigate that frame (screenshot), I got completely different colors: 但是,当我调查该帧(截图)时,我得到了完全不同的颜色:

在此处输入图片说明

Also, here's something strange - I pick the blue pixel and it's says it's black (on the right): 另外,这有些奇怪-我选择了蓝色像素,说它是黑色的(右侧):

在此处输入图片说明


edit 2: 编辑2:

As catflier requested I post some additional code. 根据catflier的要求,我发布了一些其他代码。

The rendering and buffers binding: 渲染和缓冲区绑定:

//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);
}

The rasterizer: 光栅化器:

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);
}

edit 3: 编辑3:

The debugger output (there are some mismatches in semantics?): 调试器输出(语义上有一些不匹配吗?):

D3D11 ERROR: ID3D11DeviceContext::DrawIndexed: Input Assembler - Vertex Shader linkage error: Signatures between stages are incompatible. D3D11错误:ID3D11DeviceContext :: DrawIndexed:输入汇编程序-顶点着色器链接错误:阶段之间的签名不兼容。 The input stage requires Semantic/Index (NORMAL,0) as input, but it is not provided by the output stage. 输入级需要语义/索引(NORMAL,0)作为输入,但输出级不提供。 [ EXECUTION ERROR #342: DEVICE_SHADER_LINKAGE_SEMANTICNAME_NOT_FOUND] [执行错误#342:DEVICE_SHADER_LINKAGE_SEMANTICNAME_NOT_FOUND]

D3D11 ERROR: ID3D11DeviceContext::DrawIndexed: Input Assembler - Vertex Shader linkage error: Signatures between stages are incompatible. D3D11错误:ID3D11DeviceContext :: DrawIndexed:输入汇编程序-顶点着色器链接错误:阶段之间的签名不兼容。 Semantic 'TEXCOORD' is defined for mismatched hardware registers between the output stage and input stage. 为输出级和输入级之间的硬件寄存器不匹配定义了语义“ TEXCOORD”。 [ EXECUTION ERROR #343: DEVICE_SHADER_LINKAGE_REGISTERINDEX] [执行错误#343:DEVICE_SHADER_LINKAGE_REGISTERINDEX]

D3D11 ERROR: ID3D11DeviceContext::DrawIndexed: Input Assembler - Vertex Shader linkage error: Signatures between stages are incompatible. D3D11错误:ID3D11DeviceContext :: DrawIndexed:输入汇编程序-顶点着色器链接错误:阶段之间的签名不兼容。 Semantic 'TEXCOORD' in each signature have different min precision levels, when they must bet identical. 当每个签名的语义“ TEXCOORD”必须相同时,它们具有不同的最低精度级别。 [ EXECUTION ERROR #3146050: DEVICE_SHADER_LINKAGE_MINPRECISION] [执行错误#3146050:DEVICE_SHADER_LINKAGE_MINPRECISION]

I am pretty sure your bytes are missaligned. 我很确定您的字节未对齐。 A float is 4 bytes me thinks and a float4 is then 16 bytes. 我认为float是4个字节,然后float4是16个字节。 And it wants to be 16 byte aligned. 它希望是16字节对齐的。 So observe: 因此请注意:

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 };

The value; 价值; 0,12,20. 0、12、20。 (AlignedByteOffset) Is where the value then starts. (AlignedByteOffset)是值开始的位置。 Which would mean; 那意味着什么; Position starts at 0. Texcoord starts at the end of a float3, which gives you wrong results. 位置从0开始。Texcoord在float3的末尾开始,这会给您错误的结果。 Because look inside the shader: 因为在着色器内部查看:

struct VS_INPUT
{
    float4 position : POSITION;
    float2 tex : TEXCOORD;
    float3 normal : NORMAL;
};

And Normal at float3+float2. 并在float3 + float2处为Normal。 So generally, you want to align things more consistantly. 因此,通常来说,您想使内容更一致。 Maybe even "padding" to fill the spaces to keep all the variables at 16 bytes aligned. 甚至“填充”来填充空格,以使所有变量保持16个字节对齐。

But to keep it more simple for you. 但是为了您更简单。 You want to switch that statement to: 您想要将该语句切换为:

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 };

What happens now? 现在会发生什么? Well, the thing aligns itself automagically, however it can be less optimal. 好吧,事物会自动对齐,但是可能会不太理想。 But one thing about shaders, try to keep it 16 byte aligned. 但是关于着色器的一件事,请尝试使其保持16字节对齐。

Your data structure on upload doesn't match your Input Layout declaration. 您上传的数据结构与您的输入布局声明不匹配。

since your data structure for vertex is : 因为您的顶点数据结构是:

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 and weightCount will be copied into your vertex buffer (even if they do not contain anything useful. startWeightIndex和weightCount将被复制到您的顶点缓冲区中(即使它们不包含任何有用的内容)。

If you check sizeof(MyVertex), you will have a size of 40. 如果选中sizeof(MyVertex),则大小为40。

Now let's look at your input layout declaration (whether you use automatic offset or not is irrelevant): 现在,让我们看一下您的输入布局声明(是否使用自动偏移量无关紧要):

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 };

From what you see here, you are declaring a data structure of (12+8+12) = 32 bytes, which of course does not match your vertex size. 从这里看到的内容,您正在声明(12 + 8 + 12)= 32字节的数据结构,这当然与您的顶点大小不匹配。

So first vertex will be fetched properly, but next ones will start to use invalid data (as the Input Assembler doesn't know that your data structure is bigger than what you specified to it). 因此,第一个顶点将被正确提取,但是下一个顶点将开始使用无效数据(因为输入汇编器不知道您的数据结构大于您为其指定的数据结构)。

Two ways to fix it: 修复它的两种方法:

1/ Strip your vertex declaration 1 /去除顶点声明

In that case you modify your vertex structure to match your input declaration (I removed constructors for brevity: 在那种情况下,您可以修改顶点结构以匹配输入声明(为简洁起见,我删除了构造函数:

struct MyVertex
{//vertex structure
    DirectX::XMFLOAT3 position;
    DirectX::XMFLOAT2 textureCoordinates;
    DirectX::XMFLOAT3 normal = DirectX::XMFLOAT3(1.0f, 0.0f, 0.0f);
};

Now your vertex structure exactly matches your declaration, so vertices will be fetched properly. 现在,您的顶点结构与您的声明完全匹配,因此可以正确获取顶点。

2/Adapt your Input Layout declaration: 2 /调整您的输入布局声明:

In that case you change your layout to make sure that all data contained in your buffer is declared, so it can be taken into account by the Input Assembler (see below) 在这种情况下,您可以更改布局以确保声明了缓冲区中包含的所有数据,以便输入汇编程序可以将其考虑在内(请参见下文)

Now your declaration becomes: 现在,您的声明变为:

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 };

So that means you inform the Input assembler of all the data that your structure contains. 因此,这意味着您将结构包含的所有数据通知给输入汇编器。

In that case even if the data is not needed by your Vertex Shader, as you specified a full data declaration, Input assembler will safely ignore STARTWEIGHTINDEX and WEIGHTCOUNT, but will respect your whole structure padding. 在这种情况下,即使您的顶点着色器不需要数据(如您指定的完整数据声明),Input汇编程序也将安全地忽略STARTWEIGHTINDEX和WEIGHTCOUNT,但会尊重您的整个结构填充。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM