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 }

效果如下:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM