为什么常量缓冲区部分更新在Direct11中

时间:2017-11-13 21:12:11

标签: c++ winapi directx directx-11

通过"使用Directx 11"进行3D游戏编程简介。我正在重新处理样本以便不使用效果框架,到目前为止一切都很好。

但是,我提出了一个问题,其中一个常量缓冲区仅部分更新。

CPU端CB结构:

struct CBPerFrame
{
    DirectionalLight  DirLight[3];
    DirectX::XMFLOAT3 EyePositionW;

    DirectX::XMFLOAT4 FogColour;

    float             FogStart;
    float             FogRange;

    int               LightNumber;
    double            Padding;
};

我在任何绘图之前更新CB。

CBPerFrame cbPerFrame { };

    cbPerFrame.DirLight[ 0 ] = mDirectionalLights[ 0 ];
    cbPerFrame.DirLight[ 1 ] = mDirectionalLights[ 1 ];
    cbPerFrame.DirLight[ 2 ] = mDirectionalLights[ 2 ];

    cbPerFrame.EyePositionW  = mEyePosW;

    cbPerFrame.FogColour     = XMFLOAT4( Colors::Black );
    cbPerFrame.FogRange      = 1.0F;
    cbPerFrame.FogStart      = 0.0F;

    cbPerFrame.LightNumber   = mLightCount;

    cbPerFrame.Padding       = 0.0;

mD3DImmediateContext->UpdateSubresource( mCBPerFrame.Get( ), 0, nullptr, &cbPerFrame, 0, 0 );

Pixel Shader:

cbuffer CBPerFrame : register( b0 )
{
    DirectionalLight gDirectionalLights[ 3 ];
    float3           gEyePosW;

    float4           gFogColor;
    float            gFogStart;
    float            gFogRange;

    int              gLightCount;
    double           gPadding;
}

cbuffer CBPerObject : register( b1 )
{
    matrix   gWorld;
    matrix   gWorldInverseTranspose;
    matrix   gWorldViewProjection;
    float4x4 gTextureTransform;
    Material gMaterial;
}

float4 main( VertexOut input ) : SV_TARGET
{
    // Interpolating normal can unnormalize it, so normalize it.
    input.NormalW = normalize( input.NormalW );

    // The toEye vector is used in lighting.
    float3 toEye = normalize( gEyePosW - input.PositionW );

    // Cache the distance to the eye from this surface point.
    float distToEye = length( toEye );

    // Normalize.
    toEye /= distToEye;

    //
    // Lighting.
    //

    // Start with a sum of zero. 
    float4 ambient = float4( 0.0F, 0.0F, 0.0F, 0.0F );
    float4 diffuse = float4( 0.0F, 0.0F, 0.0F, 0.0F );
    float4 spec    = float4( 0.0F, 0.0F, 0.0F, 0.0F );

    // Sum the light contribution from each light source.  
   /* [unroll]*/
    for ( int i = 0; i < gLightCount; i++ )
    {
        float4 A, D, S;

        ComputeDirectionalLight( gMaterial, gDirectionalLights[ i ], input.NormalW, toEye, A, D, S );

        ambient += A;
        diffuse += D;
        spec    += S;
    }

    float4 litColour = ambient + diffuse + spec;

    // Common to take alpha from diffuse material.
    litColour.a = gMaterial.Diffuse.a;

    return litColour;
}

gLightCount始终设置为0,即使应在应用程序启动时将其设置为2。如果我将循环的条件更改为硬编码的1/2/3,则着色器可以正常工作。

我意识到CB中有额外的变量,但示例代码有这个,我相信它会在其他示例中使用。

我认为问题在于如何填充CBPerFrame结构,因此它没有被正确地复制到GPU。有什么想法吗?

感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

这似乎是包装的问题。根据{{​​3}},数据应以4字节边界打包,但数据块也不能跨越16字节边界。在这种情况下,EyePositionW之后肯定会有填充:

struct CBPerFrame
{
    DirectionalLight  DirLight[3];
    DirectX::XMFLOAT3 EyePositionW;
    float             padding1;
    DirectX::XMFLOAT4 FogColour;

此外,我不确定为什么最后有double gPadding;。它可能应该是另一个int