将数组作为常量缓冲区传递给着色器时数据全为0,如何正确操作?
数组传递到HLSL常量缓冲区的规范与问题排查
必须遵守的核心规范
- 内存对齐要求:HLSL常量缓冲区所有数据按照16字节(4个float)边界对齐。数组单个元素的大小、整个常量缓冲区的总大小都必须是16字节的整数倍。如果传递自定义结构体,C++侧需要添加
__declspec(align(16))修饰结构体,保证内存布局和HLSL侧完全一致。 - 大小匹配要求:C++侧创建常量缓冲区时指定的字节大小,必须和HLSL中cbuffer定义的总字节大小一致。你示例中96个float4x4的总大小是
96 * 64 = 6144字节,符合16字节对齐要求。 - 插槽匹配要求:C++侧
*SSetConstantBuffers的第一个参数(插槽索引)必须和HLSL中cbuffer绑定的register(bN)的N值完全匹配,且要绑定到对应着色器阶段:顶点着色器用VSSetConstantBuffers,像素着色器用PSSetConstantBuffers,不可混用。 - 数据更新要求:如果需要CPU侧动态更新常量缓冲区,创建时必须指定
Usage = D3D11_USAGE_DYNAMIC、CPUAccessFlags = D3D11_CPU_ACCESS_WRITE,否则Map操作会失败,数据无法写入缓冲区。
针对你代码的排查步骤
- 确认
BoneTransformBuffer创建时的大小是否等于96 * sizeof(XMFLOAT4X4),如果创建时大小不足,填充数据会被截断,未填充区域默认为0。 - 检查
Animator->GetTransforms(_timeDelta)返回的vector<XMFLOAT4X4>的长度是否等于96,如果返回元素数小于96,仅拷贝现有元素的情况下,数组剩余部分会保留初始0值。 - 验证
SetData实现逻辑:是否正确调用ID3D11DeviceContext::Map且返回成功,拷贝数据的长度是否为finalTransforms.size() * sizeof(XMFLOAT4X4),是否将整个vector的内存完整拷贝到了映射后的缓冲区内存中。 - 确认你没有在后续代码中调用
VSSetConstantBuffers(1, 1, &其他缓冲区)覆盖了b1插槽的绑定。 - 使用RenderDoc等渲染调试工具抓帧,直接查看绑定到顶点着色器b1插槽的常量缓冲区实际内容:如果缓冲区内部本身全为0,问题出在C++侧的数据填充逻辑;如果缓冲区数据正确,说明是着色器侧访问越界(比如骨骼索引超过95,HLSL越界访问默认返回0)。
补充说明:XMFLOAT4X4是行优先存储,HLSL默认使用列优先矩阵,若后续出现矩阵计算结果错误而非全0的问题,需要在C++侧转置矩阵后再传递,或者在HLSL中声明矩阵时添加
row_major修饰。
内容的提问来源于stack exchange,提问作者문형준
相关产品推荐
相关产品推荐

