效果展示:


如何使用?
-
首先创建一个空物体,将SetMotionBlur脚本挂到空物体上

-
新建一个Image,并适应Canvas,将TS_MotionBlur材质球挂上即可

实现代码:
Shader "TS/MotionBlur"
{
Properties
{
_Color("Main Color", Color) = (1,1,1,1)
_Size("Size", Range(0, 20)) = 1
}
Category{
// We must be transparent, so other objects are drawn before this one.
Tags {
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
"PreviewType" = "Plane"
"CanUseSpriteAtlas" = "True"
}
SubShader {
// Horizontal blur
GrabPass
{
Tags { "LightMode" = "Always" }
}
Pass {
Tags { "LightMode" = "Always" }
Name "MotionBlur"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
float4 color : COLOR;
};
struct v2f {
float4 vertex : POSITION;
float4 uvgrab : TEXCOORD0;
float4 color : COLOR;
};
v2f vert(appdata_t v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
#if UNITY_UV_STARTS_AT_TOP
float scale = -1.0;
#else
float scale = 1.0;
#endif
o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y * scale) + o.vertex.w) * 0.5;
o.uvgrab.zw = o.vertex.zw;
o.color = v.color;
return o;
}
sampler2D _GrabTexture;
float4 _GrabTexture_TexelSize;
float4 _MainTex_TexelSize;
float _Size;
uniform float4 _Color;
half4 GrabPixel(v2f i, float weight, float kernel) {
kernel = sign(sign(abs(i.uvgrab.x)) + sign(abs(i.uvgrab.y))) * kernel;
return tex2Dproj(_GrabTexture, UNITY_PROJ_COORD(float4(i.uvgrab.x + _GrabTexture_TexelSize.x * kernel * _Size, i.uvgrab.y, i.uvgrab.z, i.uvgrab.w))) * weight;
}
half4 frag(v2f i) : COLOR {
half4 sum = half4(0,0,0,0);
sum += GrabPixel(i, 0.05, -4.0);
sum += GrabPixel(i, 0.09, -3.0);
sum += GrabPixel(i, 0.12, -2.0);
sum += GrabPixel(i, 0.15, -1.0);
sum += GrabPixel(i, 0.18, 0.0);
sum += GrabPixel(i, 0.15, +1.0);
sum += GrabPixel(i, 0.12, +2.0);
sum += GrabPixel(i, 0.09, +3.0);
sum += GrabPixel(i, 0.05, +4.0);
float4 col5 = tex2Dproj(_GrabTexture, UNITY_PROJ_COORD(i.uvgrab));
fixed decayFactor = sign(sign(abs(i.uvgrab.x)) + sign(abs(i.uvgrab.y)));
sum = lerp(col5, sum, decayFactor) * i.color * _Color;
return sum;
}
ENDCG
}
GrabPass {
Tags { "LightMode" = "Always" }
}
Pass {
Tags { "LightMode" = "Always" }
Name "MotionBlur"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord: TEXCOORD0;
float4 color : COLOR;
};
struct v2f {
float4 vertex : POSITION;
float4 uvgrab : TEXCOORD0;
float4 color : COLOR;
};
v2f vert(appdata_t v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
#if UNITY_UV_STARTS_AT_TOP
float scale = -1.0;
#else
float scale = 1.0;
#endif
o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y * scale) + o.vertex.w) * 0.5;
o.uvgrab.zw = o.vertex.zw;
o.color = v.color;
return o;
}
sampler2D _GrabTexture;
float4 _GrabTexture_TexelSize;
float _Size;
uniform float4 _Color;
half4 GrabPixel(v2f i, float weight, float kernel) {
kernel = sign(sign(abs(i.uvgrab.x)) + sign(abs(i.uvgrab.y))) * kernel;
return tex2Dproj(_GrabTexture, UNITY_PROJ_COORD(float4(i.uvgrab.x, i.uvgrab.y + _GrabTexture_TexelSize.y * kernel * _Size, i.uvgrab.z, i.uvgrab.w))) * weight;
}
half4 frag(v2f i) : COLOR
{
half4 sum = half4(0,0,0,0);
sum += GrabPixel(i, 0.05, -4.0);
sum += GrabPixel(i, 0.09, -3.0);
sum += GrabPixel(i, 0.12, -2.0);
sum += GrabPixel(i, 0.15, -1.0);
sum += GrabPixel(i, 0.18, 0.0);
sum += GrabPixel(i, 0.15, +1.0);
sum += GrabPixel(i, 0.12, +2.0);
sum += GrabPixel(i, 0.09, +3.0);
sum += GrabPixel(i, 0.05, +4.0);
float4 col5 = tex2Dproj(_GrabTexture, UNITY_PROJ_COORD(i.uvgrab));
fixed decayFactor = sign(sign(abs(i.uvgrab.x)) + sign(abs(i.uvgrab.y)));
sum = lerp(col5, sum, decayFactor) * i.color * _Color;
return sum;
}
ENDCG
}
}
}
}
C#代码,用于Demo演示及如何调用Shader里的_size值
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class SetMotionBlur : MonoBehaviour
{
public Material motionBlurMaterial; // 引用具有MotionBlur Shader的材质
float value = 0;
public bool isUpdate = false;
void Start()
{
// 检查材质和Shader是否正确设置
if (motionBlurMaterial != null && motionBlurMaterial.shader.name == "TS/MotionBlur")
{
// 设置初始的Size值
motionBlurMaterial.SetFloat("_Size", 1.0f);
}
else
{
Debug.LogError("MotionBlur材质或Shader未正确设置。");
}
}
void Update()
{
if (isUpdate)
{
//来回执行 增强 减弱
float newSize = Mathf.PingPong(Time.time * 15, 20.0f);
motionBlurMaterial.SetFloat("_Size", newSize);
}
else
{
if (Input.GetKeyDown(KeyCode.Q))
{
//判断是否超出范围
if (value + 5 <= 20)
{
value += 5;
motionBlurMaterial.SetFloat("_Size", value);
Debug.Log(string.Format("<color=yellow>{0}</color>", "模糊已增强"));
}
else
{
Debug.LogError("超出范围,请先减弱模糊");
}
}
if (Input.GetKeyDown(KeyCode.E))
{
//判断是否超出范围
if (value - 5 >= 0)
{
value -= 5;
motionBlurMaterial.SetFloat("_Size", value);
Debug.Log(string.Format("<color=cyan>{0}</color>", "模糊已减弱"));
}
else
{
Debug.LogError("超出范围,请先增强模糊");
}
}
}
}
/// <summary>
///
/// </summary>
void OnGUI()
{
GUIStyle fontStyle = new GUIStyle();
fontStyle.alignment = TextAnchor.UpperLeft;
fontStyle.fontSize = 60;
fontStyle.fontStyle = FontStyle.Bold;
fontStyle.normal.textColor = new Color(50 / 255f, 50 / 255f, 50 / 255f, 1);
if (isUpdate==false)
{
GUI.Label(new Rect(20, 20, 60, 60), "Q键增强模糊效果 E键减弱模糊效果", fontStyle);
GUI.Label(new Rect(20, 100, 60, 60), "勾选isUpdate执行限值内自运行", fontStyle);
}
}
/// <summary>
/// 编辑器模式
/// </summary>
public void OnValidate()
{
if (isUpdate)
{
Debug.Log(string.Format("<color=red>{0}</color>", "开始执行限值内自运行"));
}
else
{
Debug.Log(string.Format("<color=red>{0}</color>", "停止执行限值内自运行"));
}
}
}
最后附上实例Demo包
点击下载