記錄下Directx11中幾個易錯點_C++和HLSL的變數繫結
最近遇到幾個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裡面變數繫結的時候要非常小心啊。