首页 > 解决方案 > 如何在 DirectX 中将 float3x3 传递给 HLSL 着色器?

问题描述

我在将 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 类型。

如何正确处理?

标签: matrixdirectxhlsl

解决方案


您遇到的问题是用于打包的 HLSL 规则与 C++ 不同。请参阅Microsoft 文档

HLSL 打包规则类似于#pragma pack 4使用 Visual Studio 执行,后者将数据打包到 4 字节边界中。此外,HLSL 对数据进行打包,使其不会跨越 16 字节边界。

还要记住,默认情况下,HLSL 使用“列优先”矩阵,而 DirectXMath 使用“行优先”。这就是为什么您会看到大量将矩阵转置为XMFLOAT?X?HLSL 常量缓冲区结构的样本的原因。请参阅Microsoft 文档

通常,您最好的选择是XMFLOAT4X4用于 HLSL 矩阵。保存少量常量缓冲内存的一种选择(对于蒙皮特别有用,尤其是在您有许多不包括投影变换的骨骼的情况下)是在 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];
}

推荐阅读