1. 程式人生 > >記錄下Directx11中幾個易錯點_C++和HLSL的變數繫結

記錄下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裡面變數繫結的時候要非常小心啊。