如何将 float3x3 传递给 DirectX 中的 HLSL 着色器?

How to pass float3x3 to HLSL Shader in DirectX?

我在将 3x3 矩阵通过常量缓冲区传递到 DirectX 中的着色器时遇到了问题。这就是我定义常量缓冲区的方式:

在 .cpp 中:

struct PostProcessConvolutionCB {
    float screenWidth;
    float screenHeight;
    float sum;
    XMFLOAT3X3 kernel;
};

在 .hlsl 中:

struct PostProcessConvolutionCB {
    float screenWidth;
    float screenHeight;
    float sum;
    float3x3 kernel;
};
ConstantBuffer<PostProcessConvolutionCB> cb : register(b0);

struct PixelShaderInput {
    float4 Position : SV_Position;
};

float4 main(PixelShaderInput IN) : SV_Target {
    return float4(cb.kernel[1][1], 0.f, 0.f, 1.f);
}

似乎对某些元素的访问一团糟。为了测试这一点,我在常量缓冲区中初始化矩阵,如下所示:XMFLOAT3X3(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f); 并尝试通过在着色器中对矩阵索引进行硬编码来显示每个元素的值,就像上面的 hlsl 代码片段 (cb.kernel[1][1]) 一样。运行 9 次后,我得到以下结果:

kernel[0][0] = 0.1
kernel[1][0] = 0.2
kernel[2][0] = 0.3

kernel[0][1] = 0.5
kernel[1][1] = 0.6
kernel[2][1] = 0.7

kernel[0][2] = 0.9
kernel[1][2] = 1.0
kernel[2][2] = 1.0

似乎每一行都与 4 个浮点数对齐。将矩阵更改为 4x4 有帮助,但我想必须有一种方法可以使用 float3x3 类型。

如何正确处理?

您遇到的问题是 HLSL 打包规则与 C++ 不同。见 Microsoft Docs:

HLSL packing rules are similar to performing a #pragma pack 4 with Visual Studio, which packs data into 4-byte boundaries. Additionally, HLSL packs data so that it does not cross a 16-byte boundary.

另请记住,默认情况下,HLSL 使用 'column-major' 矩阵,而 DirectXMath 使用 'row-major'。这就是为什么您会看到很多样本将矩阵从 XMFLOAT?X? 转置为 HLSL 常量缓冲区结构。参见 Microsoft Docs

通常,您最好的选择是对 HLSL 矩阵使用 XMFLOAT4X4。在 HLSL 中使用一个选项来保存一点常量缓冲内存(对于蒙皮特别有用,特别是当您有许多不包括投影变换的骨骼时):

struct SkinnedEffectConstants
{
…
    XMVECTOR bones[MaxBones][3];
};

然后在 C++ 中使用:

for (size_t i = 0; i < count; i++)
{
    XMMATRIX boneMatrix = XMMatrixTranspose(XMLoadFloat4x3(…));

    boneConstant[i][0] = boneMatrix.r[0];
    boneConstant[i][1] = boneMatrix.r[1];
    boneConstant[i][2] = boneMatrix.r[2];
}