1. 程式人生 > >獲取深度和法線紋理 背後的原理【Unity Shader入門精要13.1.1】

獲取深度和法線紋理 背後的原理【Unity Shader入門精要13.1.1】

 

13.1.1背後的原理

什麼是深度紋理:

實際上就是一張渲染紋理,只不過它裡面儲存的畫素值不是顏色值,而是一個高精度的深度值,由於被儲存在一張紋理中,深度紋理裡的深度範圍是[0,1],而且通常是 非線性分佈的。

深度值來自頂點變換後得到的歸一化的裝置座標(Normalized Device Coordinates,NDC)

一個模型想要最終被繪製在螢幕上,需要把它的頂點從模型空間變換到齊次裁剪座標系下,這是通過在頂點著色器中乘以MVP變換矩陣得到的,在變換的最後一步,需要使用一個投影矩陣來變換頂點。當用的是透視投影型別的攝像機時,這個投影矩陣就是非線性的了。正交投影使用的變換矩陣是線性的。

線性關係:兩個變數之間存在 一次方函式 關係

線性迴歸:(Linear Regression)利用線性迴歸方程的最小平方幻術對一個或多個自變數之間的關係進行建模的一種迴歸分析。

深度值對應了NDC中頂點座標的Z分量的值,由於NDC中Z分量的範圍是[0,1],為了讓這些值能儲存在一張影象中,需要進行對映:

對映公式:  d=0.5\cdot z_{ndc}+0.5


如何得到深度紋理:

在Unity中,深度紋理來自於:

【1】直接來自於真正的深度快取:當使用 延遲渲染路徑(包括遺留的延遲渲染路徑)時,可以訪問到,資訊儲存在G-buffer中。

【2】由一個單獨的Pass渲染得到:無法直接獲取深度快取時

實現:unity使用著色器替換(Shader Replacement)技術選擇渲染型別(即SubShader的RenderType標籤)為Opaque的物體,判斷他們使用的渲染佇列是否小於等於(內建的Background,Geometry和AlphaTest渲染佇列均在此範圍內),如果滿足條件,就把他渲染到深度和法線紋理中,因此,想讓物體能夠出現在深度和法線紋理中,就必須在shade中設定正確的RenderType標籤。

 

在unity中,可以讓一個攝像機生成一張深度紋理 或 一張深度紋理 + 法線紋理

一:生成一張深度紋理:

  1. unity直接獲取深度快取
  2. 用著色器替換技術,選取需要的不透明物體,並使用它投射陰影時使用的Pass(即LightMode被設定為ShadowCaster的PSS)得到深度紋理,如果Shader中不包含這樣一個Pass,那這個物體就不會出現在深度紋理中(當然,它也就不能向其他物體投射陰影)
  • 深度紋理的精度通常是24位或16位,這取決於使用的深度快取的精度

二: 一張深度紋理 + 法線紋理:

  • unity會建立一張和螢幕解析度相同,精度為32位(每個通道為8位,四個通道)的紋理,其中觀察空間下的法線資訊會被編碼進紋理的R和G通道,深度資訊會被編碼進B和A通道。

法線資訊的獲取在延遲渲染中是可以非常容易就得到的,unity只需要合併深度和法線快取即可,而在向前渲染中,預設情況下是不會建立法線快取的,因此unity底層使用了一個單獨的pass把整個場景再渲染一遍來完成,


13.1.2如何獲取

深度紋理的獲取

1>指令碼中設定攝像機的depthTexture

camera.depthTextureMode=DepthTextureMode.Depth;

2>在shader中宣告_CameraDepthTexture變數來訪問

獲取深度加法線

1>指令碼中設定攝像機的depthTexture

camera.depthTextureMode=DepthTextureMode.DepthNormals;

2>在shader中宣告_CameraDepthNormalsTexture變數來訪問,

同時產生一張深度 和 深度加法線紋理

1>指令碼中設定攝像機的depthTexture

camera.depthTextureMode|=DepthTextureMode.Depth;
camera.depthTextureMode|=DepthTextureMode.DepthNormals;

2>在shader中宣告_CameraDepthTexture變數來訪問深度,宣告_CameraDepthNormalsTexture變數來訪問深度加法線,

 

可以在攝像機的Camera元件上看到當前攝像機是否需要渲染深度或深度加法線

當在shader中訪問到深度紋理_CameraDepthTexture後,就可以使用當前畫素的紋理座標對它進行取樣,絕大多數情況下直接使用tex2D函式取樣即可,但在你某些平臺上(如PS3和PSP2),需要進行一些特殊處理,用unity提供的內建巨集

【SAMPLE_DEPTH_TEXTURE】巨集用來處理平臺差異

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

i.uv:float2的型別的變數,對應了當前畫素的紋理座標

......................巴拉巴拉 ...........P269

 

當通過紋理取樣得到深度值後,這些值旺旺是非線性的,這種非線性來自於透視投影使用的裁剪矩陣。然而計算過程中通常是需要線性的深度值