1. 程式人生 > >D3D渲染技術之混合案例

D3D渲染技術之混合案例

在本篇內容中,我們將介紹用於獲得特定效果的一些混合因子組合, 在這些示例中,我們僅檢視RGB混合, Alpha混合的處理方式類似。

無顏色寫入

假設我們想要保持原始目標畫素的原樣,而不是覆蓋它或將其與當前光柵化的源畫素混合。例如,如果我們只想寫入深度/模板緩衝區,而不是後臺緩衝區。 為此,請將源畫素混合因子設定為D3D12_BLEND_ZERO,將目標混合因子設定為D3D12_BLEND_ONE,將混合運算子設定為D3D12_BLEND_OP_ADD。 通過此設定,混合等式可簡化為:
在這裡插入圖片描述
在這裡給讀者舉一個例子,實現的方法是將D3D12_RENDER_TARGET_BLEND_DESC :: RenderTargetWriteMask成員設定為0,這樣就不會寫入任何顏色通道。
在這裡插入圖片描述


上圖中,新增源和目標顏色, 新增顏色後,新增會建立更亮的影象。

增加/減少

假設我們要新增具有目標畫素的源畫素(見上圖所示)。 要執行此操作,請將源混合因子設定為D3D12_BLEND_ONE,將目標混合因子設定為D3D12_BLEND_ONE,將混合運算子設定為D3D12_BLEND_OP_ADD。 通過此設定,混合等式可簡化為:
在這裡插入圖片描述
我們可以使用上面的混合因子從目標畫素中減去源畫素,並用D3D12_BLEND_OP_SUBTRACT替換混合操作(見下圖所示)。
在這裡插入圖片描述
從目標顏色中減去源顏色, 由於顏色被移除,減法會產生較暗的影象。

相乘

假設我們想要將源畫素與其對應的目標畫素相乘(見下圖所示), 為此,我們將源混合因子設定為D3D12_BLEND_ZERO,將目標混合因子設定為D3D12_BLEND_SRC_COLOR,將混合運算子設定為D3D12_BLEND_OP_ADD。 通過此設定,混合等式可簡化為:
在這裡插入圖片描述

在這裡插入圖片描述
將源顏色和目標顏色相乘。

透明度

源α分量被認為是控制源畫素的不透明度的百分比(例如,0α表示0%不透明,0.4表示40%不透明,1.0表示100%不透明)。 不透明度和透明度之間的關係只是T = 1 - A,其中A是不透明度,T是透明度。 例如,如果某些東西是0.4不透明的,則它是1 - 0.4 = 0.6透明, 現在假設我們想要根據源畫素的不透明度混合源畫素和目標畫素, 要執行此操作,請將源混合因子設定為D3D12_BLEND_SRC_ALPHA,將目標混合因子設定為D3D12_BLEND_INV_SRC_ALPHA,將混合運算子設定為D3D12_BLEND_OP_ADD。 通過此設定,混合等式可簡化為:
在這裡插入圖片描述


例如,假設為as= 0.25,也就是說源畫素僅為25%不透明, 然後當源畫素和目標畫素混合在一起時,我們期望最終顏色將是源畫素的25%和目標畫素的75%(源畫素“後面”的畫素)的組合,因為源畫素是 75%透明。 上面的等式恰恰說明了這一點:
在這裡插入圖片描述
使用這種混合方法,我們可以繪製透明物件,如前面圖中所示。 應該注意的是,使用這種混合方法,繪製物件的順序很重要。 我們使用以下規則:
繪製不使用混合的物件, 接下來,按照與攝像機的距離對使用混合的物件進行排序, 最後,繪製以前後順序使用混合的物件。
從後到前繪製順序的原因是物件與空間背後的物件混合, 因為如果一個物體是透明的,我們可以通過它看到它背後的場景。 因此,必須將透明物件後面的所有畫素都寫入後緩衝區,以便我們可以將透明源畫素與其後面場景的目標畫素混合。
對於無顏色寫入介紹的混合方法,繪製順序無關緊要,因為它只是阻止源畫素寫入後緩衝區。 對於增加/減少和相乘討論的混合方法,我們仍然首先繪製非混合物件,最後繪製混合物件; 這是因為我們希望在開始混合之前首先將所有非混合幾何體放置在後緩衝區上。 但是,我們不需要對使用混合的物件進行排序, 這是因為操作是可交換的。 也就是說,如果從後緩衝區畫素顏色B開始,然後對該畫素執行n加法/減法/乘法混合,則順序無關緊要:
在這裡插入圖片描述

混合和深度緩衝

當與加法/減法/乘法混合混合時,深度測試會出現問題,我們將僅使用加法混合來解釋,但是相同的想法適用於減法/乘法混合。如果我們使用加法混合渲染一組S物件,那麼想法是S中的物件不會相互模糊;相反,它們的顏色只是累積(見下圖所示)。因此,我們不希望在S中物件之間進行深度測試;因為如果我們這樣做,沒有從後到前的繪製順序,S中的一個物件會遮擋S中的另一個物件,從而導致畫素片段由於深度測試而被拒絕,這意味著物件的畫素顏色不會被累積到混合總和中。我們可以通過在S中渲染物件時禁用對深度緩衝區的寫入來禁用S中物件之間的深度測試。由於禁用了深度寫入,因此使用加法混合繪製的S中物件的深度將不會寫入深度緩衝區;因此,由於深度測試,該物件不會遮擋後面S中任何後來繪製的物件。請注意,我們僅在S中繪製物件時禁用深度寫入(使用附加混合繪製的物件集)。深度測試仍然啟用,這樣,非混合幾何體(在混合幾何體之前繪製)仍將模糊其後面的混合幾何體。例如,如果在牆後面有一組加法混合的物件,則不會看到混合物件,因為實心牆遮擋了它們。如何禁用深度寫入,一般地說,配置深度測試設定將在下面部落格中介紹。
在這裡插入圖片描述
通過新增混合,在更多顆粒重疊並加在一起的源點附近強度更大, 隨著顆粒擴散,強度減弱,因為較少的顆粒重疊並被加在一起。

Alpha 通道

在透明中的示例顯示源alpha分量可用於RGB混合以控制透明度, 混合方程中使用的源顏色來自畫素著色器。 我們將漫反射材質的alpha值作為畫素著色器的alpha輸出返回, 因此,漫反射貼圖的alpha通道用於控制透明度。

float4 PS(VertexOut pin) : SV_Target
{
  float4 diffuseAlbedo = gDiffuseMap.Sample(
    gsamAnisotropicWrap, pin.TexC) * gDiffuseAlbedo;
 
  …
  
  // Common convention to take alpha from diffuse albedo.
  litColor.a = diffuseAlbedo.a;
  return litColor;
}

通常可以在任何流行的影象編輯軟體(如Adobe Photoshop)中新增Alpha通道,然後將影象儲存為支援像DDS這樣的Alpha通道的影象格式。

裁剪畫素

有時我們希望不處理源畫素, 這可以通過內在的HLSL裁剪(x)函式來完成, 此函式只能在畫素著色器中呼叫,如果x <0,它將丟棄當前畫素進一步處理。此函式對於渲染線柵紋理很有用,例如,如下圖所示。 也就是說,渲染畫素的畫素是完全不透明或完全透明的。
在這裡插入圖片描述
與它的Alpha通道的鐵絲網紋理, 具有黑色alpha值的畫素將被裁剪功能不被繪製; 因此,只留下鐵絲網。 實質上,alpha通道用於從紋理中遮蔽非柵欄畫素。
在畫素著色器中,我們抓取紋理的alpha分量, 如果它是一個接近0的小值,表示該畫素是完全透明的,那麼我們將該畫素剪輯為進一步處理。

float4 PS(VertexOut pin) : SV_Target
{
  float4 diffuseAlbedo = gDiffuseMap.Sample(
    gsamAnisotropicWrap, pin.TexC) * gDiffuseAlbedo;
   
#ifdef ALPHA_TEST
  // Discard pixel if texture alpha < 0.1. We do this test as soon 
  // as possible in the shader so that we can potentially exit the
  // shader early, thereby skipping the rest of the shader code.
  clip(diffuseAlbedo.a - 0.1f);
#endif
  …
 
  // Common convention to take alpha from diffuse albedo.
  litColor.a = diffuseAlbedo.a;
 
  return litColor;
}

觀察我們只在定義了ALPHA_TEST時裁剪, 這是因為我們可能不想為某些渲染項呼叫裁剪,因此我們需要能夠通過使用專門的著色器來開啟/關閉它。 此外,使用alpha測試需要付出代價,因此我們只應在需要時使用它。
請注意,使用混合可以獲得相同的結果,首先,不需要進行混合計算(可以禁用混合), 此外,可以通過畫素著色器丟棄畫素。
由於過濾,alpha通道可能會有點模糊,因此在裁剪畫素時應留下一些緩衝空間。 例如,剪下畫素的alpha值接近0,但不一定恰好為零。
下圖顯示了“Blend”演示的螢幕截圖, 它使用透明混合渲染半透明水,並使用裁剪測試渲染線柵欄盒, 值得一提的另一個變化是,因為我們現在可以通過帶有柵欄紋理的方框看到,我們想要禁用alpha測試物件的背面剔除:
在這裡插入圖片描述

D3D12_GRAPHICS_PIPELINE_STATE_DESC alphaTestedPsoDesc = opaquePsoDesc;
alphaTestedPsoDesc.PS = 
{ 
  reinterpret_cast<BYTE*>(mShaders["alphaTestedPS"]->GetBufferPointer()),
    mShaders["alphaTestedPS"]->GetBufferSize()
};
alphaTestedPsoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&alphaTestedPsoDesc, IID_PPV_ARGS(&mPSOs["alphaTested"])));

程式碼下載地址:連結:https://pan.baidu.com/s/1X0Vikf6qGYGPKU-Nwf-wYA
提取碼:h79q