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

て烟熏妆下的殇ゞ 提交于 2021-01-24 00:15:05

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 }

 

效果如下:

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!