Re:《Unity Shader入門精要》13.3全域性霧效--如何從深度紋理重構世界座標

只懂得unity的JL發表於2022-03-17

如何從深度紋理重構世界座標

遊戲特效,後處理是必不可少的,而後處理經常需要我們得到當前畫面的畫素對應世界空間的所有資訊。

思路

  1. 通過深度紋理取得NDC座標,然後再通過NDC座標還原成世界空間座標
//csharp指令碼部分
Matrix4x4 matrix = camera.projectionMatrix * camera * worldToCameraMatrix;
Matrix4x4 InverseMatrix = matrix.inverse;

//shader部分
float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth);
float4 H = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, d * 2 - 1, 1);
float D  = mul(InverseMatrix, H);
float worldPos = D/D.w

計算量比較大,一般用下面的方法。
2. 通過得到相機的座標以及在世界空間下畫素和相機的偏移量,通過兩者相加得到畫素的世界座標。這裡偏移量通過頂點的線性深度乘以頂點著色器輸出並被插值後所得的相機指向畫素的射線來得到。

float4 worldPos = _WorldSpaceCameraPos + linearDepth * interpolatedRay;

接下來來介紹這種辦法計算的過程。
我們很容易計算出相機到近平面四個頂點的向量方向以及世界空間下的長度,只需要得到相機記錄的fov,aspect等資訊
在這裡插入圖片描述
在這裡插入圖片描述
我們要得到的是畫素的世界座標。
這裡有個很重要的點,相機能看到這個畫素,必然說明原本的世界座標的點和在近平面的投影點(畫素位置)處於相機相機指向畫素的射線的方向上。也就是下面13.7圖。
在這裡插入圖片描述
所以問題轉為如何得出相機到畫素所對應的世界座標的向量的長度
很幸運的是我們擁有深度紋理,所以13.7圖的depth是已知的。
那麼如何通過depth來得到dist,這裡書本上舉例的是TL點的計算。
在這裡插入圖片描述
單單看這個點的計算是沒問題的,自己手畫一個就能得出結果。但關鍵是為什麼所有點都滿足,這裡設世界座標下任意一點A。
請新增圖片描述
其和相機的連線經過近平面併產生交點
請新增圖片描述
畫出near向量
請新增圖片描述
延長near到長度和A點的z變數,也就是depth一樣長,並且做三角形OAB和ODC
這張有點亂,希望大家不要嫌棄,真的不太懂怎麼安排線
請新增圖片描述
很容易發現三角形ODC ~ 三角形OAB
得到長度關係
|near| / |interpolatedRay| = |depth| / |dist|
對任意點都是適用的

所以
|dist| = |depth| * (|interpolatedRay| / |near|)
這裡的depth是線性深度,也就是座標的z分量。
對應書上片元著色器的部分就是這部分程式碼。
在這裡插入圖片描述
而書本計算interpolatedRay藉助了Shader的插值。
那麼現在關鍵就在於平面的四個頂點的(interpolatedRay / |near|)在頂點著色器輸出後插值得到的到底是不是每個畫素對應的 (interpolatedRay / |near|) 。其次|near|是一個常數,所以只需要論證interpolatedRay是否正確即可。

求助

可惜的是我網上找了很久還是不知道向量是如何插值的,希望有大佬知道的可以告訴我。

2D情況下

可能只想要得到近平面的畫素點(平面本身構成的圖片的畫素)在世界空間下的位置,只需要計算出相機到近平面的四個頂點的向量直接插值,無需任何深度紋理的資訊。
也就是書上的這部分程式碼無需乘scale
在這裡插入圖片描述
並且計算公式變為

float4 worldPos = _WorldSpaceCameraPos + interpolatedRay;

這樣子的情況下,霧效的產生將和相機直接掛鉤,相機的近平面就是霧效產生的地方,可以在2D遊戲中使用。

相關文章