屏幕后处理——Bloom

来自于《Unity Shader 入门精要》书本的学习

先上图

 

代码分3部分 

1.PostEffectsBase.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//在编辑器状态下可执行该脚本来查看效果
[ExecuteInEditMode] 
//屏幕后处理特效一般需要绑定在摄像机上
[RequireComponent(typeof(Camera))]
public class PostEffectsBase : MonoBehaviour {

    
    void Start () {
        CheckResources();
    }



    protected void CheckResources()
    {
        bool isSupported = CheckSupport();
        //如果显卡检测 返回false
        if (isSupported == false)
        {
            //NotSupported()方法,即不显示
            NotSupported();
        }
    }
    //检查显卡是否支持
   protected bool CheckSupport()
    {
        //如果显卡不支持图像后期处理
        if (SystemInfo.supportsImageEffects == false)
        {
            //返回false
            return false;
        }
        //如果支持图像后处理,返回true
        return true;
    }
    //图像不显示
    protected void NotSupported()
    {
        enabled = false;
    }


    //CheckShaderAndCreateMaterial函数接受两个参数,第一个参数指定了改特效需要使用的Shader
    //第二个参数则是用于后期处理的材质。该函数首先检查Shader的可用性,检查通过后就返回一个使
    //用了该shader的材质,否则返回null.
    protected Material CheckShaderAndCreateMaterial(Shader shader, Material material)
    {   //如果shader为空
        if (shader == null)
        {
            return null;
        }
        //shader.isSupported:能在终端用户的图形卡上运行这个着色器&& 并且存在material 他的shader是我们输入的shader
        if (shader.isSupported && material && material.shader == shader)
        {
            return material;
        }
        
        if (!shader.isSupported)
        {
            return null;
        }
        //上面都不满足的话,重新创建新的材质球
        else
        {
            material = new Material(shader);
            //hideFlags:控制对象销毁的位掩码
            //HideFlags.DontSave对象不会保存到场景中。加载新场景时不会被破坏。
            material.hideFlags = HideFlags.DontSave;
            return material;
        }

    }
  }

2.bloom.using System.Collections;

using System.Collections.Generic;
using UnityEngine;

public class bloom : PostEffectsBase {
    public Shader bloomShader;
    private Material bloomMaterial;
    public Material material
    {
        get
        {
            //根据PostEffectsBase中的方法检测,第一个参数指定了该特效需要使用的Shader,第二个参数则是用于后期处理的材质;
            //该函数首先检查Shader的可用性,检查通过后返回一个使用了该shader的材质,否则返回Null.
            bloomMaterial = CheckShaderAndCreateMaterial(bloomShader, bloomMaterial);
            return bloomMaterial;
        }
    }
  
   //高斯模糊的叠带次数
    [Range(0, 4)]
    public int iterations = 3;

    //高斯模糊的叠带范围
    [Range(0.2f, 4.0f)]
    public float blurSpread = 0.6f;

    //降采样的数值
    [Range(1, 8)]
    public int downSample = 2;

    //luminanceThreshold,大多数情况下图像亮度不会超过1.但如果我们开启了HDR,硬件会允许我们把颜色值储存在一个更高精度范围的缓冲中,
    //此时像素的亮度就会超过1.
    [Range(0.0f, 4.0f)]
    public float luminanceThreshold = 0.6f;



    //OnRenderImage函数
    void OnRenderImage (RenderTexture src , RenderTexture dest)
    {
        if (material != null)
        {
            material.SetFloat("_luminanceThreshold", luminanceThreshold);

            //将图像进行降采样不仅可以减少需要处理的像素,提高性能,而且适当的降采样旺旺还可以得到更好的模糊效果
            int rtW = src.width / downSample;
            int rtH = src.height / downSample;

            //定义第一个缓存buffer0,并吧src中的图像缩放后储存到buffer0中。
            RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
            buffer0.filterMode = FilterMode.Bilinear;

            //调用shader中的第一个Pass提取图像中较亮的区域,提到的较亮区域将储存在buffer0 中。
            Graphics.Blit(src, buffer0, material, 0);

            for (int i = 0; i < iterations; i++)
            {
                material.SetFloat("_BlurSize", 1.0f + i * blurSpread);
                //定义buffer1
                RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
                //调用第二个pass,输入buffer0,输出buffer1.
                Graphics.Blit(buffer0, buffer1, material, 1);

                RenderTexture.ReleaseTemporary(buffer0);
                //将输出的buffer1重新赋值给buffer0
                buffer0 = buffer1;
                buffer1 = RenderTexture.GetTemporary(rtW, rtH,0);

                //调用第三个pass,输入buffer0(上面输出的buffer1),输出buffer1(新的buffer1)
                Graphics.Blit(buffer0, buffer1, material, 2);
                //将新的buffer1再次给buffer0赋值
                buffer0 = buffer1;
            }
            //第四个Pass,将buffer0赋值给贴图_Bloom
            material.SetTexture("_Bloom", buffer0);
            Graphics.Blit(src, dest, material, 3);

            RenderTexture.ReleaseTemporary(buffer0);
        }
        else
        {
            Graphics.Blit(src, dest);
        }
    }

}

3.bloom.shader

Shader "Unlit/Bloom"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        //高斯模糊较亮的区域 
        _Bloom ("Bloom(RGB)",2D) = "Black"{}
        //阔值,提取大于这个亮度的区域 后面将在 大于这个值 的区域里进行高斯模糊
        _luminanceThreshold("luminanceThreshold",Float) = 0.5
        //控制不同迭代之间高斯模糊的模糊区域范围  也就是uv偏移的范围
        _BlurSize ("Blur Size",Float) = 1.0
    }
    SubShader
    {
        CGINCLUDE
         #include "UnityCG.cginc"  
        sampler2D _MainTex;
        half4 _MainTex_TexelSize;
        sampler2D _Bloom;
        Float _luminanceThreshold;
        float _BlurSize;



        ///提取交亮区域的 顶点.片元 着色器
        struct v2f {
        float4 pos :SV_POSITION;
        half2 uv :TEXCOORD0;
        };
        v2f vertExtractBright (appdata_img v)
        {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.uv = v.texcoord;
            return o;
        }
        //通过主贴图得到一个灰度值
        fixed4 luminance (fixed4 color){
            return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
        }
        fixed4 fragExtractBright (v2f i ):SV_Target {
            ///我们降采样得到的亮度值减去阔值_luminanceThreshold,并把结果截取到0到1的范围内,然后
            ///我们把该值和原像素相乘,得到提取后的两部区域
            ///这样就是低于_luminanceThreshold显示为黑色

            fixed c = tex2D(_MainTex , i.uv);
            //clamp(x,a,b),如果x<a,返回a,x>b,返回b,否则返回为x;
            fixed val = clamp (luminance(c) - _luminanceThreshold, 0.0, 1.0);

            return c * val;
        }



        ///使用Unity提供的_MainTex_Texel_TexelSize变量,计算相邻文理坐标的的偏移(也是高斯模糊的写法)
        //竖方向跟横方向的两个顶点着色器公用的v2f输出定义
        struct v2fBlur {
            float4 pos : SV_POSITION;
            half2 uv[5]:TEXCOORD0;
        };
        //竖直方向的
        v2fBlur vertBlurV (appdata_img v){
            v2fBlur o;
            o.pos = UnityObjectToClipPos (v.vertex);
            half2 uv = v.texcoord;

            o.uv[0] = uv;
            o.uv[1] = uv + float2 (0.0,_MainTex_TexelSize.y * 1.0) * _BlurSize;
            o.uv[2] = uv - float2 (0.0,_MainTex_TexelSize.y * 1.0) * _BlurSize;
            o.uv[3] = uv + float2 (0.0,_MainTex_TexelSize.y * 2.0) * _BlurSize;
            o.uv[4] = uv - float2 (0.0,_MainTex_TexelSize.y * 2.0) * _BlurSize;
            
            return o;
        }

        //水平方向的
        v2fBlur vertBlurH (appdata_img v){
            v2fBlur o;
            o.pos = UnityObjectToClipPos (v.vertex);
            half2 uv = v.texcoord;

            o.uv[0] = uv;
            o.uv[1] = uv + float2 (_MainTex_TexelSize.x * 1.0 , 0.0) * _BlurSize;
            o.uv[2] = uv - float2 (_MainTex_TexelSize.x * 1.0 , 0.0) * _BlurSize;
            o.uv[3] = uv + float2 (_MainTex_TexelSize.x * 2.0 , 0.0) * _BlurSize;
            o.uv[4] = uv - float2 (_MainTex_TexelSize.x * 2.0 , 0.0) * _BlurSize;
            
            return o;
        }

        //定义两个pass公用的片元着色器
        fixed4 fragBlur (v2fBlur i) : SV_Target{
        
        float weight [3] = {0.4026, 0.2442, 0.0545};

        fixed3 sum = tex2D (_MainTex,i.uv[0]).rgb * weight[0];

        for (int j = 1; j<3; j++ ){
            sum += tex2D(_MainTex,i.uv[j*2-1]).rgb * weight[j];
            sum += tex2D(_MainTex,i.uv[j*2]).rgb * weight[j];
        }
        return fixed4 (sum,1.0);

        }


        ///混合亮部图像和原图像时使用的 顶点.片元 着色器
        struct v2fBloom {
            float4 pos :SV_POSITION;
            half4 uv :TEXCOORD0;
        };

        v2fBloom vertBloom (appdata_img v){
            v2fBloom  o;
            o.pos = UnityObjectToClipPos (v.vertex);  
            o.uv.xy = v.texcoord;
            o.uv.zw = v.texcoord;

            #if UNITY_UV_STARTS_AT_TOP
            if (_MainTex_TexelSize.y < 0.0)
                o.uv.w = 1.0 - o.uv.w;
            #endif

            return o;
        }
        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 { 
            CGPROGRAM
            #pragma vertex vertExtractBright
            #pragma fragment  fragExtractBright
            ENDCG
        }

        pass {
        
            CGPROGRAM
            #pragma vertex vertBlurV
            #pragma fragment  fragBlur
            ENDCG
        }

        pass {
        
            CGPROGRAM
            #pragma vertex vertBlurH
            #pragma fragment  fragBlur
            ENDCG
        }

        pass {
        
            CGPROGRAM
            #pragma vertex vertBloom
            #pragma fragment  fragBloom
            ENDCG
        }
        
    }
    FallBack Off
}

 

posted @ 2017-10-19 16:14  欧恩Owen  阅读(1006)  评论(0编辑  收藏  举报