本次利用速度映射图方法实现运动模糊。速度映射图中存储了每个像素的速度,然后使用这个速度来决定模糊的方向和大小。

速度缓冲生成的方法:

(1)把场景中所有物体的速度渲染到一张纹理中。但这种方法的缺点在于需要修改场景中所有物体的Shader代码,使其添加计算速度的代码并输出到一个渲染纹理中。

(2)利用深度纹理在片元着色器中为每个像素计算其在世界空间下的位置,这是通过使用当前的视角*投影矩阵的逆矩阵对NDC下的顶点坐标进行变换得到的。当得到世界空间中的顶点坐标后,我们使用前一帧的视角*投影矩阵对其进行变换,得到该位置在前一帧中的NDC坐标。然后,我们计算前一帧和当前帧的位置差,生成该像素的速度。这种方法的优点是可以在一个屏幕后处理步骤中完成整个效果的模拟,但缺点是需要在片元着色器中进行两次矩阵乘法的操作,对性能有所影响。

本次利用第二种方法实现运动模糊。

后处理代码:

using UnityEngine;
using System.Collections;public class MotionBlurWithDepthTexture : PostEffectsBase {public Shader motionBlurShader;private Material motionBlurMaterial = null;public Material material {  get {motionBlurMaterial = CheckShaderAndCreateMaterial(motionBlurShader, motionBlurMaterial);return motionBlurMaterial;}  }//由于本节需要得到摄像机的视角和投影矩阵,我们需要定义一个Camera类型的变量//以获取该脚本所在的摄像机组件:private 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;    //保存上一帧摄像机的视角*投影矩阵void OnEnable() {camera.depthTextureMode |= DepthTextureMode.Depth;    //获取摄像机的深度纹理previousViewProjectionMatrix = camera.projectionMatrix * camera.worldToCameraMatrix;}void OnRenderImage (RenderTexture src, RenderTexture dest) {if (material != null) {material.SetFloat("_BlurSize", blurSize);material.SetMatrix("_PreviousViewProjectionMatrix", previousViewProjectionMatrix);Matrix4x4 currentViewProjectionMatrix = camera.projectionMatrix * camera.worldToCameraMatrix;Matrix4x4 currentViewProjectionInverseMatrix = currentViewProjectionMatrix.inverse;material.SetMatrix("_CurrentViewProjectionInverseMatrix", currentViewProjectionInverseMatrix);previousViewProjectionMatrix = currentViewProjectionMatrix;Graphics.Blit (src, dest, material);} else {Graphics.Blit(src, dest);}}
}

OnRenderImage函数:我们首先需要计算和传递运动模糊使用的各个属性。本例需要使用两个变换矩阵——前一帧的视角 *投影矩阵以及当前帧的视角*投影矩阵的逆矩阵。因此,我们通过调用camera.worldToCameraMatrix和camera.projectionMatrix来分别得到当前摄像机的视角矩阵和投影矩阵。对它们相乘后取逆,得到当前帧的视角*投影矩阵的逆矩阵,并传递给材质。然后,我们把取逆前的结果存储在previousViewProjectionMatrix变量中,以便在下一帧时传递给材质的_PreviousViewProjectionMatrix 属性。

Shader代码:

Shader "Unity Shaders Book/Chapter 13/Motion Blur With Depth Texture" {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_TOPif (_MainTex_TexelSize.y < 0)o.uv_depth.y = 1 - o.uv_depth.y;#endifreturn o;}fixed4 frag(v2f i) : SV_Target {// 对深度纹理进行采样,得到深度值float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth);// 构建像素的NDC坐标H,同理得到NDC的xy分量,范围均为[-1,1]float4 H = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, d * 2 - 1, 1);// Transform by the view-projection inverse.float4 D = mul(_CurrentViewProjectionInverseMatrix, H);// Divide by w to get the world position. float4 worldPos = D / D.w;// Current viewport position float4 currentPos = H;// 前一帧在NDC下的坐标float4 previousPos = mul(_PreviousViewProjectionMatrix, worldPos);// Convert to nonhomogeneous points [-1,1] by dividing by w.previousPos /= previousPos.w;// 前一帧和当前帧在屏幕空间下的位置差,得到当前像素的速度float2 velocity = (currentPos.xy - previousPos.xy)/2.0f;float2 uv = i.uv;float4 c = tex2D(_MainTex, uv);uv += velocity * _BlurSize;for (int it = 1; it < 3; it++, uv += velocity * _BlurSize) {float4 currentColor = tex2D(_MainTex, uv);c += currentColor;}c /= 3;return fixed4(c.rgb, 1.0);}ENDCGPass {      ZTest Always Cull Off ZWrite OffCGPROGRAM  #pragma vertex vert  #pragma fragment frag  ENDCG  }} FallBack Off
}

_MainTex对应了输入的渲染纹理,_BlurSize 是模糊图像时使用的参数。我们注意到,虽然在脚本里设置了材质的PreviousViewProjectionMatrix 和CurrentViewProjectionInverseMatrix 属性,但并没有在Properties块中声明它们。这是因为Unity没有提供矩阵类型的属性,但我们仍然可以在CG代码块中定义这些矩阵,并从脚本中设置它们。

由于在本例中,我们需要同时处理多张渲染纹理,因此在DirectX这样的平台.上,我们需要.处理平台差异导致的图像翻转问题。在上面的代码中,我们对深度纹理的采样坐标进行了平台差异化处理,以便在类似DirectX的平台上,在开启了抗锯齿的情况下仍然可以得到正确的结果。

片元着色器:我们首先需要利用深度纹理和当前帧的视角*投影矩阵的逆矩阵来求得该像素在世界空间下的坐标。过程开始于对深度纹理的采样,我们使用内置的SAMPLE DEPTH _TEXTURE宏和纹理坐标对深度纹理进行采样,得到了深度值d。

float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth);

d是由NDC下的坐标映射而来的。我们想要构建像素的NDC坐标H,就需要把这个深度值重新映射回NDC。这个映射很简单,只需要使用原映射的反函数即可,即d*2-1。同样,NDC的xy分量可以由像素的纹理坐标映射而来(NDC下的xyz分量范围均为[-1, 1])。

float4 H = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, d * 2 - 1, 1);

当得到NDC下的坐标H后,我们就可以使用当前帧的视角*投影矩阵的逆矩阵对其进行变换,并把结果值除以它的w分量来得到世界空间下的坐标表示worldPos。

float4 D = mul(_CurrentViewProjectionInverseMatrix, H);
float4 worldPos = D / D.w;

一旦得到了世界空间下的坐标,我们就可以使用前- -帧的视角*投影矩阵对它进行变换,得到前一帧在NDC下的坐标previousPos。

float4 currentPos = H;
float4 previousPos = mul(_PreviousViewProjectionMatrix, worldPos);
previousPos /= previousPos.w;

然后,我们计算前一-帧和当 前帧在屏幕空间下的位置差,得到该像素的速度velocity。

float2 velocity = (currentPos.xy - previousPos.xy)/2.0f;

当得到该像素的速度后,我们就可以使用该速度值对它的邻域像素进行采样,相加后取平均值得到一一个模糊的效果。采样时我们还使用了_ BlurSize 来控制采样距离。

本节实现的运动模糊适用于场景静止、摄像机快速运动的情况,这是因为我们在计算时只考虑了摄像机的运动。因此,如果读者把本节中的代码应用到---个物体快速运动而摄像机静止的场景,会发现不会产生任何运动模糊效果。如果我们想要对快速移动的物体产生运动模糊的效果,就需要生成更加精确的速度映射图。读者可以在Unity自带的ImageEffect包中找到更多的运动模糊的实现方法。

UnityShader入门精要——运动模糊(2)相关推荐

  1. UnityShader入门精要——运动模糊

    运动模糊是真实世界中的摄像机的一种效果.如果在摄像机曝光时,拍摄场景发生了变化,就会产生模糊的画面. 运动模糊的实现有多种方法.一种实现方法是利用一块累积缓存(accumulation buffer ...

  2. [学习笔记]UnityShader入门精要_第12章_屏幕后处理效果

    12.2 调整屏幕的亮度,饱和度和对比度 C# CheckShaderAndCreateMaterial Range OnRenderImage Material.SetFloat Graphics ...

  3. 《UnityShader入门精要》总结(1)理论篇

    紫色:大类概念或简短有力的总结 蓝色:细分概念或重要部分 红色:重要的补充注释 第二章:渲染流程与流程分工 渲染的流程分三个阶段: 应用阶段(开发者控制阶段) 由开发者全权进行管理,控制场景内摄像机位 ...

  4. UnityShader入门精要-9

    目录 1. Unity的渲染路径 前向渲染路径 Unity中的前向渲染 延迟渲染 Unity的光源类型 Unity的光照衰减 Unity的阴影 1. Unity的渲染路径 Unity 5.0之前,有3 ...

  5. UnityShader入门精要——全局雾效

    基于屏幕后处理的全局雾效的关键是,根据深度纹理来重建每个像素在世界空间下的位置.我们在模拟运动模糊时已经实现了这个要求,即构建出当前像素的NDC坐标,再通过当前摄像机的视角*投影矩阵的逆矩阵来得到世界 ...

  6. UnityShader入门精要-屏幕后处理效果 亮度饱和度对比度、边缘检测、高斯模糊、bloom效果、运动模糊

    (从这里开始可能会记录的更简略一些,时间紧张想迅速读完这本书的主要内容,可能有的部分不会自己上手做) 屏幕后处理通常指渲染完整个场景得到屏幕图像后,再对图像进行操作,抓取屏幕可以使用OnRenderI ...

  7. UnityShader入门精要-3.3 UnityShader的结构

    一个UnityShader的基础结构如下所示: Shader "ShaderName"{Properties{//属性 } SubShader{//显卡A使用的子着色器 }SubS ...

  8. UnityShader入门精要笔记1——顶点/片元着色器结构与BRDF(基本光照模型)——实现漫反射

    文章目录 BRDF(基本光照模型) 实现漫反射 光线强度的计算 好现在开始写Shader 新建Shader 添加一个Properties语义块 添加SubShader和Pass. 使用CG/HLSL语 ...

  9. UnityShader入门精要——程序纹理

    程序纹理(Procedural Texture)指的是那些由计算机生成的图像,我们通常使用一些特定的算法来创建个性化图案或非常真实的自然元素,例如木头.石子等.使用程序纹理的好处在于我们可以使用各种参 ...

最新文章

  1. Python 写了一个网页版的「P图软件」,惊呆了!
  2. 关于MYSQL中like 检索汉字问题。
  3. python适合做什么生意_适合Python的5大练手项目,你练了么?
  4. java this self_[原]Javasript 关于self(that) = this用法的理解
  5. CSDN - 屏蔽百度广告
  6. [导入]一个Form验证的方案
  7. 双 JK 触发器 74LS112 逻辑功能。真值表_【数电笔记】时序逻辑电路设计举例
  8. Java项目:ssm党员管理系统
  9. eclipse官方下载安装、JDK官方下载安装和环境变量配置
  10. 自动驾驶芯片,合适的才是最好的
  11. 斑马打印机ZPL语言和EPL语言的区别是什么
  12. 该如何选择适合的服务器
  13. 微信中各种代码/符号合集
  14. oracle与sun的java_甲骨文吞Sun Java何去何从?
  15. JAVA毕业设计酒店管理系统设计与实现计算机源码+lw文档+系统+调试部署+数据库
  16. 秘宝 联想正式进入元宇宙 互联网巨头的数字藏品用的哪种技术?
  17. ansible问题记录--Timeout (12s) waiting for privilege escalation prompt
  18. 【Codecs系列】x265编码器(一):编译和运行
  19. uniapp 添加本地文件日志 并增加防抖(防止重复写入被覆盖)
  20. 计算机2016基础知识,计算机基础知识2016.doc

热门文章

  1. 解决chrome浏览器应用商店排版混乱问题
  2. 京东一元抢宝系统的数据库架构优化
  3. Excel|5个神技巧,提高你的数据分析效率~
  4. cv2 改变图片大小 resize
  5. MATLAB批量改变图片大小
  6. APICloud教程
  7. wifi计费认证系统php,TP-LINK认证计费系统 - TP-LINK官方网站
  8. 手动计算Q-Learning的一个实例
  9. 群内2018_4月讨论整理2
  10. 大冰--寻人启事--one