《Unity Shader》12.4.2 实现

本节,我们将会使用上述5×5的高斯核对原图像进行高斯模糊。为此,我们需要进行如下准备工作。


(1)新建一个场景。在本书资源中,该场景名为Scene_12_4。在Unity 5.2中,默认情况下场景将包含一个摄像机和一个平行光,并且使用了内置的天空盒子。在Window → Lighting →Skybox中去掉场景中的天空盒子。

(2)把本书资源中的Assets/Textures/Chapter12/Sakura1.jpg拖曳到场景中,并调整的位置使其可以填充整个场景。注意,Sakura1.jpg的纹理类型已被设置为Sprite,因此可以直接拖曳到场景中。

https://github.com/candycat1992/Unity_Shaders_Book/blob/master/Assets/Textures/Chapter12/sakura1.jpg

(3)新建一个脚本。在本书资源中,该脚本名为GaussianBlur.cs。把该脚本拖曳到摄像机上。

(4)新建一个Unity Shader。在本书资源中,该Shader名为Chapter12-GaussianBlur。

我们首先来编写GaussianBlur.cs脚本。打开该脚本,并进行如下修改。

(1)首先,继承12.1节中创建的基类:

(2)声明该效果需要的Shader,并据此创建相应的材质:

(3)在脚本中,我们还提供了调整高斯模糊迭代次数、模糊范围和缩放系数的参数:

(4)最后,我们需要定义关键的OnRenderImage函数。我们首先来看第一个版本,也就是最简单的OnRenderImage的实现:

(5)在理解了上述代码后,我们可以实现第二个版本的OnRenderImage函数。在这个版本中,我们将利用缩放对图像进行降采样,从而减少需要处理的像素个数,提高性能。

(6)最后一个版本的代码还考虑了高斯模糊的迭代次数:

https://github.com/candycat1992/Unity_Shaders_Book/blob/master/Assets/Scripts/Chapter12/GaussianBlur.cs

cs 复制代码
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;
        }
    }
    // Blur  iterations  -  larger  number  means  more  blur. 高斯模糊迭代次数
    [Range(0,  4)]
    public  int  iterations  =  3;

    // Blur  spread  for  each  iteration  -  larger 模糊范围
    [Range(0.2f,  3.0f)]
    public  float  blurSpread  =  0.6f;

    //缩放系数
    [Range(1,  8)]
    public  int  downSample  =  2;

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

    /* 第一版
    void  OnRenderImage(RenderTexture  src, RenderTexture  dest)  {
        if  (material  !=  null)  { 
            int rtW = src.width; //rendertexturewidth
            int rtH = src.height; //rendertexturewidth
            RenderTexture  buffer  =  RenderTexture.GetTemporary(rtW,  rtH,  0); //分配了一块与屏幕图像大小相同的缓冲区,高斯模糊需要调用两个Pass,我们需要使用一块中间缓存来存储第一个Pass执行完毕后得到的模糊结果

            //  Render  the  vertical  pass
            Graphics.Blit(src,  buffer,  material,  0); //使用Shader中的第一个Pass(即使用竖直方向的一维高斯核进行滤波)对src进行处理,并将结果存储在了buffer中
            //  Render  the  horizontal  pass
            Graphics.Blit(buffer,  dest,  material,  1); //调用Graphics.Blit(buffer, dest, material, 1),使用Shader中的第二个Pass(即使用水平方向的一维高斯核进行滤波)对buffer进行处理,返回最终的屏幕图像
            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); 
            buffer.filterMode  =  FilterMode.Bilinear;
            //第一个版本代码不同的是,我们在声明缓冲区的大小时,使用了小于原屏幕分辨率的尺寸,并将该临时渲染纹理的滤波模式设置为双线性
            //图像进行降采样

           
            Graphics.Blit(src,  buffer,  material,  0); 
            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  buffer0  =  RenderTexture.GetTemporary(rtW,  rtH,  0); 
            buffer0.filterMode  =  FilterMode.Bilinear;
            //第一个版本代码不同的是,我们在声明缓冲区的大小时,使用了小于原屏幕分辨率的尺寸,并将该临时渲染纹理的滤波模式设置为双线性
            //图像进行降采样

            Graphics.Blit(src,  buffer0);
            for  (int  i  =  0;  i  <  iterations;  i++){
                material.SetFloat("_BlurSize",  1.0f  +  i  *  blurSpread);
                RenderTexture  buffer1  =  RenderTexture.GetTemporary(rtW,  rtH,  0);
                //  Render  the  vertical  pass
                Graphics.Blit(buffer0,  buffer1,  material,  0);
                RenderTexture.ReleaseTemporary(buffer0);
                buffer0  =  buffer1;
                buffer1  =  RenderTexture.GetTemporary(rtW,  rtH,  0);
                //  Render  the  horizontal  pass
                Graphics.Blit(buffer0,  buffer1,  material,  1);
                RenderTexture.ReleaseTemporary(buffer0);
                buffer0  =  buffer1;
            }

            Graphics.Blit(buffer0,  dest);
            RenderTexture.ReleaseTemporary(buffer0);
        } else {
            Graphics.Blit(src,  dest);
        }
        //在迭代开始前,我们首先定义了第一个缓存buffer0,并把src中的图像缩放后存储到buffer0中
        //在迭代过程中,我们又定义了第二个缓存buffer1
        //执行第一个Pass时,输入是buffer0,输出是buffer1,完毕后首先把buffer0释放,再把结果值buffer1存储到buffer0中,重新分配buffer1
        //然后再调用第二个Pass,重复上述过程
        //迭代完成后,buffer0将存储最终的图像,我们再利用Graphics.Blit(buffer0, dest)把结果显示到屏幕上,并释放缓存。
    }



    
}

下面,我们来实现Shader的部分。打开Chapter12-GaussianBlur,进行如下修改。

(1)我们首先需要声明本例使用的各个属性:

2)在本节中,我们将第一次使用CGINCLUDE来组织代码。我们在SubShader块中利用CGINCLUDE和ENDCG语义来定义一系列代码:

(3)在CG代码块中,定义与属性对应的变量:

(4)分别定义两个Pass使用的顶点着色器。下面是竖直方向的顶点着色器代码:

(5)定义两个Pass共用的片元着色器:

(6)然后,我们定义了高斯模糊使用的两个Pass:

(7)最后,关闭该Shader的Fallback:

完成后返回编辑器,并把Chapter12-GaussianBlur拖曳到摄像机的GaussianBlur.cs脚本中的gaussianBlurShader参数中。当然,我们可以在GaussianBlur.cs的脚本面板中将gaussianBlurShader参数的默认值设置为Chapter12-GaussianBlur,这样就不需要以后使用时每次都手动拖曳了。

https://github.com/candycat1992/Unity_Shaders_Book/blob/master/Assets/Shaders/Chapter12/Chapter12-GaussianBlur.shader

cs 复制代码
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Custom/Chapter12-GaussianBlur"
{
    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;

            o.uv[0] = uv;
            o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize; //属性_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;
        }

        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" //为Pass定义名字,可以在其他Shader中直接通过它们的名字来使用该Pass,而不需要再重复编写代码。
            CGPROGRAM
            #pragma vertex vertBlurVertical  
			#pragma fragment fragBlur

            ENDCG  
        }
        Pass {  
			NAME "GAUSSIAN_BLUR_HORIZONTAL"
            CGPROGRAM  
            #pragma vertex vertBlurHorizontal  
			#pragma fragment fragBlur
            ENDCG
        }
    }
    FallBack "Diffuse"
}
相关推荐
sindyra3 小时前
Unity UGUI 之 Canvas Scaler
unity·游戏引擎
在路上看风景7 小时前
2.Square Grid
unity
程序猿阿伟7 小时前
《突破Unity热更新瓶颈:底层函数调用限制与生态适配秘籍》
unity·游戏引擎
龙智DevSecOps解决方案9 小时前
Perforce《2025游戏技术现状报告》Part 3:不同行业挑战以及Unreal、Godot、自研游戏引擎的应用趋势
游戏引擎·godot·游戏开发·perforce
在路上看风景9 小时前
13. UGUI合批
unity
jtymyxmz1 天前
《Unity Shader》12.2调整屏幕的亮度、饱和度和对比度
unity·游戏引擎
AllBlue1 天前
unity嵌入安卓界面,如何显示状态
android·unity·游戏引擎
tealcwu1 天前
【Unity技巧】实现在Play时自动保存当前场景
java·unity·游戏引擎
tealcwu1 天前
【Unity基础】实现Scroll View跟随动态内容滚动
java·unity·游戏引擎