75.深度纹理实现运动模糊具体实现

  1. 75.深度和法线纹理-效果实现-深度纹理实现运动模糊-具体实现
    1. 75.1 知识点
      1. 知识回顾
      2. 补充知识点:C# 设置矩阵变量给 Shader
      3. 实现 利用深度纹理实现运动模糊屏幕后期处理效果 对应 Shader
        1. 主要步骤
        2. 新建Shader文件,取名 MotionBlurWithDepthTexture 深度纹理运动模糊效果,保留骨架
        3. 声明属性,进行属性映射。包括主纹理和模糊偏移量、深度纹理、当前帧裁剪到世界空间变换矩阵和上一帧世界到裁剪空间变换矩阵。注意深度纹理的命名规范为_CameraDepthTexture,名字不一致的话无法正确获得深度值。
        4. 屏幕后处理标配指令
        5. 结构体声明顶点和 UV 坐标
        6. 顶点函数对坐标进行转换和对uv坐标赋值
        7. 片元函数中,得到裁剪空间下的两个点,作差得到运动方向,最后基于运动方向进行模糊处理
        8. 不使用回退Shader,即此Shader无法运行时不会切换到其他Shader
      4. 实现 利用深度纹理实现运动模糊屏幕后期处理效果 对应 C#
        1. 主要步骤
        2. 创建C#代码,命名和Shader一样,继承屏幕后处理基类PostEffectBase,声明模糊偏移量变量和用于记录上一次变换矩阵的变量
        3. 重写OnRenderImage函数,在其中进行属性设置,变换矩阵计算,屏幕后处理。注意在Start中启用深度纹理,OnEnable时初始化上一帧变换矩阵避免上一变换矩阵为空
        4. 挂载脚本到摄像机并关联 Shader,观察运动模糊效果
      5. 其他注意点
        1. 考虑不同平台可能存在的垂直翻转问题,使用纹素判断是否要翻转。同时深度采样时用纹素算出来的深度去采样深度纹理,当然主纹理还用原来成uv
        2. 片元函数中让移动方向向量除以2,从而降低运动模糊效果的强度,不要过于强烈
    2. 75.2 知识点代码
      1. Lesson75_深度和法线纹理_效果实现_深度纹理实现运动模糊_具体实现.cs
      2. Lesson75_MotionBlurWithDepthTexture.shader
      3. Lesson75_MotionBlurWithDepthTexture.cs

75.深度和法线纹理-效果实现-深度纹理实现运动模糊-具体实现


75.1 知识点

知识回顾

利用深度纹理实现运动模糊效果的基本原理是:
通过获取像素当前帧和上一帧在裁剪空间下的位置,利用这两个位置计算物体的运动方向,从而模拟出运动模糊的效果。

补充知识点:C# 设置矩阵变量给 Shader

  • 我们需要通过 C# 代码为 Shader 设置矩阵变量。然而,ShaderLab 语法中的属性并没有直接的矩阵类型变量,因此,我们在 CG 语句中声明矩阵属性,通过材质球指明变量进行设置。

  • 示例:

    • 在 CG 代码中声明 4x4 矩阵:float4x4 _ClipToWorldMatrix;
    • 在 C# 代码中声明 4x4 矩阵:Matrix4x4 frontClipToWorldMatrix;
    • 通过材质指明变量设置矩阵:material.SetMatrix("_ClipToWorldMatrix", frontClipToWorldMatrix);

实现 利用深度纹理实现运动模糊屏幕后期处理效果 对应 Shader

主要步骤

  1. 新建Shader文件,取名 MotionBlurWithDepthTexture 深度纹理运动模糊效果

  2. 声明属性,进行属性映射

    • 主纹理 _MainTex
    • 模糊偏移量 _BlurSize
    • 深度纹理 _CameraDepthTexture
    • 当前帧裁剪到世界空间变换矩阵 float4x4 _ClipToWorldMatrix
    • 上一帧世界到裁剪空间变换矩阵 float4x4 _FrontWorldToClipMatrix
  3. 屏幕后处理标配

    • ZTest Always
    • Cull Off
    • ZWrite Off
  4. 结构体

    • 顶点和uv坐标
  5. 顶点着色器

    • 坐标转换 uv坐标赋值
  6. 片元着色器

    1. 得到裁剪空间下的两个点
      • 得到点一
      • 深度值获取
      • 构建裁剪空间下组合坐标 uv 和 深度
      • 得到点二
      • 裁剪空间坐标转世界空间(注意进行齐次除法)
      • 利用上一帧变换矩阵将世界空间坐标转裁剪空间(注意进行齐次除法)
    2. 得到运动方向
      • 用当前帧点 - 上一帧点 得到运动方向
    3. 进行模糊处理
      • 利用模糊偏移量变量进行3次偏移采样颜色后进行平均值计算
  7. FallBack Off

新建Shader文件,取名 MotionBlurWithDepthTexture 深度纹理运动模糊效果,保留骨架

Shader "Unlit/Lesson75_MotionBlurWithDepthTexture"
{
    Properties
    {
        
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            ENDCG
        }
    }
}

声明属性,进行属性映射。包括主纹理和模糊偏移量、深度纹理、当前帧裁剪到世界空间变换矩阵和上一帧世界到裁剪空间变换矩阵。注意深度纹理的命名规范为_CameraDepthTexture,名字不一致的话无法正确获得深度值。

Properties
{
    // 主纹理属性,默认白色
    _MainTex ("Texture", 2D) = "white" {}
    //用于控制模糊程度的 模糊偏移量
    _BlurSize("BlurSize", Float) = 0.5
}
// 主纹理采样器
sampler2D _MainTex;
// 主纹理每个纹素的大小信息,用于一些纹理坐标相关的计算
float4 _MainTex_TexelSize;
// 存储模糊偏移量,用于控制模糊程度
fixed _BlurSize;
// 深度纹理采样器,用于获取场景深度信息
sampler2D _CameraDepthTexture;
// 裁剪空间到世界空间的变换矩阵,用于坐标变换
float4x4 _ClipToWorldMatrix;
// 上一帧世界空间到裁剪空间的变换矩阵,用于获取上一帧相关坐标信息
float4x4 _FrontWorldToClipMatrix;

屏幕后处理标配指令

// 始终进行深度测试
ZTest Always
// 关闭背面剔除
Cull Off
// 关闭深度写入
ZWrite Off

结构体声明顶点和 UV 坐标

struct v2f
{
    // 主纹理的纹理坐标,用于在片元着色器中采样主纹理
    float2 uv : TEXCOORD0;
    // 顶点在裁剪空间中的位置,用于后续渲染等操作
    float4 vertex : SV_POSITION;
};

顶点函数对坐标进行转换和对uv坐标赋值

// 顶点着色器函数
v2f vert(appdata_base appdata_base)
{
    v2f v2f;
    
    // 将顶点从模型空间转换到裁剪空间
    v2f.vertex = UnityObjectToClipPos(appdata_base.vertex);
    
    // 直接赋值主纹理的原始纹理坐标
    v2f.uv = appdata_base.texcoord;
        
    return v2f;
}

片元函数中,得到裁剪空间下的两个点,作差得到运动方向,最后基于运动方向进行模糊处理

// 片元着色器函数
fixed4 frag(v2f v2f) : SV_Target
{
    // 1. 得到裁剪空间下的两个点相关操作
    
    // 获取深度纹理在当前像素位置的深度值 这个深度值是裁剪空间下的
    float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, v2f.uv);
    
    // 构建裁剪空间下的一个组合坐标,将0~1范围的纹理坐标变换到-1~1范围
    // 此为当前帧的第一个点在裁剪空间中的坐标表示
    float4 nowClipPos = float4(v2f.uv.x * 2 - 1, v2f.uv.y * 2 - 1, depth * 2 - 1, 1);
    
    // 通过裁剪空间到世界空间的变换矩阵,将当前帧裁剪空间的点转换到世界空间,得到世界空间的坐标
    float4 worldPos = mul(_ClipToWorldMatrix, nowClipPos);
    
    // 进行透视的齐次除法,得到正确的世界空间坐标 否则w不是1的话可能不是正确的坐标
    worldPos /= worldPos.w;
    
    // 利用上一帧的变换矩阵,将世界空间的点转换回上一帧对应的裁剪空间下的点
    // 此为上一帧的第二个点在裁剪空间中的坐标表示
    float4 oldClipPos = mul(_FrontWorldToClipMatrix, worldPos);
    
    // 对上一帧的点进行透视的齐次除法
    oldClipPos /= oldClipPos.w;

    // 2. 计算得到运动方向向量
    // 我们只想得到屏幕上的移动方向 所以是2维的 使用uv采样只需要2维即可
    float2 moveDir = nowClipPos.xy - oldClipPos.xy;

    // 3. 基于运动方向进行模糊处理操作
    float2 uv = v2f.uv;
    float4 color = float4(0, 0, 0, 0);
    for (int it = 0; it < 3; it++)
    {
        // 循环内进行多次纹理采样并累加颜色
        // 第一次采样累加的是当前像素所在位置的颜色
        // 第二次采样累加的是当前像素进行了 moveDir * _BlurSize 偏移后的颜色
        // 第三次采样累加的是当前像素进行了 2*moveDir * _BlurSize 偏移后的颜色
        color += tex2D(_MainTex, uv);
        uv += moveDir * _BlurSize;
    }
    // 计算累加3次后颜色的平均值,实现模糊效果
    color /= 3;
    
    // 返回经过模糊处理后的颜色值
    return fixed4(color.rgb, 1);
}

不使用回退Shader,即此Shader无法运行时不会切换到其他Shader

// 不使用回退Shader,即此Shader无法运行时不会切换到其他Shader
Fallback Off

实现 利用深度纹理实现运动模糊屏幕后期处理效果 对应 C#

主要步骤

  1. 创建C#代码,命名和Shader一样
  2. 继承屏幕后处理基类PostEffectBase
  3. 声明模糊偏移量变量和用于记录上一次变换矩阵的变量
  4. 重写OnRenderImage函数,在其中进行属性设置,变换矩阵计算,屏幕后处理
  5. 在生命周期函数中启用深度纹理,初始化上一帧变换矩阵

创建C#代码,命名和Shader一样,继承屏幕后处理基类PostEffectBase,声明模糊偏移量变量和用于记录上一次变换矩阵的变量

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

public class Lesson75_MotionBlurWithDepthTexture : PostEffectBase
{
    //模糊程度
    [Range(0, 1)] public float blurSize = 0.5f;

    //用于记录上一次的变换矩阵的变量
    private Matrix4x4 frontWorldToClipMatrix;

    protected override void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (material != null)
        {
            //设置模糊程度
            material.SetFloat("_BlurSize", blurSize);
            //设置上一帧世界空间到裁剪空间的矩阵
            material.SetMatrix("_FrontWorldToClipMatrix", frontWorldToClipMatrix);
            //计算这一帧的变换矩阵
            frontWorldToClipMatrix = Camera.main.projectionMatrix * Camera.main.worldToCameraMatrix;
            //设置这一帧的 裁剪到世界空间的变换矩阵
            material.SetMatrix("_ClipToWorldMatrix", frontWorldToClipMatrix.inverse);
            //进行屏幕后期处理
            Graphics.Blit(source, destination, material);
        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }
}

重写OnRenderImage函数,在其中进行属性设置,变换矩阵计算,屏幕后处理。注意在Start中启用深度纹理,OnEnable时初始化上一帧变换矩阵避免上一变换矩阵为空

private void Start()
{
    //开启深度纹理
    Camera.main.depthTextureMode = DepthTextureMode.Depth;
    //初始化上一次的变换矩阵 用 观察到裁剪变换矩阵(摄像机的透视矩阵) * 世界到观察变换矩阵
    //得到的 就是 世界空间到裁剪空间的变换矩阵
    //frontWorldToClipMatrix = Camera.main.projectionMatrix * Camera.main.worldToCameraMatrix;
}

private void OnEnable()
{
    //有时我们会在界面上让脚本失活,每次激活时 可以初始化一次
    frontWorldToClipMatrix = Camera.main.projectionMatrix * Camera.main.worldToCameraMatrix;
}

protected override void OnRenderImage(RenderTexture source, RenderTexture destination)
{
    if (material != null)
    {
        //设置模糊程度
        material.SetFloat("_BlurSize", blurSize);
        //设置上一帧世界空间到裁剪空间的矩阵
        material.SetMatrix("_FrontWorldToClipMatrix", frontWorldToClipMatrix);
        //计算这一帧的变换矩阵
        frontWorldToClipMatrix = Camera.main.projectionMatrix * Camera.main.worldToCameraMatrix;
        //设置这一帧的 裁剪到世界空间的变换矩阵
        material.SetMatrix("_ClipToWorldMatrix", frontWorldToClipMatrix.inverse);
        //进行屏幕后期处理
        Graphics.Blit(source, destination, material);
    }
    else
    {
        Graphics.Blit(source, destination);
    }
}

挂载脚本到摄像机并关联 Shader,观察运动模糊效果

其他注意点

考虑不同平台可能存在的垂直翻转问题,使用纹素判断是否要翻转。同时深度采样时用纹素算出来的深度去采样深度纹理,当然主纹理还用原来成uv

Shader "Unlit/Lesson75_MotionBlurWithDepthTexture"
{
    Properties
    {
        //...
    }
    SubShader
    {
        Pass
        {
            //...

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            // 主纹理采样器
            sampler2D _MainTex;
            // 主纹理每个纹素的大小信息,用于一些纹理坐标相关的计算
            float4 _MainTex_TexelSize;
            //...

            struct v2f
            {
                // 主纹理的纹理坐标,用于在片元着色器中采样主纹理
                float2 uv : TEXCOORD0;
                // 深度纹理的纹理坐标,用于在片元着色器中采样深度纹理
                float2 uv_depth : TEXCOORD1;
                // 顶点在裁剪空间中的位置,用于后续渲染等操作
                float4 vertex : SV_POSITION;
            };

            // 顶点着色器函数
            v2f vert(appdata_base appdata_base)
            {
                v2f v2f;
                
                // 将顶点从模型空间转换到裁剪空间
                v2f.vertex = UnityObjectToClipPos(appdata_base.vertex);
                
                // 直接赋值主纹理的原始纹理坐标
                v2f.uv = appdata_base.texcoord;
                
                // 同样赋值深度纹理的原始纹理坐标
                v2f.uv_depth = appdata_base.texcoord;
                
                // 在多平台情况下,根据纹理坐标起始位置进行调整,保证深度纹理采样正确
                // 当纹理坐标在顶部开始时且主纹理纹素高度小于0,翻转深度纹理的y坐标
                #if UNITY_UV_STARTS_AT_TOP
                if (_MainTex_TexelSize.y < 0)
                    v2f.uv_depth.y = 1 - v2f.uv_depth.y;
                #endif

                return v2f;
            }

            // 片元着色器函数
            fixed4 frag(v2f v2f) : SV_Target
            {
                // 1. 得到裁剪空间下的两个点相关操作
                
                // 获取深度纹理在当前像素位置的深度值 这个深度值是裁剪空间下的
                // float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, v2f.uv);
                float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, v2f.uv_depth);
                
                //...
            }
            ENDCG
        }
    }
    //...
}

片元函数中让移动方向向量除以2,从而降低运动模糊效果的强度,不要过于强烈

// 2. 计算得到运动方向向量
// 我们只想得到屏幕上的移动方向 所以是2维的 使用uv采样只需要2维即可
// 除以二效果会好点 这是前辈总结出来的经验
float2 moveDir = (nowClipPos.xy - oldClipPos.xy) / 2;

75.2 知识点代码

Lesson75_深度和法线纹理_效果实现_深度纹理实现运动模糊_具体实现.cs

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

public class Lesson75_深度和法线纹理_效果实现_深度纹理实现运动模糊_具体实现 : MonoBehaviour
{
    void Start()
    {
        #region 知识回顾

        //利用深度纹理实现运动模糊效果 的基本原理是
        //得到像素当前帧和上一帧中在裁剪空间下的位置,
        //利用两个位置计算出物体的运动方向,从而模拟出运动模糊的效果

        #endregion

        #region 补充知识点 C#设置矩阵变量给Shader

        //我们需要通过C#代码为Shader设置矩阵变量
        //但是ShaderLab语法中的属性中并没有矩阵类型的变量
        //因此我们只需要在CG语句中声明矩阵属性即可
        //这样C#中通过矩阵的属性名同样可以进行设置
        
        //举例:
        //CG代码中声明4x4矩阵 —— float4x4 _ClipToWorldMatrix;
        //C#代码中声明4x4矩阵 —— Matrix4x4 frontClipToWorldMatrix;
        //通过材质球指明变量进行设置即可
        //material.SetMatrix("_ClipToWorldMatrix", frontClipToWorldMatrix);

        #endregion

        #region 知识点一 实现 利用深度纹理实现运动模糊屏幕后期处理效果 对应 Shader

        //1.新建Shader文件,取名 MotionBlurWithDepthTexture 深度纹理运动模糊效果
        
        //2.声明属性,进行属性映射
        //  主纹理 _MainTex
        //  模糊偏移量 _BlurSize

        //  深度纹理 _CameraDepthTexture
        //  当前帧裁剪到世界空间变换矩阵 float4x4 _ClipToWorldMatrix
        //  上一帧世界到裁剪空间变换矩阵 float4x4 _FrontWorldToClipMatrix
        
        //3.屏幕后处理标配
        //  ZTest Always 
        //  Cull Off
        //  ZWrite Off
        
        //4.结构体
        //  顶点和uv坐标
        
        //5.顶点着色器
        //  坐标转换 uv坐标赋值
        
        //6.片元着色器
        //  6-1:得到裁剪空间下的两个点
        //      得到点一
        //      深度值获取
        //      构建裁剪空间下组合坐标 uv 和 深度
        //      得到点二
        //      裁剪空间坐标转世界空间(注意进行齐次除法)
        //      利用上一帧变换矩阵将世界空间坐标转裁剪空间(注意进行齐次除法)
        //  6-2:得到运动方向
        //      用当前帧点-上一帧点 得到运动方向
        //  6-3:进行模糊处理
        //      利用模糊偏移量变量进行3次偏移采样颜色后进行平均值计算
        
        //7.FallBack Off

        #endregion

        #region 知识点二 实现 利用深度纹理实现运动模糊屏幕后期处理效果 对应 C#

        //1.创建C#代码,命名和Shader一样
        //2.继承屏幕后处理基类PostEffectBase
        //3.声明模糊偏移量变量和用于记录上一次变换矩阵的变量
        //4.重写OnRenderImage函数
        //  在其中进行属性设置,变换矩阵计算,屏幕后处理
        //5.在生命周期函数中启用深度纹理,初始化上一帧变换矩阵

        #endregion

        #region 知识点三 其他注意点

        //1.考虑不同平台可能存在的垂直翻转问题、
        //  #if UNITY_UV_STARTS_AT_TOP
        //  if (_MainTex_TexelSize.y < 0)
        //    o.uv_depth.y = 1 - o.uv_depth.y;
        //  #endif

        //2.让移动方向向量除以2
        //  从而降低运动模糊效果的强度,不要过于强烈

        #endregion
    }
}

Lesson75_MotionBlurWithDepthTexture.shader

Shader "Unlit/Lesson75_MotionBlurWithDepthTexture"
{
    Properties
    {
        // 主纹理属性,默认白色
        _MainTex ("Texture", 2D) = "white" {}
        //用于控制模糊程度的 模糊偏移量
        _BlurSize("BlurSize", Float) = 0.5
    }
    SubShader
    {
        Pass
        {
            // 始终进行深度测试
            ZTest Always
            // 关闭背面剔除
            Cull Off
            // 关闭深度写入
            ZWrite Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            // 主纹理采样器
            sampler2D _MainTex;
            // 主纹理每个纹素的大小信息,用于一些纹理坐标相关的计算
            float4 _MainTex_TexelSize;
            // 存储模糊偏移量,用于控制模糊程度
            fixed _BlurSize;
            // 深度纹理采样器,用于获取场景深度信息
            sampler2D _CameraDepthTexture;
            // 裁剪空间到世界空间的变换矩阵,用于坐标变换
            float4x4 _ClipToWorldMatrix;
            // 上一帧世界空间到裁剪空间的变换矩阵,用于获取上一帧相关坐标信息
            float4x4 _FrontWorldToClipMatrix;

            struct v2f
            {
                // 主纹理的纹理坐标,用于在片元着色器中采样主纹理
                float2 uv : TEXCOORD0;
                // 深度纹理的纹理坐标,用于在片元着色器中采样深度纹理
                float2 uv_depth : TEXCOORD1;
                // 顶点在裁剪空间中的位置,用于后续渲染等操作
                float4 vertex : SV_POSITION;
            };

            // 顶点着色器函数
            v2f vert(appdata_base appdata_base)
            {
                v2f v2f;
                
                // 将顶点从模型空间转换到裁剪空间
                v2f.vertex = UnityObjectToClipPos(appdata_base.vertex);
                
                // 直接赋值主纹理的原始纹理坐标
                v2f.uv = appdata_base.texcoord;
                
                // 同样赋值深度纹理的原始纹理坐标
                v2f.uv_depth = appdata_base.texcoord;
                
                // 在多平台情况下,根据纹理坐标起始位置进行调整,保证深度纹理采样正确
                // 当纹理坐标在顶部开始时且主纹理纹素高度小于0,翻转深度纹理的y坐标
                #if UNITY_UV_STARTS_AT_TOP
                if (_MainTex_TexelSize.y < 0)
                    v2f.uv_depth.y = 1 - v2f.uv_depth.y;
                #endif

                return v2f;
            }

            // 片元着色器函数
            fixed4 frag(v2f v2f) : SV_Target
            {
                // 1. 得到裁剪空间下的两个点相关操作
                
                // 获取深度纹理在当前像素位置的深度值 这个深度值是裁剪空间下的
                // float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, v2f.uv);
                float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, v2f.uv_depth);
                
                // 构建裁剪空间下的一个组合坐标,将0~1范围的纹理坐标变换到-1~1范围
                // 此为当前帧的第一个点在裁剪空间中的坐标表示
                float4 nowClipPos = float4(v2f.uv.x * 2 - 1, v2f.uv.y * 2 - 1, depth * 2 - 1, 1);
                
                // 通过裁剪空间到世界空间的变换矩阵,将当前帧裁剪空间的点转换到世界空间,得到世界空间的坐标
                float4 worldPos = mul(_ClipToWorldMatrix, nowClipPos);
                
                // 进行透视的齐次除法,得到正确的世界空间坐标 否则w不是1的话可能不是正确的坐标
                worldPos /= worldPos.w;
                
                // 利用上一帧的变换矩阵,将世界空间的点转换回上一帧对应的裁剪空间下的点
                // 此为上一帧的第二个点在裁剪空间中的坐标表示
                float4 oldClipPos = mul(_FrontWorldToClipMatrix, worldPos);
                
                // 对上一帧的点进行透视的齐次除法
                oldClipPos /= oldClipPos.w;

                // 2. 计算得到运动方向向量
                // 我们只想得到屏幕上的移动方向 所以是2维的 使用uv采样只需要2维即可
                // 除以二效果会好点 这是前辈总结出来的经验
                float2 moveDir = (nowClipPos.xy - oldClipPos.xy) / 2;

                // 3. 基于运动方向进行模糊处理操作
                float2 uv = v2f.uv;
                float4 color = float4(0, 0, 0, 0);
                for (int it = 0; it < 3; it++)
                {
                    // 循环内进行多次纹理采样并累加颜色
                    // 第一次采样累加的是当前像素所在位置的颜色
                    // 第二次采样累加的是当前像素进行了 moveDir * _BlurSize 偏移后的颜色
                    // 第三次采样累加的是当前像素进行了 2*moveDir * _BlurSize 偏移后的颜色
                    color += tex2D(_MainTex, uv);
                    uv += moveDir * _BlurSize;
                }
                // 计算累加3次后颜色的平均值,实现模糊效果
                color /= 3;
                
                // 返回经过模糊处理后的颜色值
                return fixed4(color.rgb, 1);
            }
            ENDCG
        }
    }
    // 不使用回退Shader,即此Shader无法运行时不会切换到其他Shader
    Fallback Off
}

Lesson75_MotionBlurWithDepthTexture.cs

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

public class Lesson75_MotionBlurWithDepthTexture : PostEffectBase
{
    //模糊程度
    [Range(0, 1)] public float blurSize = 0.5f;

    //用于记录上一次的变换矩阵的变量
    private Matrix4x4 frontWorldToClipMatrix;

    private void Start()
    {
        //开启深度纹理
        Camera.main.depthTextureMode = DepthTextureMode.Depth;
        //初始化上一次的变换矩阵 用 观察到裁剪变换矩阵(摄像机的透视矩阵) * 世界到观察变换矩阵
        //得到的 就是 世界空间到裁剪空间的变换矩阵
        //frontWorldToClipMatrix = Camera.main.projectionMatrix * Camera.main.worldToCameraMatrix;
    }

    private void OnEnable()
    {
        //有时我们会在界面上让脚本失活,每次激活时 可以初始化一次
        frontWorldToClipMatrix = Camera.main.projectionMatrix * Camera.main.worldToCameraMatrix;
    }

    protected override void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (material != null)
        {
            //设置模糊程度
            material.SetFloat("_BlurSize", blurSize);
            //设置上一帧世界空间到裁剪空间的矩阵
            material.SetMatrix("_FrontWorldToClipMatrix", frontWorldToClipMatrix);
            //计算这一帧的变换矩阵
            frontWorldToClipMatrix = Camera.main.projectionMatrix * Camera.main.worldToCameraMatrix;
            //设置这一帧的 裁剪到世界空间的变换矩阵
            material.SetMatrix("_ClipToWorldMatrix", frontWorldToClipMatrix.inverse);
            //进行屏幕后期处理
            Graphics.Blit(source, destination, material);
        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏