要命的陰影
事情是這樣的捕透,近日查一個問題聪姿,半透明的素材視頻 或者 圖片,渲染到屏幕的時候半透明部分會有一圈深色陰影乙嘀。
渲染到屏幕時末购,星星的邊緣有一圈深色陰影
混合模式
基于以上問題,先來復習一下混合模式虎谢。OpenGL使用混合(Blending)技術來完成半透明圖像的渲染盟榴。
步驟如下:
// 1.開啟混合模式
glEnable(GLES20.GL_BLEND);
// 2.設置函數(shù):源因子,目標因子
glBlendFunc(int sfactor, int dfactor);
// 3.渲染目標
// 4.渲染源
// 5.關閉混合模式
glDisable(GLES20.GL_BLEND);
混合模式的公式婴噩,一定要注意好哪個是源擎场,哪個是目標。
Cˉresult=Cˉsource?Fsource+Cˉdestination?Fdestination
Cˉsource:源顏色向量几莽。這是來自紋理的本來的顏色向量迅办。
Cˉdestination:目標顏色向量。這是儲存在顏色緩沖中當前位置的顏色向量章蚣。
Fsource:源因子站欺。設置了對源顏色的alpha值影響。
Fdestination:目標因子纤垂。設置了對目標顏色的alpha影響矾策。
混合因子可選值如下:
選項 | 值 |
---|---|
GL_ZERO | 0 |
GL_ONE | 1 |
GL_SRC_COLOR | 源顏色向量Cˉsource |
GL_ONE_MINUS_SRC_COLOR | 1?Cˉsource |
GL_DST_COLOR | 目標顏色向量Cˉdestination |
GL_ONE_MINUS_DST_COLOR | 1?Cˉdestination |
GL_SRC_ALPHA | Cˉsource的alpha值 |
GL_ONE_MINUS_SRC_ALPHA | 1? Cˉsource的alpha值 |
GL_DST_ALPHA | Cˉdestination的alpha值 |
GL_ONE_MINUS_DST_ALPHA | 1? Cˉdestination的alpha值 |
GL_CONSTANT_COLOR | 常顏色向量Cˉconstant |
GL_ONE_MINUS_CONSTANT_COLOR | 1?Cˉconstant |
GL_CONSTANT_ALPHA | Cˉconstant的alpha值 |
GL_ONE_MINUS_CONSTANT_ALPHA | 1? Cˉconstant的alpha值 |
當然還可以使用glBlendFuncSeparate( int srcRGB, int dstRGB, int srcAlpha, int dstAlpha );
來分別對RGB 和 alpha 設置不同的因子。
使用glBlendEquation( int mode );
& glBlendEquationSeparate( int modeRGB, int modeAlpha );
配置混合函數(shù)運算符峭沦。
舉幾個例子贾虽,我們重點關注alpha的變化。(此時我還沒意識到預乘Premultiplied Alpha的作用)
源(1吼鱼,1蓬豁,0履磨,0.6),目標(0.6庆尘,0.6剃诅,0.5,1)
- glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA)
result.r = 1 * 0.6 + 0.6 * (1 - 0.6)= 0.84
result.g = 1 * 0.6 + 0.6 * (1 - 0.6)= 0.84
result.b = 0 * 0.6 + 0.5 * (1 - 0.6)= 0.2
result.a = 0.6 * 0.6 + 1 * (1 - 0.6)= 0.76 - .glBlendFuncSeparate(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA, GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA)
result.r = 1 * 0.6 + 0.6 * (1 - 0.6)= 0.84
result.g = 1 * 0.6 + 0.6 * (1 - 0.6)= 0.84
result.b = 0 * 0.6 + 0.5 * (1 - 0.6)= 0.2
result.a = 0.6 * 1 + 1 * (1 - 0.6)= 1 - glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA)
result.r = 1 * 1 + 0.6 * (1 - 0.6)= 1.24
result.g = 1 * 1 + 0.6 * (1 - 0.6)= 1.24
result.b = 0 * 1 + 0.5 * (1 - 0.6)= 0.2
result.a = 0.6 * 1 + 1 * (1 - 0.6)= 1
實際上驶忌,Bitmap在加載圖像的時候矛辕,會進行預乘,預乘就是將alpha通道的值分別與r付魔、g聊品、b的值相乘,得到新的r几苍、g翻屈、b的值,所以預乘后如果源使用GLES20.GL_SRC_ALPHA顯然值是不正確的妻坝,就需要使用GLES20.GL_ONE得到正確的值伸眶。
比如像素值(1,1刽宪,0厘贼,0.6)預乘后像素值(0.6,0.6圣拄,0嘴秸,0.6)
看到這幾個圖的效果應該也知道問題怎么解決了,那么繼續(xù)看下前面的問題庇谆。
我項目中會有多個層級的混合岳掐,部分采用blendFunc、部分采用shader饭耳,此處是使用shader mix 混合的串述,mix的混合模式等同于GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
mix(a, b, c) = a * (1-c) + b * c
由上述可知,采用GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
可解決黑色陰影問題哥攘。對應公式:
resultColor = blendColor * 1.0 + baseColor * (1.0 - blendColor.a);
為防止曝光剖煌,我們增加min函數(shù):
resultColor = min(resultColor, vec4(1.0));
為什么是黑色陰影
你會發(fā)現(xiàn),無論你清屏顏色設置的是什么逝淹,最后的半透明處的顏色都不是清屏顏色耕姊,但 當你設置給控件背景色時,你會發(fā)現(xiàn)栅葡,半透明的顏色會跟背景色變化
如下是修復后的效果:
另需要注意的是茉兰,源使用GLES20.GL_ONE對與貼圖設置alpha<1(著色器中的像素值 * alpha)會顯示異常,alpha不生效欣簇,仍需要使用GLES20.GL_SRC_ALPHA规脸,所以我們要根據(jù)自己的層級情況選擇設置什么混合模式或者shader中的計算公式坯约。
更多資料
creater關于blend,關于預乘premultiply alpha莫鸭,關于圖片白邊灰邊的幾點疑問
圖片Premultiplied Alpha
紋理混合遇到的問題 pre-multiplying OpenGL Android iOS