UnityShader入门精要第十二章(高斯模糊)

高斯模糊的5x5卷积四方格:

如果使用四方格对整个画面像素进行卷积,计算量为:行像素数 * 宽像素数 * 四方格5x5。

因为高斯模糊四方格的特殊性,可以简化成使用两个一维矩阵分别对行像素和列像素进行相乘,计算量为:行像素数 * 宽像素数 * 2 * 2,且效果一样。

同时该一维矩阵中其实只有三个不同的元素。

float weight[3] = {0.4026,0.2442,0.0545}; 

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];
}

 利用CGINCLUDE少写一次相同的片元着色器

shader完整代码:

Shader "Custom/GaussianBlurShader"
{
    Properties
    {
        _MainTex("Base(RGB)",2D) = "white"{}
        _BlurSize("Blur Size",Float) = 1.0
    }

    SubShader
    {
        CGINCLUDE

        #include "UnityCG.cginc"

        sampler2D _MainTex;
        half4 _MainTex_TexelSize;
        float _BlurSize;

        struct v2f
        {
            float4 pos : SV_POSITION;
            half2 uv[5] : TEXCOORD0;
        };

        v2f vertBlurVertical(appdata_img v)
        {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            half2 uv = v.texcoord;

            //uv【0】在中间
            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;

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


        fixed4 fragBlur(v2f i) : SV_Target 
        {
            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

        ZTest Always Cull Off ZWrite Off 
        Pass {
			NAME "GAUSSIAN_BLUR_VERTICAL"
			
			CGPROGRAM
			  
			#pragma vertex vertBlurVertical  
			#pragma fragment fragBlur
			  
			ENDCG  
		}
		
		Pass {  
			NAME "GAUSSIAN_BLUR_HORIZONTAL"
			
			CGPROGRAM  
			
			#pragma vertex vertBlurHorizontal  
			#pragma fragment fragBlur
			
			ENDCG
		}


    }
    	FallBack "Diffuse"
}

脚本:

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

public class GaussianBlurCS : 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;

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if(material !=null)
        {
            //降采样
            int rtW = source.width / downSample;
            int rtH = source.height / downSample;

            //需要用到两个pass,第一个pass使用竖直方向上的一维滤波核进行滤波,第二个pass使用水平方向上的一维滤波核进行滤波。
            //分配一块屏幕图像大小/downSample的缓存区
            RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);

            //纹理的过滤模式设置为Bilinear
            //Filter Mode(选择纹理在通过 3D 变换拉伸时如何进行过滤):
            //纹理放大变模糊,这个过程需要低通滤波过滤。
            //Point:像素风格,只采样一个像素
            //Bilinear:采样邻近四个像素,进行线性插值混合
            buffer0.filterMode = FilterMode.Bilinear;

            //将第一个pass中的结果存储到buffer0中
            Graphics.Blit(source, buffer0);


            //迭代
            for(int i = 0;i < iterations; i++)
            {

                //设置模糊范围
                material.SetFloat("_BlurSize", 1.00f + i * blurSpread);

                分配一块屏幕图像大小/downSample的缓存区
                RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);

                //将buffer0为输入,buffer1为输出经过第一个pass
                Graphics.Blit(buffer0, buffer1, material, 0);

                //释放buffer0
                RenderTexture.ReleaseTemporary(buffer0);

                //buffer1中的结果赋值给buffer0
                buffer0 = buffer1;

                //buffer1重新申请一块空间
                buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);

                //将buffer0为输入,buffer1为输出经过第二个pass
                Graphics.Blit(buffer0, buffer1, material, 1);

                //释放buffer0
                RenderTexture.ReleaseTemporary(buffer0);
                buffer0 = buffer1;
            }
            //将迭代了几次的buffer0交给结果输出
            Graphics.Blit(buffer0, destination);

            //释放buffer0
            RenderTexture.ReleaseTemporary(buffer0);
        }
        else
        {
            //如果没有这个材质则直接输出,没有任何效果
            Graphics.Blit(source, destination);
        }
    }
}

迭代画图画得有点乱,就着代码食用。

 效果:

blurSpread 和 downSample 都是出于性能的考虑。在高斯核维数不变的情况下,_BlurSize 越大,模糊程度越高 ,但采样数却不会受到影响。但过大的 _BlurSize 值会造成虚影,这可能并不是我们希望的。而 downSample 越大,需要处理的像素数越少,同时也能进一步提高模糊程度,但过大的 downSample 可能会使图像像素化。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值