Unity Shader 屏幕后效果——Bloom外發光


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 }

 

效果如下:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM