Direct-X學習筆記--Alpha顏色混合
阿新 • • 發佈:2019-02-07
Alpha混合技術是灰常有用的東東。待我好好學習一下。
一.簡介
首先看一下Alpha通道,Alpha通道是計算機中儲存圖片透明度資訊的通道,它是一個8位灰度的通道,用256級灰度記錄影象中的透明資訊,定義透明,不透明,半透明等,其中黑色表示完全透明,白色表示不透明,灰色為半透明。 如果不用Alpha混合,我們繪製圖形的顏色總是替換當前顏色緩衝區中存在的顏色,這樣後面的物體總是覆蓋在原有的物體上。但是當想要繪製類似於玻璃、水等具有透明效果的物體時,這種方法顯然滿足不了要求。通過定義一個表示物體半透明度的Alpha值和一個半透明計算公式,可以將要繪製的物體顏色與顏色緩衝區中存在的顏色相混合,從而繪製出具有半透明效果的物體,即傳說中的Alpha Blend。二.Direct-X中的Alpha融合公式
OutPutColor表示alpha混合後的顏色值. RGBsrc表示源顏色值,即將要繪製的圖元的顏色值 Ksrc表示源混合係數,通常賦值為表示半透明程度的alpha值,也可以是屬於列舉型別D3DBLEND的任意值,用來和RGBsrc相乘。 RGBdst表示目標顏色值,即當前顏色緩衝區中的顏色值 Kdst表示目標混合係數,可以是屬於列舉D3DBLEND的任意值,用來和RGBdst相乘。 OP表示源計算結果與顏色緩衝區計算結果的混合方法,預設狀態下OP為D3DBLEND_ADD,即源計算結果與顏色緩衝區計算結果相加。
圖形顯示中,對alpha混合最普遍的用法是:把OP賦值為D3DBLEND_ADD,使源計算結果和顏色緩衝區計算結果相加,這樣一來,alpha混合顏色的公式變為:
color = (RGBsrc * Ksrc) + (RGBdst * Kdst) 注意,這個公式中的*,其實指的是向量的點積運算,因為所謂的RGBsrc,和RGBdest都是一個顏色和Alpha組合而成的四元組(R,G,B,A)即紅,綠,藍,Alpha值,各對應融合的引數。所以更加詳細的來看,上面的公式應該寫成: color = (Rsrc * Ksrc1 + Rdst * Kdes1, Gsrc * Ksrc2 + Gdst * Kdst2, Bsrc * Ksrc3 + Bdst * Kdst3, Asrc * Ksrc4 + Adst * Kdst4); 而其實這個公式可以更加簡化,即我們要透明的物體使用一個係數K,而要透明到的地方只需要設定成(1- K)即可,在DX中已經為我們設定好了這個巨集:即把Ksrc賦值為D3DBLEND_SRCALPHA,即當前繪製畫素的alpha值;把Kdst賦值為D3DBLEND_INVSRCALPHA,即1減去當前繪製畫素的alpha值。 其實上面的設定已經可以較好地模擬大多數半透明物體的效果。當然我們也可以自己再設定其他的融合方式。所以我們還是有必要記一下其他的融合方式以及融合係數。
三. 融合的方式和融合係數整理
D3DBLENDOP_ADD源畫素計算結果與目標畫素的計算結果相加,即【最終結果】=【源】+【目標】
D3DBLENDOP_SUBTRACT源畫素計算結果與目標畫素的計算結果相減,即【最終結果】=【源】-【目標】
D3DBLENDOP_REVSUBTRACT目標畫素的計算結果減去源畫素計算結果,即【最終結果】=【目標】-【源】
D3DBLENDOP_MIN在源畫素計算結果和目標畫素計算結果之間取小者。即【最終結果】= MIN(【目標】,【源】)
D3DBLENDOP_MAX在源畫素計算結果和目標畫素計算結果之間取大者。即【最終結果】= MAX(【目標】,【源】) 2.融合因子: D3DBLEND_ZERO融合因子=(0,0,0,0)
D3DBLEND_ONE融合因子=(1,1,1,1)
D3DBLEND_SRCCOLOR融合因子=(R_src,G_src,B_src,A_src)
D3DBLEND_INVSRCCOLOR融合因子=(1-R_src,1-G_src,1-B_src,1-A_src)
D3DBLEND_SRCALPHA融合因子=(1-A_src,A_src,A_src,A_src)
D3DBLEND_INVSRCALPHA融合因子=(1-A_src,1-A_src,1-A_src,1-A_src)
D3DBLEND_DESTALPHA融合因子=(A_dst , A_dst, A_dst , A_dst)
D3DBLEND_INVDESTALPHA融合因子= (1-A_dst, 1-A_dst, 1-A_dst , 1-A_dst ).
D3DBLEND_DESTCOLOR融合因子=(R_dst , G_dst, B_dst , A_dst).
D3DBLEND_INVDESTCOLOR融合因子= (1 - R_dst, 1 - G_dst, 1 - B_dst, 1 - A_dst).
D3DBLEND_SRCALPHASAT融合因子= (f, f, f, 1),其中f = min(A_src,1 - A_dst)
其中R_src , G_src , B_src , A_src分別表示源(即source)畫素的紅、綠、藍、透明四個分量值,而R_dst , G_dst, B_dst , A_dst表示目標(即destination)畫素的紅、綠、藍、透明四個分量值。
四.Alpha值的設定
五.使用Alpha Blend
個人感覺Blend用於R,G,B,A都可以,可能是因為透明的情況用得比較多,所以才常用Alpha blend吧(純屬個人猜測)。 這裡我們使用了第二種情況,設定光照材質,改變模型材質的Alpha值,來達到觀察不同Alpha值進行融合的效果。 感覺Alpha Blend就是我們在繪製某個透明物體時,開啟的一種渲染狀態。 使用Alpha Blend有下面幾部: 1.設定繪製物件的Alpha值,預設的值是1.0f,即不透明,所以我們如果需要設定某個物件為透明的,就改變他的Alpha值,就可以讓其透明。 這裡面,我們直接使用的是物件的材質資訊,即預設的1.0f,並且增加了一個可以改動材質Alpha值的函式,讓Alpha值改變。 2.開啟Alpha測試 DX預設是關閉Alpha測試的,所以我們需要通過DX的萬能函式...SetRenderState來開啟Alpha測試。<span style="white-space:pre"> </span>//開啟Alpha融合
m_pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
當然,關閉的話,第二個引數為false即可。我們繪製完透明物件之後,不需要Alpha blend時,要將其關閉。
3.設定融合因子這裡面就是用上面我們說過的,DX為我們準備的最常用的那兩個融合因子就可以啦!
<span style="white-space:pre"> </span>//設定融合因子
m_pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); //設定源融合因子
m_pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); //設定目標融合因子
4.設定融合運算方式
這一步其實可以省略的,DX預設的Alpha融合方式就是ADD方式,但是我們還是寫一下,萬一哪天不用這個了呢,還得會設定別的呀!
<span style="white-space:pre"> </span>//設定融合運算方式(可以省略,DX預設即為D3DBLENDOP_ADD的融合運算方式)
m_pDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
六.完整的Demo
這裡面,我們使用了一個龍的模型(好吧,還是從淺墨大大那裡偷得),讀取之後,不使用紋理資訊,使用的預設的材質資訊,並且新增一個可以改變材質Alpha值的函式,用來改變物件的透明度。 先來一張沒去掉材質的截圖:去掉紋理資訊,開啟Alpha融合,預設Alpha值為1.0f,即完全不透明
減少材質中Diffuse中的Alpha值,半透明:
再減少,再減少,咦?龍呢? 答:被我吃了....
程式碼(將Alpha混合部分和材質資訊部分的程式碼放在了之前縮寫的CMesh類中,所以這裡僅貼出Mesh.cpp相關內容):
#include "stdafx.h"
#include "Mesh.h"
CMesh::CMesh(LPDIRECT3DDEVICE9 pDevice) : m_pDevice(pDevice)
{
}
CMesh::~CMesh(void)
{
//釋放相關資源
SAFE_DELETE_ARRAY(m_pMaterials);
if (m_pTextures)
{
for (int i = 0; i < m_dwNumMtrls; i++)
{
SAFE_RELEASE(m_pTextures[i]);
}
SAFE_DELETE_ARRAY(m_pTextures);
}
SAFE_RELEASE(m_pMesh);
}
void CMesh::CreateMesh(LPSTR filename)
{
LPD3DXBUFFER pAdjBuffer = NULL;
LPD3DXBUFFER pMtrlBuffer = NULL;
D3DXLoadMeshFromX(filename, D3DXMESH_MANAGED, m_pDevice, &pAdjBuffer, &pMtrlBuffer, NULL, &m_dwNumMtrls, &m_pMesh);
//讀取材質和紋理資料
D3DXMATERIAL *pMtrl = (D3DXMATERIAL*)pMtrlBuffer->GetBufferPointer();
m_pMaterials = new D3DMATERIAL9[m_dwNumMtrls];
m_pTextures = new LPDIRECT3DTEXTURE9[m_dwNumMtrls];
for (int i = 0; i < m_dwNumMtrls; i++)
{
//材質資訊
m_pMaterials[i] = pMtrl[i].MatD3D;
m_pTextures[i] = NULL;
//不使用Mesh模型的紋理資訊
//D3DXCreateTextureFromFileA(m_pDevice, pMtrl[i].pTextureFilename, &m_pTextures[i]);
}
//優化網格模型
m_pMesh->OptimizeInplace(D3DXMESHOPT_COMPACT | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_STRIPREORDER, (DWORD*)pAdjBuffer->GetBufferPointer(), NULL, NULL, NULL);
SAFE_RELEASE(pAdjBuffer);
SAFE_RELEASE(pMtrlBuffer);
}
void CMesh::DrawMesh(const D3DXMATRIXA16& matWorld)
{
m_pDevice->SetTransform(D3DTS_WORLD, &matWorld);
///Alpha Blend相關
//開啟Alpha融合
m_pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
//設定融合因子
m_pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); //設定源融合因子
m_pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); //設定目標融合因子
//設定融合運算方式(可以省略,DX預設即為D3DBLENDOP_ADD的融合運算方式)
m_pDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
//繪製Mesh
for (int i = 0; i < m_dwNumMtrls; i++)
{
m_pDevice->SetMaterial(&m_pMaterials[i]);
m_pDevice->SetTexture(0, m_pTextures[i]);
m_pMesh->DrawSubset(i);
}
}
//增加Alpha值的函式
void CMesh::AddAlphaValue()
{
for (int i = 0; i < m_dwNumMtrls; i++)
{
m_pMaterials[i].Diffuse.a += 0.1f;
if (m_pMaterials[i].Diffuse.a > 1.0f)
m_pMaterials[i].Diffuse.a = 1.0f;
}
}
//減少Alpha值的函式
void CMesh::ReduceAlphaValue()
{
for (int i = 0; i < m_dwNumMtrls; i++)
{
m_pMaterials[i].Diffuse.a -= 0.1f;
if (m_pMaterials[i].Diffuse.a < 0.0f)
m_pMaterials[i].Diffuse.a = 0.0f;
}
}