UnityShader高级篇——高斯模糊

1.此代码放到Camera上

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GaussianBlur : PostEffectsBase
{
    public Shader GaussianBlurShader;
    private Material _gaussianBlurMaterial = null;

    public Material Material
    {
        get
        {
            _gaussianBlurMaterial = CheckShaderAndCreateMaterial(GaussianBlurShader, _gaussianBlurMaterial);
            return _gaussianBlurMaterial;
        }
    }

    //模糊迭代次数,数值越大越模糊
    [Range(0, 4)] public int Iterations = 3;
    //每次迭代的模糊范围,数值越大越模糊,但过大会造成虚影
    [Range(0.2f, 3.0f)] public float BlurSpread = 0.6f;
    //缩放系数,数值越大越模糊,需要处理的像素越少,但过大会使图像像素化
    [Range(1, 8)] public int DownSample = 2;

    /* //版本一,只应用模糊
     void OnRenderImage(RenderTexture src, RenderTexture dest)
     {
         if (Material != null)
         {
             int rtW = src.width;
             int rtH = src.height;
             //使用RenderTexture.GetTemporary函数分配了一块与屏幕图像大小相同的缓冲区
             //高斯模糊需要调用两个Pass,需要使用缓冲区来存储第一个Pass执行完毕后的模糊结果
             RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);
             //使用第一个Pass(竖直方向的一维高斯核)处理src,并存储再buffer中,最后一个参数是Pass的索引
             Graphics.Blit(src, buffer, Material, 0);
             //使用第二个Pass(水平方向的一维高斯核)处理buffer,并输出最终结果,最后一个参数是Pass的索引
             Graphics.Blit(buffer, dest, Material, 1);
             //释放之前分配的缓存
             RenderTexture.ReleaseTemporary(buffer);
         }
         else
         {
             Graphics.Blit(src, dest);
         }
     }
     */

   /* //版本二,利用缩放对图像进行降采样,减少需要处理的像素个数,提高性能
    //适当降采样可以得到更好的模糊效果,但过大的缩放系数可能会造成图像像素化
    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        if (Material != null)
        {
            //利用缩放系数,使用小于屏幕分辨率的尺寸
            int rtW = src.width / DownSample;
            int rtH = src.height / DownSample;
            RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);
            //设置临时渲染纹理的滤波模式为双线性
            //这样在调用第一个Pass时,我们需要处理的像素个数就是原来的几分之一
            buffer.filterMode = FilterMode.Bilinear;

            //调用第一个Pass(竖直方向)处理src,并存储在缓冲区
            Graphics.Blit(src, buffer, Material, 0);
            //调用第二个Pass(水平方向)处理buffer,并输出最终效果
            Graphics.Blit(buffer, dest, Material, 1);

            //释放缓冲区
            RenderTexture.ReleaseTemporary(buffer);
        }
        else
        {
            Graphics.Blit(src, dest);
        }
    }
    */

    //版本三,考虑高斯模糊的迭代次数,使用两个缓存,循环调用Pass
    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        if (Material != null)
        {
            int rtW = src.width / DownSample;
            int rtH = src.height / DownSample;

            //定义第一个缓存
            RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
            buffer0.filterMode = FilterMode.Bilinear;
            //图像缩放后存储入缓存中
            Graphics.Blit(src, buffer0);

            //循环迭代
            for (int i = 0; i < Iterations; i++)
            {
                //将迭代的模糊范围穿给shader
                Material.SetFloat("_BlurSize", 1.0f + i * BlurSpread);

                //定义第二个缓存
                RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);

                //执行第一个Pass, 输入buffer0, 输出buffer1
                Graphics.Blit(buffer0, buffer1, Material, 0);
                //释放buffer0
                RenderTexture.ReleaseTemporary(buffer0);
                //将buffer1存储到buffer0
                buffer0 = buffer1;
                //重新分配buffer1
                buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);

                //执行第二个pass,输入buffer0,输出buffer1
                Graphics.Blit(buffer0, buffer1, Material, 1);
                //释放buffer0
                RenderTexture.ReleaseTemporary(buffer0);
                //将buffer1存储入buffer0
                buffer0 = buffer1;
            }

            //迭代完成后,最终值存储在buffer0中,输出最终值
            Graphics.Blit(buffer0, dest);
            //释放buffer0
            RenderTexture.ReleaseTemporary(buffer0);
        }
        else
        {
            Graphics.Blit(src, dest);
        }
    }


}

2.此Shader赋值给上面的代码

Shader "UnityShaderBook/Chapter12/GaussianBlur"
{
	Properties
	{
		_MainTex ("Base(RGB)", 2D) = "white" {}
		_BlurSize("Blur Size", Float) = 1.0
	}
		SubShader
		{
			//使用CGINCLUDE & ENDCG组织代码,这些代码不需要包含在任何Pass中
			//使用时,只需要在Pass中直接制定需要使用的顶点着色器和片元着色器函数名即可
			//由于高斯模糊需要定义两个Pass,但他们的片元着色器代码时相同的,使用CGINDCLUDE可以避免编写两个完全一样的frag函数
			CGINCLUDE

			#include "UnityCG.cginc"

			//定义与属性对应的变量
			sampler2D _MainTex;
		    //计算相邻像素的纹理坐标偏移量,为得到相邻像素的纹理坐标
		    half4 _MainTex_TexelSize;
			float _BlurSize;

			//分别定义两个Pass使用的顶点着色器	
			struct v2f {
				float4 pos : SV_POSITION;
				//使用5*5的高斯核,二维高斯核会被拆分成两个大小为5的一维高斯核,所以只需要计算5个纹理坐标
				half2 uv[5] : TEXCOORD0;
			};

			//竖直方向的顶点着色器代码
			v2f vertBlurVertical(appdata_img v) {
				v2f 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;
			}

			//水平方向的顶点着色器代码
			v2f vertBlurHorizontal(appdata_img v) {
				v2f 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(v2f i) : SV_Target{
				//由于5*5的高斯核可以拆分成两个大小为5的一维高斯核,并且由于对称性,所以只需要记录3个高斯权重
				float weight[3] = {0.4026,0.2442,0.0545};
			    //初始化滤波值,当前像素值乘以它的权重值
			    fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
				//根据对称性,进行了两次迭代,每次迭代包含了两次纹理采样,并把像素值和权重相乘后的结果叠加到滤波值中
				for (int it = 1; it < 3; it++) {
					sum += tex2D(_MainTex, i.uv[it * 2 - 1]).rgb * weight[it];
					sum += tex2D(_MainTex, i.uv[it * 2]).rgb * weight[it];
				}
				return fixed4(sum, 1.0);
			}
		ENDCG

	    //定义高斯模糊使用的两个Pass
		//设置渲染状态
		ZTest Always Cull Off ZWrite Off

		Pass
		{
			//定义了名字,在其他Shader中可以直接通过名字来使用该Pass,不需要再重复写代码
			NAME"GAUSSIAN_BLUR_VERTICAL"
			CGPROGRAM

			#pragma vertex vertBlurVertical
			#pragma fragment fragBlur
		
			ENDCG
		}

		Pass
		{
			NAME"GAUSSIAN_BLUR_HORIZONTAL"
			CGPROGRAM

            #pragma vertex vertBlurVertical
			#pragma fragment fragBlur

			ENDCG
		}
	}

	Fallback "Diffuse"
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值