問題描述
項目中,用一個相機將圖像渲染到Render Texture上面,然後將這個Render Texture給UI中的Raw Image顯示出來,發現粒子效果沒有出現。
如圖:
實際的樣子:
Raw Image中顯示的樣子:
解決
其中,粒子使用的Shader是Legacy Shaders/Particles/Alpha Blended。在其中可以看到如下代碼:
Tags { "QUEUE"="Transparent" "IGNOREPROJECTOR"="true" "RenderType"="Transparent" "PreviewType"="Plane" }
Pass {
Tags { "QUEUE"="Transparent" "IGNOREPROJECTOR"="true" "RenderType"="Transparent" "PreviewType"="Plane" }
ZWrite Off
Cull Off
Blend SrcAlpha OneMinusSrcAlpha
ColorMask RGB
這裏要了解一個叫做Premultiplied Alpha的概念。也就是預先將Alpha計算進RGB的值中。比如原來是(r,g,b,a), 通過Premultiplied Alpha變爲(ar,ag,ab,a)。Premultiplied Alpha的作用一個是減少後期計算,還有一個更重要的作用是進行Texture Filtering:
Premultiplied Alpha 後的像素格式變得不直觀,因爲在畫圖的時候都是先從調色板中選出一個RGB顏色,再單獨設置透明度,如果RGB乘以透明度就搞不清楚原色是什麼了。從前面的 Alpha Blending 公式可以看出,Premultiplied Alpha 之後,混合的時候可以少一次乘法,這可以提高一些效率,但這並不是最主要的原因。最主要的原因是:
沒有 Premultiplied Alpha 的紋理無法進行 Texture Filtering(除非使用最近鄰插值)。
以最常見的filtering方式線性插值爲例,一個寬2px高1px的圖片,左邊的像素是紅色,右邊是綠色10%透明度,如果把這個圖片縮放到1x1的大小,那麼縮放後1像素的顏色就是左右兩個像素線性插值的結果,也就是把兩個像素各個通道加起來除以2。如果使用沒有Premultiplied Alpha的顏色進行插值,那麼結果就是:
((255,0,0,1)+(0,255,0,0.1))⋅0.5=(127,127,0,0.55)((255,0,0,1)+(0,255,0,0.1))⋅0.5=(127,127,0,0.55)
如果綠色Premultiplied Alpha,也就是 (0, 255 * 0.1, 0, 0.1),和紅色混合後:
((255,0,0,1)+(0,25,0,0.1))⋅0.5=(127,25,0,0.55)((255,0,0,1)+(0,25,0,0.1))⋅0.5=(127,25,0,0.55)
從上面的圖裏第三個顏色是沒有Premultiplied Alpha的混合結果,對比第四個Premultiplied Alpha後顏色的結果,顯然第四個顏色更符合直覺,第三個顏色太綠了,因爲綠色通道沒有乘以透明度,所以在線性插值的時候佔了過大的權重。
所以Premultiplied Alpha最重要的意義是使得帶透明度圖片紋理可以正常的進行線性插值。這樣旋轉、縮放或者非整數的紋理座標才能正常顯示,否則就會像上面的例子一樣,在透明像素邊緣附近產生奇怪的顏色。
GPU專用的紋理格式,比如 PVR、ETC 一般在生成紋理都是默認Premultiplied Alpha的,這些格式一般是GPU硬解碼,引擎用CPU處理會很慢。
原因在於默認的粒子效果使用到的shader中使用了ColorMask RGB
,所以只有RGB三個通道的值被存入了緩衝,而沒有寫入A通道的值, 所以我們得到的texture其實沒有粒子的alpha信息,由於使用了ZWrite Off
,所以也沒有粒子的深度信息,當我們把這張紋理拿出來顯示的時候,由於某些粒子所在位置alpha值爲0,所以通過alpha預存得到的RGBA值是(0, 0, 0, 0),所以最後也就看不到顏色了。
對用的解決辦法是將shader中的ColorMask RGB
改爲ColorMask RGBA
,寫入粒子的alpha信息就行了。
如圖:
參考鏈接
參考鏈接: