unity shader 后处理实现运动模糊效果
记录用unity shader 后处理实现运动模糊效果(learn by 《unity shader 入门精要》)
运动模糊的实现有多种方式。这里的实现方式是利用一块累计缓存(accumulatioin buffer)来混合多张连续的图像
实现过程如下:
1、创建一个让摄像机移动的脚本 模拟运动效果。并将其拖至主摄像机
Translating.cs
using UnityEngine;
using System.Collections;
public class Translating : MonoBehaviour {
public float speed = 10.0f;
public Vector3 startPoint = Vector3.zero;
public Vector3 endPoint = Vector3.zero;
public Vector3 lookAt = Vector3.zero;
public bool pingpong = true;
private Vector3 curEndPoint = Vector3.zero;
// Use this for initialization
void Start () {
transform.position = startPoint;
curEndPoint = endPoint;
}
// Update is called once per frame
void Update () {
transform.position = Vector3.Slerp(transform.position, curEndPoint, Time.deltaTime * speed);
transform.LookAt(lookAt);
if (pingpong) {
if (Vector3.Distance(transform.position, curEndPoint) < 0.001f) {
curEndPoint = Vector3.Distance(curEndPoint, endPoint) < Vector3.Distance(curEndPoint, startPoint) ? startPoint : endPoint;
}
}
}
}
2、创建继承自基类的运动模糊后处理脚本MotionBlur.cs,并将其拖拽至摄像机中
基类脚本见上文
MotionBlur.cs
using UnityEngine;
using System.Collections;
public class MotionBlur : PostEffectsBase {
public Shader motionBlurShader;
private Material motionBlurMaterial = null;
public Material material {
get {
motionBlurMaterial = CheckShaderAndCreateMaterial(motionBlurShader, motionBlurMaterial);
return motionBlurMaterial;
}
}
//定义运动模糊在混合图像时使用的模糊参数 会有拖尾效果
[Range(0.0f, 0.9f)]
public float blurAmount = 0.5f;
//用于保存之前图像叠加的效果
private RenderTexture accumulationTexture;
//脚本不运行时,即调用OnDisable函数时,立即销毁accumulation Texture
void OnDisable() {
DestroyImmediate(accumulationTexture);
}
void OnRenderImage (RenderTexture src, RenderTexture dest) {
if (material != null) {
// Create the accumulation texture
if (accumulationTexture == null || accumulationTexture.width != src.width || accumulationTexture.height != src.height) {
DestroyImmediate(accumulationTexture);
accumulationTexture = new RenderTexture(src.width, src.height, 0);
accumulationTexture.hideFlags = HideFlags.HideAndDontSave;//表示该变量不会显示在Hierarchy中,也不会保存到场景中
Graphics.Blit(src, accumulationTexture);//将源纹理直接传给accumulationTexture作为初始化
}
// We are accumulating motion over frames without clear/discard
// by design, so silence any performance warnings from Unity
accumulationTexture.MarkRestoreExpected();//进行一个渲染纹理的恢复操作
material.SetFloat("_BlurAmount", 1.0f - blurAmount);
Graphics.Blit (src, accumulationTexture, material);//将当前屏幕图像
Graphics.Blit (accumulationTexture, dest);
} else {
Graphics.Blit(src, dest);
}
}
}
上文提到我们使用的方法是累计缓存 就是使用accumulationTexture变量不断地叠加新图像进行混合
逻辑如下:首先判断是否为空,是否与当前分辨率相等,如不满足重新创建该变量,用Blit进行初始化
然后进行一个渲染纹理的恢复操作,这样他会一直保存我们之前的混合结果
之后再用Blit将当前屏幕图像新叠加到accumulationTexture中
下面来实现Shader部分
MotionBlur.shader
Shader "MyShader/Motion Blur" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_BlurAmount ("Blur Amount", Float) = 1.0 //混合图像时的混合系数
}
SubShader {
CGINCLUDE //用CGINCLUDE复用代码 后面两个pass都能用
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed _BlurAmount;
struct v2f {
float4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
};
v2f vert(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
//一个通道更新RGB通道,一个通道更新渲染纹理的A通道部分
//之所以要两个通道 是因为更新RGB时我们需要设置它的A通道来混合图像,但又不希望A通道的值写入渲染纹理中
fixed4 fragRGB (v2f i) : SV_Target {
return fixed4(tex2D(_MainTex, i.uv).rgb, _BlurAmount);
}
half4 fragA (v2f i) : SV_Target {
return tex2D(_MainTex, i.uv);
}
ENDCG
ZTest Always Cull Off ZWrite Off
Pass {
Blend SrcAlpha OneMinusSrcAlpha //透明度混合 源alpha和 (1-源alpha)
ColorMask RGB
CGPROGRAM
#pragma vertex vert
#pragma fragment fragRGB
ENDCG
}
Pass {
Blend One Zero
ColorMask A
CGPROGRAM
#pragma vertex vert
#pragma fragment fragA
ENDCG
}
}
FallBack Off
}
完成后返回编辑器,别忘了将shader拖拽至摄像机的MotionBlur.cs脚本中
实现效果如下
------------恢复内容结束------------
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)