在進行 OpenGL 紋理混合的過程當中,遇到一個詭異的現象,兩個紋理混合的效果出人所料: 將一個ALPHA漸變的【鬍鬚】加在另外一張圖片上,這個 【鬍鬚】是由外向裏逐漸增長透明度的,也就是最外圍的透明度爲0,而中心的透明度接近 1,也就是徹底不透明,實心。那麼預期的效果但願是在底圖上加一個朦朧的效果,然而實際獲得的效果很讓人意外,出現一片淡淡的黑色!(以下圖中的鬍鬚旁邊的黑色。)html
shader:android
vec4 main_color = texture(rgbTexture, v_TexCoord); vec4 huxu_color = texture(huxu_texture, v_TexCoord); vec4 color_out = mix(main_color, huxu_color, huxu_color.a);
由於【鬍鬚】圖片是帶有透明度,根據其透明度與原圖進行混合,理應可以獲得咱們想要的結果。在 debug 過程當中我嘗試不進行混合,直接將【鬍鬚】繪製在圖片上,發現【鬍鬚】仍是有漸變效果,發現:算法
【鬍鬚】的 RGB 數據和原始圖片的 RGB 不一樣 此處存在 pre-multiplying.post
pre-multiplying:Android 平臺在加載一張位圖的時候,會自動作一個操做,將 RGB 的值乘上 alpha 值,從新保存。用公式表示以下:this
If you use OpenGL ES 2.0, pre-multiplying the output of your fragment shader is extremely simple: color.rgb *= color.a
回頭再看個人混合的方式,在 RGB 數據已經被作過一次 pre-multiplying 的狀況下,再乘一個 alpha: RGB_new = RGB * alpha * alpha
而後再和底圖的顏色加起來,天然就出錯了。好比在白色的透明度爲0.5的地方,原來的 RGB 爲255,這種奇怪的算法獲得的結果就是 63.75,接近黑色。這就是出現黑色的緣由。google
解決思路有2個spa
不作 pre-multiplying 混合時考慮到前面的狀況,再也不乘上 alpha
第一種方案:debug
android 加載方法:加載圖片時 設置 BitmapFactory.Options.
inPremultiplied
= false;code
inPremultiplied
added in API level 19
boolean inPremultiplied
If true (which is the default), the resulting bitmap will have its color channels pre-multipled by the alpha channel.
This should NOT be set to false for images to be directly drawn by the view system or through a Canvas. The view system and Canvas assume all drawn images are pre-multiplied to simplify draw-time blending, and will throw a RuntimeException when un-premultiplied are drawn.
This is likely only useful if you want to manipulate raw encoded image data, e.g. with RenderScript or custom OpenGL.
This does not affect bitmaps without an alpha channel.
Setting this flag to false while setting inScaled to true may result in incorrect colors.
See also:
hasAlpha()
isPremultiplied()
inScaled
IOS 上木有這種接口。htm
第二種方案:
vec4 main_color = texture(rgbTexture, v_TexCoord); vec4 huxu_color = texture(huxu_texture, v_TexCoord); vec4 color_out = main_color * (1.0 - huxu_color.a) + huxu_color;
和原來的相比,用OpenGL 的表述方式,原來作法是:(SRC_ALPHA, ONE_MINUS_SRC_ALPHA)
考慮pre-multiplying的話:(ONE, ONE_MINUS_SRC_ALPHA)
參考資料: