速度映射图主要是为了得到每个像素相对于前一帧的运动矢量,其中一种方法是使用摄像机的深度纹理来推导。

推导过程如下:

先由深度纹理逆推出NDC(归一化的设备坐标)下的顶点坐标,利用VP矩阵(视角*投影矩阵)的逆矩阵反向变换出每个像素在世界空间中的位置,

再利用世界空间下的坐标与前一帧的VP矩阵顺向变换出前一帧的NDC坐标,利用NDC下前一帧和相当帧的坐标差来确定速度的方向,

最后利用速度的方向对纹理采样的结果进行加权平均并多次绘制,以达到带有物体运动方向的模糊效果。

基于这一原理,需要准备的要素有:

1.摄像机的深度纹理(是由NDC下的坐标映射来的,需要先反向映射回NDC)

2.当前帧的VP矩阵的逆矩阵

3.前一帧的VP矩阵

摄像机深度值和深度纹理的获取方法在之前的博客中有写,具体可以参考:

https://www.cnblogs.com/koshio0219/p/11178215.html

视角矩阵(V矩阵):

MyCamera.worldToCameraMatrix;(也就是世界空间变换到摄像机空间(也叫视角空间,观察空间))

投影矩阵(P矩阵):

MyCamera.projectionMatrix;(也就是摄像机空间变换到裁剪空间)

具体的数学推导过程可以见这篇文章:

http://feepingcreature.github.io/math.html

下面是具体的程序实现:

1.此脚本挂载在摄像机上,用于模糊参数调控,深度纹理准备,矩阵传递:

 using UnityEngine;

 public class MotionBlurWithDepthTexCtrl : ScreenEffectBase
{
private const string _BlurSize = "_BlurSize";
private const string _PreViewMatrix = "_PreViewMatrix";
private const string _CurViewInserseMatrix = "_CurViewInserseMatrix"; [Range(0.0f, 1.0f)]
public float blurSize = .5f; //前一帧的VP矩阵
private Matrix4x4 preViewMatrix; private Camera myCamera;
public Camera MyCamera
{
get
{
if(null == myCamera)
{
myCamera = GetComponent<Camera>();
}
return myCamera;
}
} //开启这相机深度模式,并初始化前一帧的VP矩阵
private void OnEnable()
{
MyCamera.depthTextureMode |= DepthTextureMode.Depth; //右乘原则,前边是P矩阵,后边是V矩阵
preViewMatrix = MyCamera.projectionMatrix * MyCamera.worldToCameraMatrix;
} //不用时恢复
private void OnDisable()
{
MyCamera.depthTextureMode &= ~DepthTextureMode.Depth;
} private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (Material)
{
Material.SetFloat(_BlurSize, blurSize);
//设置前一帧的VP矩阵
Material.SetMatrix(_PreViewMatrix, preViewMatrix);
//计算当前帧VP矩阵,右乘
Matrix4x4 curViewMatrix = MyCamera.projectionMatrix * MyCamera.worldToCameraMatrix;
//存起来,作为下次计算的前一帧
preViewMatrix = curViewMatrix;
//设置当前帧的VP矩阵的逆矩阵
Material.SetMatrix(_CurViewInserseMatrix, curViewMatrix.inverse); Graphics.Blit(source, destination, Material);
}
else
Graphics.Blit(source, destination); }
}

基类脚本见:

https://www.cnblogs.com/koshio0219/p/11131619.html

2.Shader脚本:

 Shader "MyUnlit/MotionBlurWithDepthTex"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Pass
{
ZTest Always Cull Off ZWrite Off CGPROGRAM
#pragma vertex vert
#pragma fragment frag #include "UnityCG.cginc" sampler2D _MainTex;
half4 _MainTex_TexelSize;
fixed _BlurSize;
//声明深度纹理和对应矩阵
sampler2D _CameraDepthTexture;
float4x4 _PreViewMatrix;
float4x4 _CurViewInserseMatrix; struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
}; struct v2f
{
//这里的的uv同时存了主纹理的uv和深度纹理uv,xy为主纹理,zw为深度纹理
half4 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
}; v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv.xy =v.uv;
o.uv.zw=v.uv; //主纹理外的纹理要进行平台差异化处理
#if UNITY_UV_STARTS_AT_TOP
if(_MainTex_TexelSize.y<)
o.uv.w=-o.uv.w;
#endif return o;
} fixed4 frag (v2f i) : SV_Target
{
//用深度纹理和zw取得深度值
float d=SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv.zw);
//反映射回NDC坐标,由[0,1]到[-1,1]的映射,z分量就是深度值本身
float4 H=float4(i.uv.x*-,i.uv.y*-,d*-,);
//用VP的逆矩阵反向变换并除以w分量得到世界坐标位置,为什么除以w详细见前面数学推导的文章链接
float4 D=mul(_CurViewInserseMatrix,H);
float4 worldPos=D/D.w; //分别得到前一帧和当前帧的NDC坐标取差值计算速度方向
float4 curViewPos=H;
float4 preViewPos=mul(_PreViewMatrix,worldPos);
preViewPos/=preViewPos.w; //除以的系数可以根据自己的需求调整
float2 velocity=(curViewPos.xy-preViewPos.xy)/10.0f; float2 uv=i.uv.xy;
//纹理采样的速度权重,这里进行了前2帧的计算,包括当前帧总共3个值,值依次递减且保证和为1,不为1则需要进行额外的除法
//目的是为了让越之前的帧看上去效果越淡,轨迹逐渐消失
float velColRate[]={0.6,0.3,0.1};
//当前采样结果用权重最大的值0.6
fixed4 col = tex2D(_MainTex, uv)*velColRate[];
uv+=velocity*_BlurSize; for(int it=;it<;it++)
{
//前两帧采样结果依次递减,0.3,0.1
fixed4 curCol=tex2D(_MainTex,uv.xy)*velColRate[it];
//将所有结果加起来,保证权重为1
col+=curCol;
//按速度方便对纹理进行偏移,并用模糊系数加以控制
uv+=velocity*_BlurSize;
} return fixed4(col.rgb,1.0);
}
ENDCG
}
}
FallBack Off
}

效果如下:

最新文章

  1. Git 进阶指南
  2. No.009:Palindrome Number
  3. 手把手教你调试Linux C++ 代码(一步到位包含静态库和动态库调试)
  4. Nginx 简单的负载均衡配置示例(转载)
  5. Maven详细介绍
  6. POJ 3468 A Simple Problem with Integers (伸展树区间更新求和操作 , 模板)
  7. OutputCache祥解
  8. 客户机增加域 及server文件共享
  9. windows安装nvm管理node版本
  10. Python学习笔记(五)
  11. Python——爬虫——数据提取
  12. 全文搜索引擎Elasticsearch入门实践
  13. nginx访问502 gateway,*1 connect() failed (111: Connection refused) while connecting to upstream
  14. Redis分布式锁服务(转)
  15. docker 知识点汇总
  16. [转]Linux联网问题
  17. DevExpress的安装方法与破解教程【转】
  18. Zookeeper系统设计的优点
  19. 【Matlab】运动目标检测之“帧差法”
  20. 机器学习-kNN-数据归一化

热门文章

  1. for循环用了那么多次,但你真的了解它么?
  2. MySQL UNSIGNED和ZEROFILL属性
  3. 内核态发生非法地址访问是否会panic
  4. 为何我建议1-3年的Java程序员仔细看看这篇文章
  5. sqlite3数据库最大可以是多大?可以存放多少数据?读写性能怎么样?
  6. AFNetworking遇到错误 Request failed: unacceptable content-type: text/html
  7. SparkSql 整合 Hive
  8. iPad替代midi键盘
  9. Linux下迅速删除一个大文件夹
  10. 密度峰值聚类算法MATLAB程序