• Unity Shader 屏幕后效果——摄像机运动模糊(速度映射图实现)


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

    推导过程如下:

    先由深度纹理逆推出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.此脚本挂载在摄像机上,用于模糊参数调控,深度纹理准备,矩阵传递:

     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://www.cnblogs.com/koshio0219/p/11131619.html

    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 }

    效果如下:

     

  • 相关阅读:
    4、springboot之全局异常捕获
    3、springboot之热部署
    可重入锁
    2、springboot返回json
    1、springboot之HelloWorld
    [转]查询 SQL Server 系统目录常见问题
    设计模式原则详解
    [转]第二章 控制反转和依赖注入
    [转]Spring.Net介绍
    [转]Oracle High Water Level高水位分析
  • 原文地址:https://www.cnblogs.com/koshio0219/p/11199547.html
Copyright © 2020-2023  润新知