速度映射图。速度映射图存储了每个像素的速度,然后使用这个速度决定模糊的方向和大小。
得到当前帧视角 * 投影矩阵的逆矩阵
用深度纹理在片元着色器中为每个像素计算其在世界空间下的位置
NDC下的顶点坐标 * (当前视角 * 投影矩阵的逆矩阵)/ w分量 = 在世界空间下的位置
使用在世界空间下的位置 * (前一帧的当前视角 * 投影矩阵)/w分量 = 前一帧中的NDC顶点坐标
两个相减得到速度
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MotionBlurWithDepthTexCS : PostEffectsBase
{
public Shader motionBlurShader;
private Material motionBlurMaterial = null;
public Material material
{
get
{
motionBlurMaterial = CheckShaderAndCreateMaterial(motionBlurShader, motionBlurMaterial);
return motionBlurMaterial;
}
}
//需要得到摄像机的投影矩阵
//所以要获取摄像机组件
public Camera myCamera;
public Camera camera
{
get
{
if(myCamera == null)
{
myCamera = GetComponent<Camera>();
}
return myCamera;
}
}
[Range(0.0f, 1.0f)]
public float blurSize = 0.5f;
//存上一帧摄像机的视角 * 投影矩阵
private Matrix4x4 previousViewProjectionMatrix;
private void OnEnable()
{
camera.depthTextureMode |= DepthTextureMode.Depth;
//记录 当前帧视角 * 投影矩阵
//用于后面传入到shader中
//相当于初始化
previousViewProjectionMatrix = camera.projectionMatrix * camera.worldToCameraMatrix;
}
//取逆,得到当前帧视角 * 投影矩阵的逆矩阵
//用深度纹理在片元着色器中为每个像素计算其在世界空间下的位置
//NDC下的顶点坐标 * (当前视角 * 投影矩阵的逆矩阵)= 在世界空间下的位置
//再使用 在世界空间下的位置 * (前一帧的当前视角 * 投影矩阵) = 前一帧中的NDC顶点坐标
//两个相减得到速度
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if(material != null)
{
material.SetFloat("_BlurSize", blurSize);
//将上一帧的视角 * 投影矩阵传给shader
material.SetMatrix("_PreviousViewProjectionMatrix", previousViewProjectionMatrix);
//当前帧视角 * 投影矩阵取逆
Matrix4x4 currentViewProjectionMatrix = camera.projectionMatrix * camera.worldToCameraMatrix;
Matrix4x4 currentViewProjectionInverseMatrix = currentViewProjectionMatrix.inverse;
material.SetMatrix("_CurrentViewProjectionInverseMatrix", currentViewProjectionInverseMatrix);
//当前帧的视角 * 投影矩阵记录下来给下一帧用
previousViewProjectionMatrix = currentViewProjectionMatrix;
Graphics.Blit(source, destination, material);
}
else
{
Graphics.Blit(source, destination);
}
}
}
shader:
Shader "Unlit/MotionBlurWithDepthTex"
{
Properties
{
_MainTex("Base (RGB)",2D) = "white"{}
_BlurSize("Blur Size",Float) = 1.0
}
SubShader
{
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
half4 _MainTex_TexelSize;
sampler2D _CameraDepthTexture;
float4x4 _CurrentViewProjectionInverseMatrix;
float4x4 _PreviousViewProjectionMatrix;
half _BlurSize;
struct v2f
{
float4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
half2 uv_depth : TEXCOORD1;
};
v2f vert(appdata_img v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
o.uv_depth = v.texcoord;
#if UNITY_UV_STARTS_AT_TOP
if(_MainTex_TexelSize.y < 0)
o.uv_depth.y = 1 - o.uv_depth.y;
#endif
return o;
}
fixed4 frag(v2f i) : SV_Target
{
//得到深度值
float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth);
//构建NDC坐标(归一化的设备坐标,观察空间中经过了矩阵裁剪矩阵 + 齐次除法)
float4 H = float4(i.uv.x * 2 - 1,i.uv.y * 2 - 1,d * 2 - 1,1);
//利用视角 * 投影的逆矩阵算出世界坐标
float4 D = mul(_CurrentViewProjectionInverseMatrix, H);
float4 worldPos = D / D.w;
float4 currentPos = H;
//算出前一帧世界坐标
float4 previousPos = mul(_PreviousViewProjectionMatrix, worldPos);
previousPos /= previousPos.w;
//算出速度
float2 velocity = (currentPos.xy - previousPos.xy) / 2.0f;
float2 uv = i.uv;
float4 c = tex2D(_MainTex,i.uv);
//取该点旁边的像素点,速度越快,取的点越远
uv += velocity * _BlurSize;
//取那个点和原来的点取平均
for(int it = 1;it < 3;it++,uv += velocity * _BlurSize)
{
//取三个点的uv坐标然后采样,结果相加
float4 currentColor = tex2D( _MainTex, uv);
c += currentColor;
}
//取平均
c /= 3;
return fixed4(c.rgb,1.0);
}
ENDCG
Pass{
ZTest Always Cull Off ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
FallBack Off
}