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

Why does the Constant Buffer update partially in Direct11

我正在努力 "Introduction to 3D Game Programming with Directx 11"。我正在修改示例,以便不使用 Effects Framework,到目前为止一切正常。

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

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

像素着色器:

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。有什么想法吗?

感谢您的帮助。

好像是打包的问题。根据 Packing Rules for Constant Variables 数据应在 4 字节边界处打包,但数据块也不会跨越 16 字节边界。在这种情况下,EyePositionW:

之后肯定会有一个填充
struct CBPerFrame
{
    DirectionalLight  DirLight[3];
    DirectX::XMFLOAT3 EyePositionW;
    float             padding1;
    DirectX::XMFLOAT4 FogColour;

我也不知道为什么最后会有double gPadding;。应该是另一个 int.