簡體   English   中英

Direct3D使用“乘法”混合模式和alpha渲染2D圖像

[英]Direct3D rendering 2D images with “multiply” blending mode and alpha

我正在嘗試使用Direct3D復制Photoshop濾鏡。 我一直在閱讀和谷歌搜索不同的渲染狀態,我的效果幾乎正常。 問題是它忽略了紋理的alpha值。

這是一個解釋情況的圖像:

http://www.kloonigames.com/petri/stackoverflow_doesnt_allow_.jpg

我找到了一個解決方案,即保存沒有透明度和白色背景的圖像。 但我對這個解決方案不滿意。 問題是我真的需要使用alpha值。 我想逐漸淡出圖像。 如果混合模式忽略alpha值,我就無法做到這一點。

那么問題是如何使用alpha渲染圖像?

這是混合模式代碼:

dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);

編輯添加了SetTextureStageState

dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
dev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
dev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);

您可以通過在像素着色器中預乘alpha或使用帶有預乘alpha的紋理,一步實現此效果。

例如,如果您有一個着色器的3種可能的混合操作,並且您希望每個操作都考慮到alpha。

Blend = ( src.rgb * src.a ) + ( dest.rgb * (1-src.a) )
Add = ( src.rgb * src.a ) + ( dest.rgb )
Multiply = (src.rgb * dest.rgb * src.a) + (dest.rgb * (1-src.a) )

您會注意到單次傳遞不可能使用Multiply,因為源顏色有兩個操作。 但是,如果您在着色器中預乘alpha,則可以從混合操作中提取alpha分量,並且可以在同一着色器中混合所有三個操作。

在像素着色器中,您可以手動預乘Alpha。 或者使用像DirectXTex texconv這樣的工具來修改紋理。

return float4(color.rgb*color.a, color.a);

運營成為:

Blend = ( src.rgb ) + ( dest.rgb * (1-src.a) )
Add = ( src.rgb ) + ( dest.rgb )
Multiply = ( src.rgb * dest.rgb ) + (dest.rgb * (1-src.a) )

這聽起來像你想要的:

dst.rgb = (src.a * src.rgb) * ((1 - src.a) * dst.rgb)

您可以使用D3DRS_BLENDOP來做到這一點,但不幸的是沒有D3DBLENDOP_MULTIPLY。 如果沒有片段着色器,我認為這種操作是不可能的。

dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR);
dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

會做的伎倆。 但是,您不能再使用漫反射頂點顏色中的“alpha”。 在頂點顏色上設置低alpha實際上會使您的重疊像素變亮。

好吧,這並不像你想象的那么簡單。 我會使用一個效果和兩個renderTargets ...我很有趣你使用一個渲染過程來嘗試這樣做,這是行不通的。 Photoshop有圖層,每個圖層都有一個alpha通道。 順便說一下,知道你制作什么樣的應用程序會很高興。

所以首先在D3D中我會創建2個與窗口大小相同的RGBA_32bit renderTargets並將它們清除為白色。 使它像一個數組(new RenderTarget [2];)進行交換。

現在將混合狀態設置為(AlphaFunc = Add,Src = SrcAlpha,Dst = InvSrcAlpha)。 對於第一個圓圈,使用renderTarget [1]作為紋理/采樣器輸入源將其繪制到renderTarget [0]中。 您將使用效果渲染圓圈,該效果將采用圓圈顏色並將其與renderTarget [1]的采樣器顏色相乘。 在繪制圓形之后,通過簡單的索引將renderTarget [0]與renderTarget [1]交換,所以現在renderTarget [1]是您繪制的那個,renderTarget [0]是您從中采樣的。 然后重復第2輪的繪制過程,依此類推。

繪制圓圈后,將最后繪制的renderTarget復制到backBuffer並顯示場景。

以下是邏輯上如何執行此操作的示例。 如果您需要參考編碼http://www.codesampler.com/是個好地方。

void TestLayering()
{
bool rtIndex = false;
RenderTarget* renderTarget = new RenderTarget[2];
Effect effect = new Effect("multiplyEffect.fx");
effect.Enable();
BlendingFunc = Add;
BlendingSource = SrcAlpha;
BlendingDest = InvSrcAlpha;

for(int i = 0; i != circleCount; ++i)
{
  renderTarget[rtIndex].EnableAsRenderTarget();
  renderTarget[!rtIndex].EnableAsSampler();
  circle[i].Draw();
  rtIndex = !rtIndex;
}

//Use D3D9's StretchRect for this...
backBuffer.CopyFromSurface(renderTarget[rtIndex]);
}

//Here is the effects pixel shader
float4 PS_Main(InStruct In) : COLOR
{
float4 backGround = tex2D(someSampler, In.UV);
return circleColor * backGround;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM