2010-01-31 54 views
15

我正在通過framebuffer對象渲染到紋理,並且在繪製透明圖元時,圖元與在該單個繪圖步驟中繪製的其他圖元正確混合,但它們沒有正確混合與幀緩衝區的以前的內容。opengl - 與framebuffer的先前內容混合

有沒有一種方法可以將紋理內容與新數據正確混合?

編輯:更多的信息請求,我會試圖更清楚地解釋;

我正在使用的blendmode是GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA。 (我相信這通常是標準的混合模式)

我正在創建一個跟蹤鼠標移動的應用程序。它繪製連接前一個鼠標位置和當前鼠標位置的線條,並且由於我不想在每一幀再次繪製線條,我想我會繪製紋理,從不清除紋理,然後繪製一個帶有該矩形的矩形紋理上顯示它。

這一切都正常工作,除了當我繪製alpha小於1的形狀到紋理上時,它不會與紋理的先前內容正確混合。假設我在紋理上繪製了一些alpha = .6的黑線。一對夫婦抽籤後,我在這些線上繪製一個alpha = .4的黑色圓圈。圓下方的線條被完全覆蓋。儘管圓形不是平坦的黑色(它與白色背景適當融合),但圓形下面沒有「黑色線條」,正如您所期望的那樣。

但是,如果我在同一個框架中繪製線條和圓形,它們就會適當地混合。我的猜測是紋理不會與之前的內容混合。這就像它只與glclearcolor混合。 (在這種情況下,它是< 1.0f,1.0f,1.0f,1.0f>)

+2

混合應該適用於framebuffer中的任何內容。 (這是什麼混合_does_)。提供更多的數據(你使用什麼混合模式,你稱之爲混合「不正確」?) – Bahbar 2010-01-31 10:10:45

+0

我已經更新了更多的信息,感謝您的快速回復 – staticfloat 2010-01-31 20:28:26

回答

26

我認爲這裏有兩個可能的問題。

請記住,所有覆蓋線都在這裏混合兩次。一旦混合到FBO紋理中,並再次在場景中混合FBO紋理時。

因此,第一種可能性是,在FBO覆蓋圖中繪製一條線時不啓用混合。當您在混合關閉的情況下繪製RGBA曲面時,當前alpha將直接寫入FBO疊加層的Alpha通道。之後,當您將FBO的整個紋理融合到場景中時,該alpha會讓您的線條變得透明。因此,如果您對「世界」進行混合但不在疊加元素之間進行混合,則可能不會發生混合。

另一個相關問題:當​​您在FBO中以「標準」混合模式(src alpha,1 - src alpha)將另一條線疊加到另一條線上時,「混合」部分的Alpha通道將包含混合這兩個覆蓋元素的alpha。這可能不是你想要的。例如,如果您在覆蓋圖中相互繪製兩條50%alpha線,爲了在FBO blit時獲得等同效果,您需要FBO的alpha值爲... 75%。 (也就是1 - (1-.5)*(1-0.5),如果你在場景中畫了兩條50%的alpha線,會發生什麼,但是當你畫出兩條50%的線時,在FBO中獲得50%alpha(50%與... 50%的混合)

這帶來了最後一個問題:在將它們融合到世界之前,通過預先混合線條,改變繪製順序。而你可能有:

混合(混合(混合(背景色,模型),第一行),第二行);

現在你將有

混合(混合(第一行,第二行),混合(背景顏色,型號))。

換句話說,將疊加線預先混合到FBO中會改變混合的順序,從而以您不想要的方式改變最終外觀。

首先,解決這個問題的簡單方法是:不要使用FBO。我意識到這是一個「重新設計你的應用程序」的答案,但使用FBO並不是最便宜的,現代GL卡在繪製線條方面非常出色。因此,一種選擇是:將線條混合到FBO中,而不是將線條几何體寫入頂點緩衝區對象(VBO)。每次簡單擴展一下VBO。如果你一次畫的線數少於40,000行,那麼這幾乎肯定會像以前那樣快。

(一個提示,如果你走這條路線:使用glBufferSubData來寫入行,而不是glMapBuffer - 映射可能是昂貴的,並不適用於許多驅動程序的子範圍...更好的是讓驅動程序複製幾個新的頂點)

如果這不是一個選項(例如,如果您繪製混合的形狀類型或混合使用GL狀態,以便「記住」您所做的更復雜不僅僅是積累頂點),那麼你可能想要改變你如何繪製到VBO。

基本上你需要做的是啓用單獨的混合;將疊加層初始化爲黑色+ 0%alpha(0,0,0,0),並通過「標準混合」RGB進行混合,但對alpha通道進行疊加混合。這對alpha通道來說仍然不太正確,但它通常更接近 - 沒有這個,過度繪製的區域將會過於透明。

然後,在繪製FBO時,使用「預先倍增」的alpha,即(one,one-minus-src-alph)。

這就是爲什麼需要最後一步:當您繪製到FBO中時,您已經將每個繪製調用乘以其Alpha通道(如果混合處於打開狀態)。由於您正在繪製黑色,因此綠色(0,1,0,0.5)線現在變爲深綠色(0,0.5,0,0.5)。如果alpha處於打開狀態並且您再次正常混合,則重新應用alpha並且您將具有0,0.25,0,0.5。)。通過簡單地使用FBO顏色,可以避免第二個alpha乘法。

這有時稱爲「預先倍增」alpha,因爲alpha已經被乘以RGB顏色。在這種情況下,你希望它得到正確的結果,但在其他情況下,程序員使用它來提高速度。 (通過預倍增,當執行混合操作時,它消除了每個像素的多個像素)。

希望有幫助!當圖層沒有按順序混合時,正確混合就變得非常棘手,單獨的混合在舊硬件上不可用,因此每次簡單地繪製線條可能是最不痛苦的道路。

+0

謝謝你的深刻反應!我花了這麼長時間回到你身邊,但是如果我有更多積分給你,我會的!謝謝! – staticfloat 2010-03-03 06:08:27

3

正如Ben Supnik所說,最好的方法是使用顏色和alpha的獨立混合函數渲染整個場景。如果您正在使用經典非預乘混合函數,請嘗試glBlendFuncSeparateOES(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA,GL_ONE,GL_ONE)以將場景呈現給FBO。和glBlendFuncSeparateOES(GL_ONE,GL_ONE_MINUS_SRC_ALPHA)來顯示FBO。

它不是100%準確的,但在大多數情況下不會產生意外的透明度。請記住,舊硬件和一些移動設備(主要是OpenGL ES 1.x設備,如原始iPhone和3G)不支持分離的混合功能。 :(

13

清除FBO與透明的黑色(0,0,0,0)時,抽入回到前與

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 

並繪製與FBO

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 

以獲得確切的結果

正如Ben Supnik所寫,FBO包含的顏色已經與alpha通道相乘,所以不是再次使用GL_SRC_ALPHA進行繪製,而是使用GL_ONE繪製。通常使用GL_ONE_MINUS_SRC_ALPHA。

其原因在緩衝器這種方式混合alpha通道是不同的:

結合透明度的公式是

resultTr = sTr * dTr 

(I使用的,因爲並行的s和d,以OpenGL的源和目的地,但你可以看到的順序並不重要。)

寫有混濁(Alpha值),這將成爲

1 - rA = (1 - sA) * (1 - dA) 
<=> rA = 1 - (1 - sA) * (1 - dA) 
     = 1 - 1 + sA + dA - sA * dA 
     =   sA + (1 - sA) * dA 

這與默認的blend equation GL_FUNC_ADD的混合函數(源和目標因子)(GL_ONE,GL_ONE_MINUS_SRC_ALPHA)相同。


順便說一句:

以上答案從問題的具體問題,但如果你可以輕鬆地選擇繪製順序,可能在理論上可以更好地提請premultiplied色入緩衝器前到與

glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_ONE); 

和否則使用相同的方法。

我的理由是,圖形卡可以跳過已經實體的區域的着色器執行。我還沒有測試過,所以在實踐中可能沒有什麼區別。

+0

我想說〜〜5年後,我再次使用這個問題,閱讀你的帖子,並思考對我自己「哇,這解決了我的問題,非常簡單,而且完全適合!」然後我想:「我想知道誰在問原來的問題」 – staticfloat 2015-04-03 10:32:24

+0

@staticfloat嘿,我很高興達到了你。我正在尋找其他人試圖呈現嵌套的UI控件(我在實際的圖形編程方面顯得很糟糕),以前的回答讓我想起了單獨的混合模式,但我認爲如果確切的解決方案會很奇怪n是不可能的。再想一想,這個解決方案只有在你必須繪製_over_前一個緩衝區時纔是最優的。我會爲可以選擇順序的情況添加備註,例如將GUI控件渲染到緩衝區時,因爲這樣做的人不太可能最終落入此處。 – Tamschi 2015-04-04 00:17:34

+0

工程就像一個魅力!這幫助我將FBO的透明文本渲染到屏幕上。至於理解爲什麼/如何運作,我想我必須做更多的閱讀:) – Arahman 2016-09-23 15:07:14