【Unity3D】运动模糊特效

1 运动模糊原理

​ 开启混合(Blend)后,通过 Alpha 通道控制当前屏幕纹理与历史屏幕纹理进行混合,当有物体运动时,就会将当前位置的物体影像与历史位置的物体影像进行混合,从而实现运动模糊效果,即模糊拖尾效果。主要代码如下:

Pass {
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert_img // 使用内置的vert_img顶点着色器
#pragma fragment fragRGB // _BlurAmount只参与混合, 不影响alpha值
fixed4 fragRGB (v2f_img i) : SV_Target { // v2f_img为内置结构体, 里面只包含pos和uv
return fixed4(tex2D(_MainTex, i.uv).rgb, _BlurAmount); // 模糊值, 通过alpha通道控制当前屏幕纹理与历史屏幕纹理进行混合
}
ENDCG
}
Pass {
    Blend SrcAlpha OneMinusSrcAlpha

    CGPROGRAM

    #pragma vertex vert_img // 使用内置的vert_img顶点着色器
    #pragma fragment fragRGB // _BlurAmount只参与混合, 不影响alpha值

    fixed4 fragRGB (v2f_img i) : SV_Target { // v2f_img为内置结构体, 里面只包含pos和uv
        return fixed4(tex2D(_MainTex, i.uv).rgb, _BlurAmount); // 模糊值, 通过alpha通道控制当前屏幕纹理与历史屏幕纹理进行混合
    }

    ENDCG
}
Pass { Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert_img // 使用内置的vert_img顶点着色器 #pragma fragment fragRGB // _BlurAmount只参与混合, 不影响alpha值 fixed4 fragRGB (v2f_img i) : SV_Target { // v2f_img为内置结构体, 里面只包含pos和uv return fixed4(tex2D(_MainTex, i.uv).rgb, _BlurAmount); // 模糊值, 通过alpha通道控制当前屏幕纹理与历史屏幕纹理进行混合 } ENDCG }

​ 本文完整资源 见→Unity3D运动模糊特效

2 代码实现

​ MotionBlur.cs

using UnityEngine;
[RequireComponent(typeof(Camera))] // 需要相机组件
public class MotionBlur : MonoBehaviour {
[Range(0.0f, 0.9f)]
public float blurAmount = 0.5f; // 模糊值, 值越大拖尾效果越明显
private RenderTexture historyTexture; // 历史屏幕纹理
private Material material = null; // 材质
private void Start() {
material = new Material(Shader.Find("MyShader/MotionBlur"));
material.hideFlags = HideFlags.DontSave;
}
void OnDisable() { // 脚本不运行时立即销毁, 下次开始应用运动模糊时, 重新混合图像
DestroyImmediate(historyTexture);
}
void OnRenderImage(RenderTexture src, RenderTexture dest) {
if (material != null) {
// 初始化时或窗口尺寸变化时, 创建叠加纹理
if (historyTexture == null || historyTexture.width != src.width || historyTexture.height != src.height) {
DestroyImmediate(historyTexture);
historyTexture = new RenderTexture(src.width, src.height, 0);
historyTexture.hideFlags = HideFlags.HideAndDontSave;
Graphics.Blit(src, historyTexture);
}
material.SetFloat("_BlurAmount", 1.0f - blurAmount); // 设置模糊值, 通过alpha通道控制当前屏幕纹理与历史屏幕纹理进行混合
Graphics.Blit(src, historyTexture, material);
Graphics.Blit(historyTexture, dest);
} else {
Graphics.Blit(src, dest);
}
}
}
using UnityEngine;

[RequireComponent(typeof(Camera))] // 需要相机组件
public class MotionBlur : MonoBehaviour {
    [Range(0.0f, 0.9f)]
    public float blurAmount = 0.5f; // 模糊值, 值越大拖尾效果越明显
    private RenderTexture historyTexture; // 历史屏幕纹理
    private Material material = null; // 材质

    private void Start() {
        material = new Material(Shader.Find("MyShader/MotionBlur"));
        material.hideFlags = HideFlags.DontSave;
    }

    void OnDisable() { // 脚本不运行时立即销毁, 下次开始应用运动模糊时, 重新混合图像
        DestroyImmediate(historyTexture);
    }

    void OnRenderImage(RenderTexture src, RenderTexture dest) {
        if (material != null) {
            // 初始化时或窗口尺寸变化时, 创建叠加纹理
            if (historyTexture == null || historyTexture.width != src.width || historyTexture.height != src.height) {
                DestroyImmediate(historyTexture);
                historyTexture = new RenderTexture(src.width, src.height, 0);
                historyTexture.hideFlags = HideFlags.HideAndDontSave;
                Graphics.Blit(src, historyTexture);
            }
            material.SetFloat("_BlurAmount", 1.0f - blurAmount); // 设置模糊值, 通过alpha通道控制当前屏幕纹理与历史屏幕纹理进行混合
            Graphics.Blit(src, historyTexture, material);
            Graphics.Blit(historyTexture, dest);
        } else {
            Graphics.Blit(src, dest);
        }
    }
}
using UnityEngine; [RequireComponent(typeof(Camera))] // 需要相机组件 public class MotionBlur : MonoBehaviour { [Range(0.0f, 0.9f)] public float blurAmount = 0.5f; // 模糊值, 值越大拖尾效果越明显 private RenderTexture historyTexture; // 历史屏幕纹理 private Material material = null; // 材质 private void Start() { material = new Material(Shader.Find("MyShader/MotionBlur")); material.hideFlags = HideFlags.DontSave; } void OnDisable() { // 脚本不运行时立即销毁, 下次开始应用运动模糊时, 重新混合图像 DestroyImmediate(historyTexture); } void OnRenderImage(RenderTexture src, RenderTexture dest) { if (material != null) { // 初始化时或窗口尺寸变化时, 创建叠加纹理 if (historyTexture == null || historyTexture.width != src.width || historyTexture.height != src.height) { DestroyImmediate(historyTexture); historyTexture = new RenderTexture(src.width, src.height, 0); historyTexture.hideFlags = HideFlags.HideAndDontSave; Graphics.Blit(src, historyTexture); } material.SetFloat("_BlurAmount", 1.0f - blurAmount); // 设置模糊值, 通过alpha通道控制当前屏幕纹理与历史屏幕纹理进行混合 Graphics.Blit(src, historyTexture, material); Graphics.Blit(historyTexture, dest); } else { Graphics.Blit(src, dest); } } }

​ MotionBlur.shader

Shader "MyShader/MotionBlur" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {} // 主纹理
_BlurAmount ("Blur Amount", Float) = 1.0 // 模糊值, 通过alpha通道控制当前屏幕纹理与历史屏幕纹理进行混合
}
SubShader {
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex; // 主纹理
fixed _BlurAmount; // 模糊值, 通过alpha通道控制当前屏幕纹理与历史屏幕纹理进行混合
fixed4 fragRGB (v2f_img i) : SV_Target { // v2f_img为内置结构体, 里面只包含pos和uv
return fixed4(tex2D(_MainTex, i.uv).rgb, _BlurAmount);
}
half4 fragA (v2f_img i) : SV_Target { // v2f_img为内置结构体, 里面只包含pos和uv
return tex2D(_MainTex, i.uv);
}
ENDCG
ZTest Always Cull Off ZWrite Off
Pass {
Blend SrcAlpha OneMinusSrcAlpha
ColorMask RGB // 允许通过的颜色通道, 取值有: 0、R、G、B、A、RGBA的组合(RG、RGB等)
CGPROGRAM
#pragma vertex vert_img // 使用内置的vert_img顶点着色器
#pragma fragment fragRGB // _BlurAmount只参与混合, 不影响alpha值
ENDCG
}
Pass {
Blend One Zero
ColorMask A // 允许通过的颜色通道, 取值有: 0、R、G、B、A、RGBA的组合(RG、RGB等)
CGPROGRAM
#pragma vertex vert_img // 使用内置的vert_img顶点着色器
#pragma fragment fragA // 使用纹理原本的alpha值
ENDCG
}
}
FallBack Off
}
Shader "MyShader/MotionBlur" {
    Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {} // 主纹理
        _BlurAmount ("Blur Amount", Float) = 1.0 // 模糊值, 通过alpha通道控制当前屏幕纹理与历史屏幕纹理进行混合
    }

    SubShader {
        CGINCLUDE

        #include "UnityCG.cginc"
        
        sampler2D _MainTex; // 主纹理
        fixed _BlurAmount; // 模糊值, 通过alpha通道控制当前屏幕纹理与历史屏幕纹理进行混合

        fixed4 fragRGB (v2f_img i) : SV_Target { // v2f_img为内置结构体, 里面只包含pos和uv
            return fixed4(tex2D(_MainTex, i.uv).rgb, _BlurAmount);
        }

        half4 fragA (v2f_img i) : SV_Target { // v2f_img为内置结构体, 里面只包含pos和uv
            return tex2D(_MainTex, i.uv);
        }

        ENDCG

        ZTest Always Cull Off ZWrite Off

        Pass {
            Blend SrcAlpha OneMinusSrcAlpha
            ColorMask RGB // 允许通过的颜色通道, 取值有: 0、R、G、B、A、RGBA的组合(RG、RGB等)

            CGPROGRAM

            #pragma vertex vert_img // 使用内置的vert_img顶点着色器
            #pragma fragment fragRGB // _BlurAmount只参与混合, 不影响alpha值

            ENDCG
        }

        Pass {
            Blend One Zero
            ColorMask A // 允许通过的颜色通道, 取值有: 0、R、G、B、A、RGBA的组合(RG、RGB等)

            CGPROGRAM

            #pragma vertex vert_img // 使用内置的vert_img顶点着色器
            #pragma fragment fragA // 使用纹理原本的alpha值

            ENDCG
        }
    }

    FallBack Off
}
Shader "MyShader/MotionBlur" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} // 主纹理 _BlurAmount ("Blur Amount", Float) = 1.0 // 模糊值, 通过alpha通道控制当前屏幕纹理与历史屏幕纹理进行混合 } SubShader { CGINCLUDE #include "UnityCG.cginc" sampler2D _MainTex; // 主纹理 fixed _BlurAmount; // 模糊值, 通过alpha通道控制当前屏幕纹理与历史屏幕纹理进行混合 fixed4 fragRGB (v2f_img i) : SV_Target { // v2f_img为内置结构体, 里面只包含pos和uv return fixed4(tex2D(_MainTex, i.uv).rgb, _BlurAmount); } half4 fragA (v2f_img i) : SV_Target { // v2f_img为内置结构体, 里面只包含pos和uv return tex2D(_MainTex, i.uv); } ENDCG ZTest Always Cull Off ZWrite Off Pass { Blend SrcAlpha OneMinusSrcAlpha ColorMask RGB // 允许通过的颜色通道, 取值有: 0、R、G、B、A、RGBA的组合(RG、RGB等) CGPROGRAM #pragma vertex vert_img // 使用内置的vert_img顶点着色器 #pragma fragment fragRGB // _BlurAmount只参与混合, 不影响alpha值 ENDCG } Pass { Blend One Zero ColorMask A // 允许通过的颜色通道, 取值有: 0、R、G、B、A、RGBA的组合(RG、RGB等) CGPROGRAM #pragma vertex vert_img // 使用内置的vert_img顶点着色器 #pragma fragment fragA // 使用纹理原本的alpha值 ENDCG } } FallBack Off }

​ 说明: vert_img 是 Unity 内置的顶点着色器,v2f_img 是 Unity 内置的结构体变量,vert_img 和 v2f_img 的实现见→Shader常量、变量、结构体、函数;第一个 Pass 通过 Alpha 通道控制当前屏幕纹理与历史屏幕纹理进行混合,通过 ColorMask RGB 保证混合不影响 Alpha 通道值;第二个 Pass 对当前屏幕的 Alpha 通道进行采样,通过 ColorMask A 保证采样不影响 RGB 通道的值。

3 运行效果

1)原图效果

​ 将 blurAmount 设置为 0,无运动模糊特效,如下:

img

2)运动模糊效果

​ 将 blurAmount 设置为 0.9,运动模糊特效如下:

img

​ 声明:本文转自【Unity3D】运动模糊特效

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYYrCIar' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片