最近遇到几个Directx11的bug,有些最后弄懂了,有些没有,记录下避免下次再犯错。
一个bug是在Directx11推荐用的数学库xnamath,里面的XMCOLOR居然无法和HLSL里面的float4绑定,如果你要用颜色的变量和HLSL的float4绑定,必须得用XMFLOAT4,或者Directx10的数学库里面的D3DXCOLOR都可以。(D3DXVECTOR4没测试过,可能也可以)。
一个bug很弱智,如果不用Effect框架,那么在Vertex shader里面VSSetConstantBuffers绑定了一个constant buffer等,在Pixel shader如果用到,必须用PSSetConstantBuffers再绑定一次。
最后一个是我虽然糊里糊涂debug出来,但却依然最不明白。是C++和HLSL的类里面的变量排列问题。
如果想在Directx和HLSL里面定义一个同样的结构体
在HLSL里面:
struct Light
{
float3 pos;
float3 dir;
float4 ambient;
float4 diffuse;
float4 spec;
float3 att;
float spotPower;
float range;
};
在Directx里面你就必须注意float pad1,和floatpad2.
__declspec(align(16)) struct Light
{
XMFLOAT3 pos;
float pad1;
XMFLOAT3 dir;
float pad2;
XMFLOAT4 ambient;
XMFLOAT4 diffuse;
XMFLOAT4 spec;
XMFLOAT3 att;//表示点光源或聚光灯源的距离影响的因子:a0+a1*d+a2*d*d的三个系数a0,a1,a2。
FLOAT spotPower;//表示在聚光灯源中,偏离的角度对光照的影响因子:max((-LightDir*dir),0)^spotPower
FLOAT range;//表示光照的范围距离
};
为什么会有float pad1,float pad2恩?《Intro to d3d10》里面如下解释:
The preceding effect file has a constant buffer with a Light instance. We would like to be able to set this value with one function call. Therefore, in the C++ code we define a structure very similar to the HLSLLight structure:
struct Light { Light() { ZeroMemory(this, sizeof(Light)); } D3DXVECTOR3 pos; float pad1; // not used D3DXVECTOR3 dir; float pad2; // not used D3DXCOLOR ambient; D3DXCOLOR diffuse; D3DXCOLOR specular; D3DXVECTOR3 att; float spotPow; float range; };
The issue with the “pad” variables is to make the C++ structure match the HLSL structure. In the HLSL, structure padding occurs so that elements are packed into 4D vectors, with the restriction that a single element cannot be split across two 4D vectors. Consider the following example:
struct S { float3 pos; float3 dir; };
If we have to pack the data into 4D vectors, you might think it is done like this:
vector 1: (pos.x, pos.y, pos.z, dir.x) vector 2: (dir.y, dir.z, empty, empty)
However, this splits the element dir across two 4D vectors, which is not allowed — an element is not allowed to straddle a 4D vector boundary. Therefore, it has to be packed like this:
vector 1: (pos.x, pos.y, pos.z, empty) vector 2: (dir.x, dir.y, dir.z, empty)
Thus, the “pad” variables in our C++ structure are able to correspond to those empty slots in the padded HLSL structure (since C++ does not follow the same packing rules as HLSL).
但我不明白的是按照它的说法In the HLSL, structure padding occurs so that elements are packed into 4D vectors, with the restriction that a single element cannot be split across two 4D vectors,这么说我不用float pad1,和float pad2也可以啊。毕竟D3DXVECTOR3 pos和D3DXVECTOR3 dir也会被分在两个4D vector里。但是经试验是不行的,如果没有float pad1,float pad2。变量的绑定就会莫名其妙。
估计依然是C++和HLSL里面变量绑定的问题。在HLSL定义一个constant buffer,在C++定义一个相应的如下:
下面这样子是ok是:
C++:
__declspec(align(16)) struct PerFrameCBuffer
{
INT gLightType;//光的类型
XMFLOAT3 gEyePosW;//眼睛在世界坐标系的位置
Light gLight;//光的结构及变量
};
HLSL:
cbuffer cbPerFrame:register(b0)
{
int gLightType;//光的类型
float3 gEyePosW;//眼睛在世界坐标系的位置
Light gLight;//光的结构及变量
}
但是把几个变量的顺序换下就不ok了,比如把int gLightType这个变量放到结构体最后面,那么在HLSL中就识别不了了。
下面这样子是不ok是:
C++:
__declspec(align(16)) struct PerFrameCBuffer
{
Light gLight;//光的结构及变量
XMFLOAT3 gEyePosW;//眼睛在世界坐标系的位置
INT gLightType;//光的类型
};
HLSL:
cbuffer cbPerFrame:register(b0)
{
Light gLight;//光的结构及变量
float3 gEyePosW;//眼睛在世界坐标系的位置
int gLightType;//光的类型
}
换个这样样子后,在HLSL里面就识别不了gLightType这个变量了,但是gLight还是可以识别。(gEyePosW无法确定)
所以说在用Directx和HLSL里面变量绑定的时候要非常小心啊。