我們知道,D3D11中按Frame來渲染物體,每個Frame中又可能包含若干個primitive,如下面的所示:
gpu在實際渲染中,會按幀來渲染,比如frame0中,有兩個primitive(三角形),經過vs以後,PA(primitive assemble) block會進行體元裝配,然後進行光柵化操作,光柵化操作時候,會比較depth buffer的值。因為紅色的三角形面的z值更小,所以它會覆蓋黑色三角形一部分。
如果我們想要混合2個三角形的顯示,該怎麼做呢?這時就需要alpha blend操作,就是後緩衝中內容和當前ps輸出的pixel顏色進行混合操作。使用alpha blend操作,我們可以實現透明等效果。
現在我們看看D3D11中alpha blend的實現原理:
1、混合方程
假定現在ps輸出的像素顏色是Csrc = (Rs , Gs , Bs),後緩衝中的對應像素顏色值是Cdst = (Rd , Gd , Bd),
顏色Fsrc和Fdst稱作源混合(blend)因子和目的混合(blend)因子,表示顏色按分量相乘,表示任意操作符,比如加法,減法等等。
通過方程計算後得到的C是最終的顏色值,它將覆蓋後緩衝的像素顏色。
以上方程是對於RGB顏色來說的,對於alpha值也有相應的方程:
2、blend操作
混合方程中的為下面的操作符之一:
typedef enum D3D11_BLEND_OP{ D3D11_BLEND_OP_ADD = 1, D3D11_BLEND_OP_SUBTRACT = 2, D3D11_BLEND_OP_REV_SUBTRACT = 3, D3D11_BLEND_OP_MIN = 4, D3D11_BLEND_OP_MAX = 5,} D3D11_BLEND_OP;Constants
-
D3D11_BLEND_OP_ADD ,把兩個顏色加起來。
-
D3D11_BLEND_OP_SUBTRACT ,Source1減去source2。
-
D3D11_BLEND_OP_REV_SUBTRACT ,source2減去source1
-
D3D11_BLEND_OP_MIN 取最小顏色值。
-
D3D11_BLEND_OP_MAX 取最大顏色值。
3、blend因子
D3D11_BLEND_ZERO F = (0, 0, 0, 0)
D3D11_BLEND_ONE F = (1, 1, 1, 1)
D3D11_BLEND_SRC_COLOR F = (Rs , Gs , Bs)
D3D11_BLEND_INV_SRC_COLOR F = (1 – Rs ,1 – Gs ,1 - Bs)
D3D11_BLEND_SRC_ALPHA F = (Alphas , Alphas, Alphas ), 混合因子為源顏色的alpha值。
D3D11_BLEND_INV_SRC_ALPHA F = (1 - Alphas , 1 - Alphas, 1 - Alphas )
D3D11_BLEND_DEST_ALPHA F = (Alphad , Alphad , Alphad ), 混合因子為目的顏色的alpha值。
D3D11_BLEND_INV_DEST_ALPHA F = (1 - Alphad , 1 - Alphad , 1 - Alphad ),
D3D11_BLEND_DEST_COLOR F = (Rd , Gd , Bd)
D3D11_BLEND_INV_DEST_COLOR F = (1 – Rd ,1 – Gd ,1 – Bd)
D3D11_BLEND_SRC_ALPHA_SAT F = (Alpha’s , Alpha’s, Alpha’s ), Alpha’s = clamp(Alphas , 0, 1)
這兒還有一些其它的blend因子設定,具體請查看Directx的文檔。需要注意的是對於alpha blend因子:結尾是_COLOR的不能使用。
4、blend狀態
使用alpha blend之前,我們需要設定blend state,並enalbe它。
首先我們要填寫一個D3D11_BLEND_DESC結構,然後調用CreateBlendState,就可以建立blend狀態了。
重要的是我們要在MRT中設定blend因子以及操作方式等等。(一般是MRT[0],對應一個輸出,如果使用多個MRT,可以設定不同blend因子或者操作方式)。
typedef struct D3D11_BLEND_DESC
{ BOOL AlphaToCoverageEnable;
BOOL IndependentBlendEnable;
D3D11_RENDER_TARGET_BLEND_DESC RenderTarget[8];
} D3D11_BLEND_DESC;
typedef struct D3D11_RENDER_TARGET_BLEND_DESC
{
BOOL BlendEnable;
D3D11_BLEND SrcBlend;
D3D11_BLEND DestBlend;
D3D11_BLEND_OP BlendOp;
D3D11_BLEND SrcBlendAlpha;
D3D11_BLEND DestBlendAlpha;
D3D11_BLEND_OP BlendOpAlpha;
UINT8 RenderTargetWriteMask;
} D3D11_RENDER_TARGET_BLEND_DESC;
現在我們修改myTutorialD3D11_38的代碼,增加blend支援,使水有透明的效果。其實代碼很簡單,首先在D3DClass類中增加兩個函數。
void D3DClass::TurnOnAlphaBlending()
{
float blendFactor[4];
// 設定blend因子
blendFactor[0] = 0.0f;
blendFactor[1] = 0.0f;
blendFactor[2] = 0.0f;
blendFactor[3] = 0.0f;
// 開啟alpha blend
m_deviceContext->OMSetBlendState(m_alphaEnableBlendingState, blendFactor, 0xffffffff);
return;
}
void D3DClass::TurnOffAlphaBlending()
{
float blendFactor[4];
// 設定blend因子
blendFactor[0] = 0.0f;
blendFactor[1] = 0.0f;
blendFactor[2] = 0.0f;
blendFactor[3] = 0.0f;
// 關閉alpha blend
m_deviceContext->OMSetBlendState(m_alphaDisableBlendingState, blendFactor, 0xffffffff);
return;
}
我們還要在初始化函數中,建立兩個blend狀態,一個表示enable blend,一個表示disable blend。
D3D11_BLEND_DESC blendStateDescription;
…
// 初始化blend描述符
ZeroMemory(&blendStateDescription, sizeof(D3D11_BLEND_DESC));
// 建立一個alpha blend狀態.
blendStateDescription.RenderTarget[0].BlendEnable = TRUE;
blendStateDescription.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
blendStateDescription.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
blendStateDescription.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
blendStateDescription.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
blendStateDescription.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
blendStateDescription.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
blendStateDescription.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;//0x0f;
// 用描述符建立一個alpha blend狀態
result = m_device->CreateBlendState(&blendStateDescription, &m_alphaEnableBlendingState);
if(FAILED(result))
{
return false;
}
//修改描述符.
blendStateDescription.RenderTarget[0].BlendEnable = FALSE;
//建立一個新的blend狀態.
result = m_device->CreateBlendState(&blendStateDescription, &m_alphaDisableBlendingState);
在GraphicsClass類中,渲染水時,開啟alpha blend
// 開啟alpha blend.
m_D3D->TurnOnAlphaBlending();
// 把模型頂點和索引緩衝放入管線,準備渲染.
m_WaterModel->Render(m_D3D->GetDeviceContext());
result = m_LightTexShader->Render(m_D3D->GetDeviceContext(), m_WaterModel->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix,
light, material, camera,m_TexManager->createTex(m_D3D->GetDevice(),string("water2.dds")));
if(!result)
{
return false;
}
// 關閉alpha blend.
m_D3D->TurnOffAlphaBlending();
最後一點就是在lighttex.ps中做小小的改動,
float4 finalcolor1;
finalcolor1 = float4(finalcolor.xyz,0.5);
return finalcolor1;
這是因為我們的混合因子都是來自於alpha值
blendStateDescription.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
blendStateDescription.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
程式最終執行效果如下:
完整的代碼請參考:
工程檔案myTutorialD3D11_39
代碼下載:
http://files.cnblogs.com/mikewolf2002/d3d1139-49.zip
http://files.cnblogs.com/mikewolf2002/pictures.zip