Bloom的原理很簡單,主要是提取渲染圖像中的亮部區域,並對亮部區域進行模糊處理,再與原始圖像混合而成。
一般對亮部進行模糊處理的部分采用高斯模糊,關於高斯模糊,詳見之前的另一篇博客:
https://www.cnblogs.com/koshio0219/p/11152534.html
計算方法:
總共需要用到4個Pass,它們的順序如下:
Pass 1:得到紋理的亮度值(灰度值),由此計算出亮部區域,傳遞給一個臨時的新紋理,這里叫_Bloom
Pass 2,3:單獨對_Bloom進行高斯模糊(縱橫),_Bloom紋理更新
Pass 4:混合原始紋理和_Bloom紋理,得到最終效果
為了得到更為細致的Bloom效果,建議將游戲的顏色空間由默認的伽馬空間轉為線性空間,必要時還可開啟HDR
控制腳本:
1 using UnityEngine; 2 3 public class BloomCtrl : ScreenEffectBase 4 { 5 private const string _LuminanceThreshold = "_LuminanceThreshold"; 6 private const string _BlurSize = "_BlurSize"; 7 private const string _Bloom = "_Bloom"; 8 9 [Range(0, 4)] 10 public int iterations = 3; 11 [Range(0.2f, 3.0f)] 12 public float blurSize = 0.6f; 13 [Range(1, 8)] 14 public int dowmSample = 2; 15 [Range(0.0f, 4.0f)] 16 public float luminanceThreshold = 0.6f;//控制Bloom效果的亮度閾值,因為亮度值大多數時不大於1,故該值超過1時一般無效果,但開啟HDR后圖像的亮度取值范圍將擴大 17 18 private void OnRenderImage(RenderTexture source, RenderTexture destination) 19 { 20 if (Material != null) 21 { 22 Material.SetFloat(_LuminanceThreshold, luminanceThreshold); 23 24 int rth = source.height / dowmSample; 25 int rtw = source.width / dowmSample; 26 27 RenderTexture buffer0 = RenderTexture.GetTemporary(rtw, rth, 0); 28 buffer0.filterMode = FilterMode.Bilinear; 29 30 //第1個Pass中提取紋理亮部,存到buffer0中,以便后面進行高斯模糊處理 31 Graphics.Blit(source, buffer0,Material,0); 32 33 for(int i = 0; i < iterations; i++) 34 { 35 Material.SetFloat(_BlurSize, blurSize*i+1.0f); 36 37 //第2,3個Pass中對亮部分別進行縱向和橫向的渲染處理(高斯模糊) 38 RenderTexture buffer1 = RenderTexture.GetTemporary(rtw, rth, 0); 39 Graphics.Blit(buffer0, buffer1, Material,1); 40 RenderTexture.ReleaseTemporary(buffer0);//臨時創建的渲染紋理不能直接釋放 x: buffer0.Release(); 41 42 buffer0 = RenderTexture.GetTemporary(rtw, rth, 0); 43 Graphics.Blit(buffer1, buffer0, Material, 2); 44 RenderTexture.ReleaseTemporary(buffer1); 45 } 46 47 //第4個Pass將buffer0高斯模糊后的結果傳給_Bloom以進行最后的混合 48 Material.SetTexture(_Bloom, buffer0); 49 Graphics.Blit(source,destination,Material,3);//注意這里用原始紋理作為源紋理而不是buffer0,因為buffer0已經作為另一個參數進行了傳遞,而這里還需要原始的紋理以進行混合 50 RenderTexture.ReleaseTemporary(buffer0); 51 } 52 else 53 Graphics.Blit(source, destination); 54 } 55 }
基類腳本見:
https://www.cnblogs.com/koshio0219/p/11131619.html
Shader腳本:
1 Shader "MyUnlit/Bloom" 2 { 3 Properties 4 { 5 _MainTex ("Texture", 2D) = "white" {} 6 _Bloom("Bloom",2D)="black"{} 7 _LuminanceThreshold("Luminance Threshold",Float)=0.5 8 _BlurSize("Blur Size",Float)=1.0 9 } 10 SubShader 11 { 12 CGINCLUDE 13 14 #include "UnityCG.cginc" 15 16 sampler2D _MainTex; 17 half4 _MainTex_TexelSize; 18 sampler2D _Bloom; 19 float _LuminanceThreshold; 20 float _BlurSize; 21 22 struct v2f 23 { 24 half2 uv : TEXCOORD0; 25 float4 pos : SV_POSITION; 26 }; 27 28 struct v2fBloom 29 { 30 //half4是因為這里還要存儲_Bloom紋理 31 half4 uv:TEXCOORD0; 32 float4 pos:SV_POSITION; 33 }; 34 35 v2f vert(appdata_img v) 36 { 37 v2f o; 38 o.pos=UnityObjectToClipPos(v.vertex); 39 o.uv=v.texcoord; 40 return o; 41 } 42 43 v2fBloom vertBloom(appdata_img v) 44 { 45 v2fBloom o; 46 o.pos=UnityObjectToClipPos(v.vertex); 47 48 //xy存儲主紋理,zw存儲_Bloom紋理,這樣不必再申請額外空間 49 o.uv.xy=v.texcoord; 50 o.uv.zw=v.texcoord; 51 52 //紋理坐標平台差異化判斷,主要針對DirectX,因為DirectX與OpenGL紋理坐標原點不同(分別在左上和左下) 53 //同時Unity平台對於主紋理已經進行過內部處理,因此這里只需要對_Bloom紋理進行平台檢測和翻轉 54 //主要表現為進行y軸方向的翻轉(因為y軸方向相反),對於_Bloom紋理來說也就是w 55 #if UNITY_UV_STARTS_AT_TOP 56 if(_MainTex_TexelSize.y<0){ 57 o.uv.w=1.0-o.uv.w; 58 } 59 #endif 60 61 return o; 62 } 63 64 //提取超過亮度閾值的圖像 65 fixed4 fragExtractBright(v2f i):SV_Target 66 { 67 fixed4 col=tex2D(_MainTex,i.uv); 68 fixed val=clamp(Luminance(col)-_LuminanceThreshold,0.0,1.0); 69 return col*val; 70 } 71 72 //對xy和zw對應的紋理采樣進行混合 73 fixed4 fragBloom(v2fBloom i):SV_Target 74 { 75 return tex2D(_MainTex,i.uv.xy)+tex2D(_Bloom,i.uv.zw); 76 } 77 78 ENDCG 79 80 ZTest Always 81 Cull Off 82 ZWrite Off 83 84 //Pass 1:提亮部 85 Pass 86 { 87 CGPROGRAM 88 #pragma vertex vert 89 #pragma fragment fragExtractBright 90 ENDCG 91 } 92 93 //Pass 2,3:高斯模糊,這里直接調用以前寫的Pass 94 UsePass "MyUnlit/GaussianBlur/GAUSSIANBLUR_V" 95 96 UsePass "MyUnlit/GaussianBlur/GAUSSIANBLUR_H" 97 98 //Pass 4:混合原圖和模糊后亮部 99 Pass 100 { 101 CGPROGRAM 102 #pragma vertex vertBloom 103 #pragma fragment fragBloom 104 ENDCG 105 } 106 } 107 Fallback Off 108 }
效果如下: