Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

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

汐夜 2019-07-11 11:55:00 阅读数:146 评论数:0 点赞数:0 收藏数:0

Bloom的原理很简单,主要是提取渲染图像中的亮部区域,并对亮部区域进行模糊处理,再与原始图像混合而成。

 

一般对亮部进行模糊处理的部分采用高斯模糊,关于高斯模糊,详见之前的另一篇博客:

https://www.cnblogs.com/koshio0219/p/11152534.html

 

计算方法:

总共需要用到4个Pass,它们的顺序如下:

Pass 1:得到纹理的亮度值(灰度值),由此计算出亮部区域,传递给一个临时的新纹理,这里叫_Bloom

Pass 2,3:单独对_Bloom进行高斯模糊(纵横),_Bloom纹理更新

Pass 4:混合原始纹理和_Bloom纹理,得到最终效果

 

为了得到更为细致的Bloom效果,建议将游戏的颜色空间由默认的伽马空间转为线性空间,必要时还可开启HDR

 

控制脚本:

 using UnityEngine;
 
 public class BloomCtrl : ScreenEffectBase
 {
     private const string _LuminanceThreshold = "_LuminanceThreshold";
     private const string _BlurSize = "_BlurSize";
     private const string _Bloom = "_Bloom";
 
     [Range(, )]
     public int iterations = ;
     [Range(0.2f, 3.0f)]
     public float blurSize = 0.6f;
     [Range(, )]
     public int dowmSample = ;
     [Range(0.0f, 4.0f)]
     public float luminanceThreshold = 0.6f;//控制Bloom效果的亮度阈值,因为亮度值大多数时不大于1,故该值超过1时一般无效果,但开启HDR后图像的亮度取值范围将扩大
 
     private void OnRenderImage(RenderTexture source, RenderTexture destination)
     {
         if (Material != null)
         {
             Material.SetFloat(_LuminanceThreshold, luminanceThreshold);
 
             int rth = source.height / dowmSample;
             int rtw = source.width / dowmSample;
 
             RenderTexture buffer0 = RenderTexture.GetTemporary(rtw, rth, );
             buffer0.filterMode = FilterMode.Bilinear;
 
             //第1个Pass中提取纹理亮部,存到buffer0中,以便后面进行高斯模糊处理
             Graphics.Blit(source, buffer0,Material,);
 
             for(int i = ; i < iterations; i++)
             {
                 Material.SetFloat(_BlurSize, blurSize*i+1.0f);
 
                 //第2,3个Pass中对亮部分别进行纵向和横向的渲染处理(高斯模糊)
                 RenderTexture buffer1 = RenderTexture.GetTemporary(rtw, rth, );
                 Graphics.Blit(buffer0, buffer1, Material,);
                 RenderTexture.ReleaseTemporary(buffer0);//临时创建的渲染纹理不能直接释放 x: buffer0.Release();
 
                 buffer0 = RenderTexture.GetTemporary(rtw, rth, );
                 Graphics.Blit(buffer1, buffer0, Material, );
                 RenderTexture.ReleaseTemporary(buffer1);
             }
 
             //第4个Pass将buffer0高斯模糊后的结果传给_Bloom以进行最后的混合
             Material.SetTexture(_Bloom, buffer0);
             Graphics.Blit(source,destination,Material,);//注意这里用原始纹理作为源纹理而不是buffer0,因为buffer0已经作为另一个参数进行了传递,而这里还需要原始的纹理以进行混合
             RenderTexture.ReleaseTemporary(buffer0);
         }
         else
             Graphics.Blit(source, destination);
     }
 }

 

Shader脚本:

 Shader "MyUnlit/Bloom"
 {
     Properties
     {
         _MainTex ("Texture", 2D) = "white" {}
         _Bloom("Bloom",2D)="black"{}
         _LuminanceThreshold("Luminance Threshold",Float)=0.5
         _BlurSize("Blur Size",Float)=1.0
     }
     SubShader
     {
         CGINCLUDE
 
         #include "UnityCG.cginc"
 
         sampler2D _MainTex;
         half4 _MainTex_TexelSize;
         sampler2D _Bloom;
         float _LuminanceThreshold;
         float _BlurSize;
 
         struct v2f
         {
            half2 uv : TEXCOORD0;
            float4 pos : SV_POSITION;
         };
 
         struct v2fBloom
         {
            //half4是因为这里还要存储_Bloom纹理
            half4 uv:TEXCOORD0;
            float4 pos:SV_POSITION;
         };
 
         v2f vert(appdata_img v)
         {
            v2f o;
            o.pos=UnityObjectToClipPos(v.vertex);
            o.uv=v.texcoord;    
            return o;
         }
 
         v2fBloom vertBloom(appdata_img v)
         {
            v2fBloom o;
            o.pos=UnityObjectToClipPos(v.vertex);
 
            //xy存储主纹理,zw存储_Bloom纹理,这样不必再申请额外空间
            o.uv.xy=v.texcoord;
            o.uv.zw=v.texcoord;
 
            //纹理坐标平台差异化判断,主要针对DirectX,因为DirectX与OpenGL纹理坐标原点不同(分别在左上和左下)
            //同时Unity平台对于主纹理已经进行过内部处理,因此这里只需要对_Bloom纹理进行平台检测和翻转
            //主要表现为进行y轴方向的翻转(因为y轴方向相反),对于_Bloom纹理来说也就是w
            #if UNITY_UV_STARTS_AT_TOP
            if(_MainTex_TexelSize.y<){
                   o.uv.w=1.0-o.uv.w;
            }
            #endif
 
            return o;
         }
 
         //提取超过亮度阈值的图像
         fixed4 fragExtractBright(v2f i):SV_Target
         {
             fixed4 col=tex2D(_MainTex,i.uv);
             fixed val=clamp(Luminance(col)-_LuminanceThreshold,0.0,1.0);
             return col*val;
         }
 
         //对xy和zw对应的纹理采样进行混合
         fixed4 fragBloom(v2fBloom i):SV_Target
         {
             return tex2D(_MainTex,i.uv.xy)+tex2D(_Bloom,i.uv.zw);
         }
 
         ENDCG
 
         ZTest Always
         Cull Off
         ZWrite Off
 
         //Pass 1:提亮部
         Pass
         {
             CGPROGRAM
             #pragma vertex vert
             #pragma fragment fragExtractBright     
             ENDCG
         }
 
         //Pass 2,3:高斯模糊,这里直接调用以前写的Pass
         UsePass "MyUnlit/GaussianBlur/GAUSSIANBLUR_V"
 
         UsePass "MyUnlit/GaussianBlur/GAUSSIANBLUR_H"
 
         //Pass 4:混合原图和模糊后亮部
         Pass
         {
             CGPROGRAM
             #pragma vertex vertBloom
             #pragma fragment fragBloom
             ENDCG
         }
     }
     Fallback Off
 }

 

效果如下:

 

版权声明
本文为[汐夜]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/koshio0219/p/11169122.html