2011-02-01 147 views
3

我想實現預乘alpha混合。在此頁面上:What Is Color Blending?,它們確實解釋了標準alpha混合,但不適用於預乘值。預乘alpha混合

Alpha混合:(源×Blend.SourceAlpha)+(目標×Blend.InvSourceAlpha)

根據公式,將其轉換爲這樣:

a = ((srcA * srcA) >> 8) + ((tgtA * (255 - srcA)) >> 8); 
    r = ((srcR * srcA) >> 8) + ((tgtR * (255 - srcA)) >> 8); 
    g = ((srcG * srcA) >> 8) + ((tgtG * (255 - srcA)) >> 8); 
    b = ((srcB * srcA) >> 8) + ((tgtB * (255 - srcA)) >> 8); 

它的工作原理,顯然...

現在如何將其轉換爲處理預倍值?

a = ((srcA)) + ((tgtA * (255 - srcA)) >> 8); 
    r = ((srcR)) + ((tgtR * (255 - srcA)) >> 8); 
    g = ((srcG)) + ((tgtG * (255 - srcA)) >> 8); 
    b = ((srcB)) + ((tgtB * (255 - srcA)) >> 8); 

由於它已經預倍增,我放棄了第一項乘法......正確! 但結果是阿爾法混合和添加劑混合之間,趨於更多的添加劑。最後它看起來不太融合。這可能是錯誤的,因爲它應該看起來完全像經典的alpha混合;或者是這種預期的行爲?

謝謝。

+0

我認爲`srcA * srcA`在第一個代碼塊應該只是`srcA`。你不需要自行乘以alpha。 – 2011-02-01 21:03:01

回答

4

之所以預乘的作品,是因爲它實際上結束了現蕾目標阿爾法它添加源圖像到目標

如前。如果沒有預乘法,我們得到這個源圖像數據:

srcA = origA 
srcR = origR 
srcG = origG 
srcB = origB 

當應用於某一目標,我們得到這個結果圖像:

a = ((srcA * srcA) >> 8) + ((tgtA * (255 - srcA)) >> 8) 
r = ((srcR * srcA) >> 8) + ((tgtR * (255 - srcA)) >> 8) 
g = ((srcG * srcA) >> 8) + ((tgtG * (255 - srcA)) >> 8) 
b = ((srcB * srcA) >> 8) + ((tgtB * (255 - srcA)) >> 8) 

擴大這一點,我們得到:

a = ((origA * origA) >> 8) + ((tgtA * (255 - origA)) >> 8) 
r = ((origR * origA) >> 8) + ((tgtR * (255 - origA)) >> 8) 
g = ((origG * origA) >> 8) + ((tgtG * (255 - origA)) >> 8) 
b = ((origB * origA) >> 8) + ((tgtB * (255 - origA)) >> 8) 

沒有意外出現......

現在的乘前的源圖像數據,我們得到:

srcA = (origA * origA) >> 8 
srcR = (origR * origA) >> 8 
srcG = (origG * origA) >> 8 
srcB = (origB * origA) >> 8 

,當應用於目標是:

a = (srcA >> 8) + ((tgtA * (255 - srcA)) >> 8); 
r = (srcR >> 8) + ((tgtR * (255 - srcA)) >> 8); 
g = (srcG >> 8) + ((tgtG * (255 - srcA)) >> 8); 
b = (srcB >> 8) + ((tgtB * (255 - srcA)) >> 8); 

好了,我們知道這一點,但如果我們擴大了這一點,你會看到其中的差別:

a = (origA * origA) >> 8 + ((tgtA * (255 – ((origA * origA) >> 8))) >> 8); 
r = (origR * origA) >> 8 + ((tgtR * (255 - ((origA * origA) >> 8))) >> 8); 
g = (origG * origA) >> 8 + ((tgtG * (255 – ((origA * origA) >> 8))) >> 8); 
b = (origB * origA) >> 8 + ((tgtB * (255 – ((origA * origA) >> 8))) >> 8); 

比較那到NON預擴增的:

a = ((origA * origA) >> 8) + ((tgtA * (255 - origA)) >> 8) 
r = ((origR * origA) >> 8) + ((tgtR * (255 - origA)) >> 8) 
g = ((origG * origA) >> 8) + ((tgtG * (255 - origA)) >> 8) 
b = ((origB * origA) >> 8) + ((tgtB * (255 - origA)) >> 8) 

立即可以看到我們在將原始值應用到目標時將原始值平方,這意味着更多的目標會通過所產生的顏色值。

通過對它進行平方,你在說,我想要更多的目標來通過。

這就是爲什麼預乘時會消除透明塊周圍的條帶數量,因爲具有較低Alpha值的像素會獲得更多的目標像素,而不是您如果不預乘,而且發生在指數規模。

我希望這可以清除它。

+0

你好!感謝你們提供了很好的解釋,現在對這種方法的內部工作真的很有意義。事實上,我之前忘記了答案,因爲我對我的結果感到滿意。是的,正如你所說的那樣,它神奇地處理綁定,人們可以在我之前發佈的兩個屏幕截圖中看到它。你的回答告訴了一切,所以我認爲它應該成爲答案。儘管如此,我仍然鼓勵人們讀出所有這些主題,同時,感謝其他有幫助的人:-))) – Aybe 2011-06-27 21:17:28

1

瘋狂的猜測:你是否在改變混合量(srcA)?如果是這樣,您必須重新計算位圖中的預乘alpha值。如果你不這樣做,你會得到一個附加效果,這可能是你所描述的。

2

您得到的問題取決於您是否將自己的源Alpha值作爲預乘的一部分進行了預乘。如果你沒有,那麼你使用目標乘法的srcA表示是真正的阿爾法源的平方,所以你需要採取平方根爲計算:

originalSrcA = Math.Sqrt(srcA); 
a = ((srcA)) + ((tgtA * (255 - originalSrcA)) >> 8); 
r = ((srcR)) + ((tgtR * (255 - originalSrcA)) >> 8); 
g = ((srcG)) + ((tgtG * (255 - originalSrcA)) >> 8); 
b = ((srcB)) + ((tgtB * (255 - originalSrcA)) >> 8); 

如果您還沒有預自乘(我認爲更可能),則需要通過自身相乘得到同樣的結果作爲工作之一:

a = ((srcA * srcA) >> 8) + ((tgtA * (255 - srcA)) >> 8); 
r = ((srcR)) + ((tgtR * (255 - srcA)) >> 8); 
g = ((srcG)) + ((tgtG * (255 - srcA)) >> 8); 
b = ((srcB)) + ((tgtB * (255 - srcA)) >> 8); 
0

多次嘗試後,這裏就是我想出了:

我還預先乘以alpha通道,並保留我的第二個公式;這是我得到的最好結果。

在我發現關於它的最好的文檔醜陋的邊界消失時預乘法的會談: http://www.td-grafik.de/ext/xfrog/alpha/index.htmlhttp://blogs.msdn.com/b/shawnhar/archive/2010/04/08/premultiplied-alpha-in-xna-game-studio-4-0.aspx

那麼,傳統的alpha混合是不是真的有比較參考,我想我現在,因爲它看起來比正常的alpha混合更好。但是說實話,我並不真正瞭解100%,(看起來像)它的工作原理。另一個謎團

這是什麼讓我覺得沒關係;

左:alpha混合; 右:預乘

謝謝大家的幫助!