DirectX Alpha顏色運算

Game Programming with DirectX -- 12[紅+綠+藍+Alpha]

第十二集 紅+綠+藍+Alpha

 

紅綠藍組成了絢爛的顏色世界, 當紅綠藍遇到Alpha後, 這個顏色世界又多了一份神祕.

 

神祕的Alpha, 我們可以簡單的認爲它和紅, 綠, 藍一樣, 是組成色彩的一種屬性, 用來表示透明度.

 

Directx Graphics 中的色彩一般使用 "RGB" 格式表示, RGB格式有三種原色 --- red(), green(), blue(), 其他顏色都是通過三種原色的不同比例混合來表示.

 

RGB格式現在最常見存儲方式是每種原色一個字節 --- 決定了現在的原色表示範圍只有[0, 255], RGB共三個字節(24位); 如再加上一個字節的Alpha值, 有四個字節(32位);

 

 

12.1 色彩

 

12.1.1 D3DCOLOR

 

    32 位的RGB格式剛好是一個DWORD --- D3DCOLOR, 通常的順序如圖12.1,

 

                   |<------------------ 32 bits ------------------>|

                   +-----------+-----------+-----------+-----------+

                   |   alpha   |   red     |   green   |   blue    |

                   +-----------+-----------+-----------+-----------+

                  high bit                                     low bit

12.1

 

DirectX Graphics 中爲我們提供生成這個DWORDMacro,

#define D3DCOLOR_ARGB(a,r,g,b) /

    ((D3DCOLOR)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)))

#define D3DCOLOR_RGBA(r,g,b,a) D3DCOLOR_ARGB(a,r,g,b)

#define D3DCOLOR_XRGB(r,g,b)   D3DCOLOR_ARGB(0xff,r,g,b)

 

#define D3DCOLOR_XYUV(y,u,v)   D3DCOLOR_ARGB(0xff,y,u,v)

#define D3DCOLOR_AYUV(a,y,u,v) D3DCOLOR_ARGB(a,y,u,v)

還有一個特殊的單位化表示的D3DCOLOR_COLORVALUE, DirectX Graphics把顏色基本運算合併在一個結構D3DXCOLOR簡單實用.

 

12.1.2 Gamma校正

 

    Gamma 校正是全屏的 現在的顯卡都支持, 一般 Gamma 校正的用途是調整屏幕顯示的明暗度, 同樣, Gamma 校正可以實現一些特殊效果, 如第一視覺的RPG中, 男主人公被敵人偷襲, 眼前景物漸漸變成血紅色  , 男主人公從昏睡中醒過來, 眼前從黑黑一片到看到美麗的女主人公, 精彩的故事由此開始  .

 

    DirectX Graphics中, 色彩到屏幕輸出通過Gamma梯度映射校正, 如圖12.2

12.2


圖中梯度映射校正最大爲65535, 表示梯度使用WORD表示. 每種原色都有各自的梯度映射校正, 默認的三原色梯度映射校正都是相同的梯度斜率1, 例如對於RGB(0, 128, 255)進行校正, 如圖12.3


圖12.3


  [梯度映射校正]--> 0     [梯度到原色] --> 0   [最後輸出顏色的RED分量]

128 [梯度映射校正]--> 32896 [梯度到原色] --> 128 [最後輸出顏色的GREEN分量]

255 [梯度映射校正]--> 65535 [梯度到原色] --> 255 [最後輸出顏色的BLUE分量]

 

現在我們改變GREEN分量的 梯度, 使其斜率爲2, 如圖12.4, 則有


12.4


0   [梯度映射校正]--> 0     [梯度到原色] --> 0   [最後輸出顏色的RED分量]

128 [梯度映射校正]--> 65535 [梯度到原色] --> 255 [最後輸出顏色的GREEN分量]

255 [梯度映射校正]--> 65535 [梯度到原色] --> 255 [最後輸出顏色的BLUE分量]

使用這種Gamma校正屏幕會明顯偏向於綠色.

 

DirectX Graphics  , RED, GREEN, BLUE  梯度通過D3DGAMMARAMP結構定義,

 

/* Gamma Ramp: Same as DX7 */

 

typedef struct _D3DGAMMARAMP

{

    WORD                red  [256];

    WORD                green[256];

    WORD                blue [256];

} D3DGAMMARAMP;

 

然後通過IDirect3DDevice9的函數SetGammaRamp設置,

void SetGammaRamp(UINT  iSwapChain,           

                  DWORD Flags,                

                  CONST D3DGAMMARAMP * pRamp);

void GetGammaRamp(UINT  iSwapChain,            

                  D3DGAMMARAMP * pRamp);      

這裏注意有些顯卡使用WORD中的高位字節有些使用低位字節所以最好兩個字節設置相同的數值0xCOCO.

 

12.2 Alpha Test

 

    Alpha 字節的用處之一就是根據Alpha的數值和一特定數值的邏輯比較運算的結果決定當前位的顏色是否顯示.

 

12.2.1 邏輯比較運算類型

 

    邏輯運算類型在枚舉結構中定義,

typedef enum _D3DCMPFUNC {

    D3DCMP_NEVER                = 1,

    D3DCMP_LESS                 = 2,

    D3DCMP_EQUAL                = 3,

    D3DCMP_LESSEQUAL            = 4,

    D3DCMP_GREATER              = 5,

    D3DCMP_NOTEQUAL             = 6,

    D3DCMP_GREATEREQUAL         = 7,

    D3DCMP_ALWAYS               = 8,

    D3DCMP_FORCE_DWORD          = 0x7fffffff, /* force 32-bit size enum */

} D3DCMPFUNC;

 

a.   D3DCMP_NEVER : 所有比較都返回失敗

b.   D3DCMP_LESS  當前Alpha值小於特定值時返回成功

c.   D3DCMP_EQUAL : 當前Alpha值等於特定值時返回成功

d.   D3DCMP_LESSEQUAL : 當前Alpha值小於等於特定值時返回成功

e.   D3DCMP_GREATER : 當前Alpha值大於特定值時返回成功

f.   D3DCMP_NOTEQUAL : 當前Alpha值不等於特定值時返回成功

g.   D3DCMP_GREATEREQUAL : 當前Alpha值大於等於特定值時返回成功

h.   D3DCMP_ALWAYS : 所有比較都返回成功

 

12.2.2 Alpha-Test 的過程

 

1.   打開Alpha-Test標誌(一般只是在渲染的時候打開渲染結束後關閉)

   IDirect3DDevice9::SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);

2.   設置邏輯比較的類型(默認類型爲D3DCMP_ALWAYS)

   IDirect3DDevice9::SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_ALWAYS);

3.   設置比較用的特定數值(默認爲0)

   IDirect3DDevice9::SetRenderStateD3DRS_ALPHAREF, 0);

 

12.3 Alpha Test 例子

 

12.3.1 代碼更新

 

我們來看看game12的主要更新的代碼(下載game12 project),

---------------------------------------------------------------

// d9graphics.cpp 根據鼠標選取的比較運算和特殊比較值更新

VOID CD9Graphics::UpdateAlpha()

{

   m_pDev->SetRenderState(D3DRS_ALPHAREF, (DWORD)g_nValue);

   m_pDev->SetRenderState(D3DRS_ALPHAFUNC, g_aTest[g_nTest]);

}

 

// d9graphics.cpp 根據鼠標選取的不同Gamma校正更新屏幕明暗度

VOID CD9Graphics::UpdateGamma(INT nIndex)

{

   if (g_gamma == nIndex)

   {

        return;

   }

   g_gamma = nIndex;

   INT nGamma = g_aGamma[nIndex];

   D3DGAMMARAMP map = { 0 };

 

   for (INT i = 0; i < 256; i++)

   {

        map.red[i]   = g_ramp.red[i] * nGamma;

        map.green[i] = g_ramp.green[i] * nGamma;

        map.blue[i]  = g_ramp.blue[i] * nGamma;

   }

   m_pDev->SetGammaRamp(0, D3DSGR_CALIBRATE, &map);

}

VOID CD9Graphics::Render()

{

// reset device if last render detect the device lost

   HRESULT h = 0L;

   if (g_bLastDev)

   {

        h = m_pDev->TestCooperativeLevel();

        if (h == D3DERR_DEVICENOTRESET)

        {

            TermObject();

            m_pDev->Reset(&g_d3dpm);

            InitObject();

            g_bLastDev = FALSE;

        }

        else

        {

            return;

        }

   }

 

m_pDev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,

D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    if (FAILED(m_pDev->BeginScene()))

   {

        return;

   }

   m_pDev->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);

   m_pDev->SetStreamSource(0, m_pVB, 0, sizeof(MYVERTEXTEX));

   m_pDev->SetFVF(D3DFVF_MYVERTEXTEX);

   m_pDev->SetTexture(0, m_pTexture);

   m_pDev->DrawPrimitive(D3DPT_TRIANGLELIST, 0,  2);

   m_pDev->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);

 

    m_pDev->EndScene();

    h = m_pDev->Present(NULL, NULL, NULL, NULL);

   if (FAILED(h))

   {

        if (h == D3DERR_DEVICELOST)

        {

            g_bLastDev = TRUE;

        }

        else if (h == D3DERR_DRIVERINTERNALERROR)

        {

            DestroyWindow(g_hWnd);

        }

   }

}

---------------------------------------------------------------

 

 

12.3.2 例子說明

 

例子中使用的天藍色256 * 256的圖片每位都有特定的Alpha當使用不同的比較運算同不同的比較數值進行比較後得到的結果影響顯示的效果. Alpha-Test的運算效率優於Alpha-Blend, 所以如果能用Alpha-Test代替Alpha-Blend得到相同的效果建議使用Alpha-Test.

 

12.4 Alpha Blend

 

Alpha Blend 中也使用到了Alpha字節Alpha字節並不是起決定性作用的 Alpha Blend的作用是將要寫入幀緩衝器的顏色(源顏色)和幀緩衝器中原有顏色(目標顏色)按比例係數進行混合混合時分成三原色分量進行顏色混合公式,

FinalColor = SrcColor * SrcBlendFactor + DestColor * DestBlendFactor

 

12.4.1 混合比例係數

 

SrcBlendFactor DestBlendFactor取值定義在D3DBLEND, DirectX Graphics SDK中有詳細的說明

 

12.4.2 Alpha Blend 的過程

 

1.   打開Alpha Blend標誌(一般只是在渲染的時候打開渲染結束後關閉)

   IDirect3DDevice9::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);

2.   設置源和目標比例係數

( 默認源比例係數爲D3DBLEND_ONE, 目標比例係數爲D3DBLEND_ZERO)

   IDirect3DDevice9::SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE)

IDirect3DDevice9::SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO)

 

12.5 Alpha Blend 例子

 

12.5.1 代碼更新

 

我們來看看 game13 的主要更新的代碼 ( 下載 game13 project),

---------------------------------------------------------------

// d9graphics.cpp 根據鼠標選取的源和目標比例係數更新

VOID CD9Graphics::UpdateAlpha()

{

   m_pDev->SetRenderState(D3DRS_SRCBLEND, g_aBlend[g_nSrc]);

   m_pDev->SetRenderState(D3DRS_DESTBLEND, g_aBlend[g_nDst]);

}

VOID CD9Graphics::Render()

{

//...

m_pDev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,

D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    if (FAILED(m_pDev->BeginScene()))

   {

        return;

   }

   m_pDev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);

   m_pDev->SetStreamSource(0, m_pVB, 0, sizeof(MYVERTEXTEX));

   m_pDev->SetFVF(D3DFVF_MYVERTEXTEX);

   m_pDev->DrawPrimitive(D3DPT_TRIANGLELIST, 0,  2);

   m_pDev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);

    m_pDev->EndScene();

    h = m_pDev->Present(NULL, NULL, NULL, NULL);

//...

}

---------------------------------------------------------------

 

12.5.2 例子說明

 

例子中使用RGB(0, 0, 128)的幀緩衝器背景色物體顏色爲RGB(128, 0, 0), 根據不同的源和目標比例係數混合的顏色不同例子中沒有使用光照加上光照Alpha Blend效果相同只是源顏色加上了光源提供的顏色.

 

a.   源比例係數 : D3DBLEND_ONE; 目標比例係數 : D3DBLEND_ZERO

RGB(128, 0, 0) = RGB(128, 0, 0, X) * (1, 1, 1, 1) +

                 RGB(0, 0, 128, X) * (0, 0, 0, 0)

 

b.   源比例係數 : D3DBLEND_SRCCOLOR; 目標比例係數 : D3DBLEND_ZERO

這時的源比例爲單位化的物體顏色 == (0.5, 0, 0, X)

RGB(64, 0, 0) = RGB(128, 0, 0, X) * (0.5, 0, 0, X) +

                 RGB(0, 0, 128) * (0, 0, 0, 0)

 

c.   源比例係數 : D3DBLEND_SRCCOLOR; 目標比例係數 : D3DBLEND_DESTCOLOR

源比例爲單位化的物體顏色 == (0.5, 0, 0, X)

目標比例爲單位化的幀緩衝器背景色 == (0, 0, 0.5, X)

 

RGB(64, 0, 64) = RGB(128, 0, 0, X) * (0.5, 0, 0, X) +

                 RGB(0, 0, 128, X) * (0, 0, 0.5, X)

 

其他類型的Alpha Blend同樣可以計算出混合的顏色.

 

Alpha Blend 在多重紋理中是非常重要的一項功能.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章