
速度映射图主要是为了得到每个像素相对于前一帧的运动矢量,其中一种方法是使用摄像机的深度纹理来推导。
推导过程如下:
先由深度纹理逆推出NDC(归一化的设备坐标)下的顶点坐标,利用VP矩阵(视角*投影矩阵)的逆矩阵反向变换出每个像素在世界空间中的位置,
再利用世界空间下的坐标与前一帧的VP矩阵顺向变换出前一帧的NDC坐标,利用NDC下前一帧和相当帧的坐标差来确定速度的方向,
最后利用速度的方向对纹理采样的结果进行加权平均并多次绘制,以达到带有物体运动方向的模糊效果。
基于这一原理,需要准备的要素有:
1.摄像机的深度纹理(是由NDC下的坐标映射来的,需要先反向映射回NDC)
2.当前帧的VP矩阵的逆矩阵
3.前一帧的VP矩阵
摄像机深度值和深度纹理的获取方法在之前的博客中有写,具体可以参考:
https://cloud.tencent.com/developer/article/1601293
视角矩阵(V矩阵):
MyCamera.worldToCameraMatrix;(也就是世界空间变换到摄像机空间(也叫视角空间,观察空间))
投影矩阵(P矩阵):
MyCamera.projectionMatrix;(也就是摄像机空间变换到裁剪空间)
具体的数学推导过程可以见这篇文章:
http://feepingcreature.github.io/math.html
下面是具体的程序实现:
1.此脚本挂载在摄像机上,用于模糊参数调控,深度纹理准备,矩阵传递:
1 using UnityEngine;
2
3 public class MotionBlurWithDepthTexCtrl : ScreenEffectBase
4 {
5 private const string _BlurSize = "_BlurSize";
6 private const string _PreViewMatrix = "_PreViewMatrix";
7 private const string _CurViewInserseMatrix = "_CurViewInserseMatrix";
8
9 [Range(0.0f, 1.0f)]
10 public float blurSize = .5f;
11
12 //前一帧的VP矩阵
13 private Matrix4x4 preViewMatrix;
14
15 private Camera myCamera;
16 public Camera MyCamera
17 {
18 get
19 {
20 if(null == myCamera)
21 {
22 myCamera = GetComponent<Camera>();
23 }
24 return myCamera;
25 }
26 }
27
28 //开启这相机深度模式,并初始化前一帧的VP矩阵
29 private void OnEnable()
30 {
31 MyCamera.depthTextureMode |= DepthTextureMode.Depth;
32
33 //右乘原则,前边是P矩阵,后边是V矩阵
34 preViewMatrix = MyCamera.projectionMatrix * MyCamera.worldToCameraMatrix;
35 }
36
37 //不用时恢复
38 private void OnDisable()
39 {
40 MyCamera.depthTextureMode &= ~DepthTextureMode.Depth;
41 }
42
43 private void OnRenderImage(RenderTexture source, RenderTexture destination)
44 {
45 if (Material)
46 {
47 Material.SetFloat(_BlurSize, blurSize);
48 //设置前一帧的VP矩阵
49 Material.SetMatrix(_PreViewMatrix, preViewMatrix);
50 //计算当前帧VP矩阵,右乘
51 Matrix4x4 curViewMatrix = MyCamera.projectionMatrix * MyCamera.worldToCameraMatrix;
52 //存起来,作为下次计算的前一帧
53 preViewMatrix = curViewMatrix;
54 //设置当前帧的VP矩阵的逆矩阵
55 Material.SetMatrix(_CurViewInserseMatrix, curViewMatrix.inverse);
56
57 Graphics.Blit(source, destination, Material);
58 }
59 else
60 Graphics.Blit(source, destination);
61
62 }
63 }基类脚本见:
https://cloud.tencent.com/developer/article/1601301
2.Shader脚本:
1 Shader "MyUnlit/MotionBlurWithDepthTex"
2 {
3 Properties
4 {
5 _MainTex ("Texture", 2D) = "white" {}
6 }
7 SubShader
8 {
9 Pass
10 {
11 ZTest Always Cull Off ZWrite Off
12
13 CGPROGRAM
14 #pragma vertex vert
15 #pragma fragment frag
16
17 #include "UnityCG.cginc"
18
19 sampler2D _MainTex;
20 half4 _MainTex_TexelSize;
21 fixed _BlurSize;
22 //声明深度纹理和对应矩阵
23 sampler2D _CameraDepthTexture;
24 float4x4 _PreViewMatrix;
25 float4x4 _CurViewInserseMatrix;
26
27 struct appdata
28 {
29 float4 vertex : POSITION;
30 float2 uv : TEXCOORD0;
31 };
32
33 struct v2f
34 {
35 //这里的的uv同时存了主纹理的uv和深度纹理uv,xy为主纹理,zw为深度纹理
36 half4 uv : TEXCOORD0;
37 float4 vertex : SV_POSITION;
38 };
39
40 v2f vert (appdata v)
41 {
42 v2f o;
43 o.vertex = UnityObjectToClipPos(v.vertex);
44 o.uv.xy =v.uv;
45 o.uv.zw=v.uv;
46
47 //主纹理外的纹理要进行平台差异化处理
48 #if UNITY_UV_STARTS_AT_TOP
49 if(_MainTex_TexelSize.y<0)
50 o.uv.w=1-o.uv.w;
51 #endif
52
53 return o;
54 }
55
56 fixed4 frag (v2f i) : SV_Target
57 {
58 //用深度纹理和zw取得深度值
59 float d=SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv.zw);
60 //反映射回NDC坐标,由[0,1]到[-1,1]的映射,z分量就是深度值本身
61 float4 H=float4(i.uv.x*2-1,i.uv.y*2-1,d*2-1,1);
62 //用VP的逆矩阵反向变换并除以w分量得到世界坐标位置,为什么除以w详细见前面数学推导的文章链接
63 float4 D=mul(_CurViewInserseMatrix,H);
64 float4 worldPos=D/D.w;
65
66 //分别得到前一帧和当前帧的NDC坐标取差值计算速度方向
67 float4 curViewPos=H;
68 float4 preViewPos=mul(_PreViewMatrix,worldPos);
69 preViewPos/=preViewPos.w;
70
71 //除以的系数可以根据自己的需求调整
72 float2 velocity=(curViewPos.xy-preViewPos.xy)/10.0f;
73
74 float2 uv=i.uv.xy;
75 //纹理采样的速度权重,这里进行了前2帧的计算,包括当前帧总共3个值,值依次递减且保证和为1,不为1则需要进行额外的除法
76 //目的是为了让越之前的帧看上去效果越淡,轨迹逐渐消失
77 float velColRate[3]={0.6,0.3,0.1};
78 //当前采样结果用权重最大的值0.6
79 fixed4 col = tex2D(_MainTex, uv)*velColRate[0];
80 uv+=velocity*_BlurSize;
81
82 for(int it=1;it<3;it++)
83 {
84 //前两帧采样结果依次递减,0.3,0.1
85 fixed4 curCol=tex2D(_MainTex,uv.xy)*velColRate[it];
86 //将所有结果加起来,保证权重为1
87 col+=curCol;
88 //按速度方便对纹理进行偏移,并用模糊系数加以控制
89 uv+=velocity*_BlurSize;
90 }
91
92 return fixed4(col.rgb,1.0);
93 }
94 ENDCG
95 }
96 }
97 FallBack Off
98 }效果如下:
