zoukankan      html  css  js  c++  java
  • 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 }

    效果如下:

     

  • 相关阅读:
    【LeetCode OJ】Remove Element
    【LeetCode OJ】Remove Duplicates from Sorted Array
    【LeetCode OJ】Swap Nodes in Pairs
    【LeetCode OJ】Merge Two Sorted Lists
    【LeetCode OJ】Remove Nth Node From End of List
    【LeetCode OJ】Two Sum
    【LeetCode OJ】Majority Element
    最长公共子序列问题
    php fopen与file_get_contents的区别
    PHP 技巧集合
  • 原文地址:https://www.cnblogs.com/koshio0219/p/11199547.html
Copyright © 2011-2022 走看看