Unity shader 獲取深度的詳細數(shù)學原理

需要從shader?中獲取深度值善茎,主要涉及到渲染流水線中的以下幾個節(jié)點

1.觀察空間:觀察空間是以攝像機所在位置為原點的空間蚯舱。我們嘗試獲取的深度信息就是在這個空間之下愚隧。

2.裁剪空間(投影空間海蔽、齊次裁剪空間):將頂點轉換到此空間下來判斷是否在視椎體內(nèi)锥咸,從而裁剪掉不在攝像機視野內(nèi)的頂點;將頂點變換到此空間下可以方便做后續(xù)的投影工作。

3.屏幕空間:將沒有被裁剪的頂點從裁剪空間轉換到此空間欢唾,從而最終呈現(xiàn)在屏幕上且警。


主要涉及到的術語

1.裁剪矩陣(投影矩陣):

用于將頂點從觀察空間轉換到裁剪空間的矩陣。

經(jīng)過裁剪矩陣的操作后礁遣,頂點的x斑芜、y、z分量則被轉換到裁剪空間中祟霍。

裁剪空間下的頂點滿足以下條件的杏头,即為在視野內(nèi)的頂點:

-w<=x<=w;

-w<=y<=w;

-w<=z<=w;

以上裁剪原理與深度獲取關系不大沸呐,只需要注意:

經(jīng)過裁剪矩陣的操作后醇王,頂點的w?分量保存了頂點在觀察空間下的深度信息(觀察空間的頂點的z?分量)。


2.齊次除法(透視除法):

經(jīng)過裁剪矩陣的操作之后崭添,將裁剪空間下的頂點的x寓娩、y、z?分量分別除以w?分量的過程呼渣。此過程完成后棘伴,裁剪空間下的頂點將會轉換到NDC?中,即變換到一個各分量從-1到1長度為2的立方體內(nèi)部屁置。如果使用的是透視攝像機排嫌,因為z?分量保留了深度信息,經(jīng)過透視除法以后缰犁,越遠離攝像機的頂點其xy分量數(shù)值將越小(因為w?分量是觀察空間中頂點的z?分量怖糊,對于兩個頂點帅容,如果他們x、y?分量相同伍伤,而w?分量不同即深度不同并徘,越遠的頂點經(jīng)過透視除法后,其數(shù)值也就越腥呕辍)麦乞。齊次除法完成后,頂點的x劝评、y?分量再經(jīng)過簡單的縮放映射即可投射到屏幕空間二維坐標中姐直,而z?分量通過d = 0.5*z + 0.5?轉變?yōu)?-1?范圍并作為最終會存貯于深度圖中的數(shù)據(jù)。


3.屏幕空間:

渲染管線中最后一個流程將裁剪空間中的頂點通過透視除法和屏幕映射映射最終從3D?的裁剪空間轉換到2D?的空間中蒋畜,這個2D?空間就是屏幕空間声畏。屏幕空間是左下角為(0,0),右上角為(screenwidth, screenheight)的二維空間。


Shader?中獲取深度(unity 2019.4.19 urp)

Shader?中將頂點從裁剪空間轉換到屏幕空間(即齊次除法和屏幕映射)由底層完成插龄,而獲取深度的原理就是再現(xiàn)這一操作中某些步驟的過程愿棋。

通過頂點數(shù)據(jù)可以直接獲得頂點的深度;通過頂點數(shù)據(jù)和屏幕映射公式可以逆推獲得屏幕紋理坐標均牢,使用屏幕紋理坐標可以采樣深度圖糠雨,然后將深度圖中的數(shù)據(jù)逆推回模型空間就可以獲得需要的場景深度。

1.頂點著色器中需要完成將頂點從模型空間轉換到裁剪空間下的任務徘跪。

2.來到片元著色器中甘邀,經(jīng)過Unity?渲染流程的處理,頂點輸出的裁剪空間頂點(SV_POSITION?語義)坐標的xy?分量已經(jīng)做了透視除法和屏幕映射處理轉換到了屏幕空間當中真椿,z 分量也做了透視除法并轉換到(0鹃答,1)范圍內(nèi),w?分量仍然是觀察空間下的深度值突硝。


獲取頂點的深度值:

裁剪空間頂點坐標w?分量就是視角空間下的深度值测摔,將其除以遠裁面就是0-1?的深度值。

遠裁剪面的距離可以通過內(nèi)置的_ProjectionParams.z?變量來獲取解恰。?

或者比較傻的辦法:

1.上面講到片元著色器中锋八,裁剪空間中的頂點(SV_POSITION?語義)坐標的z?分量已經(jīng)做了透視除法并轉換到了(0,1)范圍中护盈,此時只需要將其逆推回觀察空間即可挟纱;

2.利用公式:(n表示遠裁剪面;f表示近裁剪面腐宋;d表示ndc中重映射后的深度值紊服,即第2步驟計算出的數(shù)值)

zv?= 1/((n-f/n*f)*d + 1/n)?計算出觀察空間下的深度值,將其除以遠裁剪面就是0-1范圍的觀察空間下的深度值胸竞,公式:

z01=1/((n-f)/n*d + f/n)

以上公式實際上是將渲染管線處理后的頂點逆推回觀察空間中欺嗤,實際上是結合了投影矩陣z?分量上的處理公式、透視除法公式卫枝、屏幕映射公式三個步驟逆推出來的公式煎饼。

之所以說這個方法很傻,是因為繞了一個圈校赤,因為頂點從裁剪空間轉到屏幕空間下又逆推回了觀察空間吆玖,之所以要介紹這種辦法,是因為在遇到需要獲取場景深度值的需求時马篮,我們可以拿到的數(shù)據(jù)(即深度圖)存儲的正是步驟1?中頂點z?分量的值沾乘。

由于頂點在轉換到裁剪空間時,其w?分量就是觀察空間下的z?分量积蔚,如果只需要頂點的深度意鲸,還是建議直接使用裁剪空間頂點的w?分量作為深度值。

?

獲取場景的深度值:

1.上面提到在片元著色器中,頂點坐標的xy?分量已經(jīng)從裁剪空間轉換到了屏幕空間怎顾。

2.直接將頂點坐標的xy?分量除以屏幕的長寬就可以得到0-1?范圍的uv?坐標(屏幕紋理坐標)读慎,屏幕的長寬可以通過內(nèi)置變量_ScreenParams?獲取。

3.對深度圖進行采樣: SAMPLE_TEXTURE2D_X(_CamearaDepthTexture,?sampler_CameraDepthTexture,?uv)

采樣深度圖需要在shader?中作如下聲明:

TEXTURE2D_X_FLOAT(_CameraDepthTexture);

SAMPLER(sampler_CameraDepthTexture);

4.利用公式:(n表示遠裁剪面槐雾;f表示近裁剪面夭委;d表示深度圖中的數(shù)據(jù),即第四步結果的x分量)

zv?= 1/((n-f/n*f)*d + 1/n)?計算出模型空間下的深度值募强,將其除以遠裁剪面就是0-1范圍的深度值株灸,公式:

z01=1/((n-f)/n*d + f/n)

也可以在頂點著色器中計算采樣坐標,但是因為從頂點到片元著色器有一個插值過程,所以不能在頂點著色器中進行齊次除法擎值,因此需要乘回w?分量慌烧,計算公式:(vc 為裁剪空間下的頂點坐標、vcw為該坐標的w分量)

vcw*(vc/vcw*0.5+0.5) => 0.5vc+0.5vcw

這也是內(nèi)置函數(shù)ComputeScreenPos?的實現(xiàn)鸠儿。

然后在片元著色器中進行齊次除法獲得uv?坐標屹蚊,然后從步驟3繼續(xù)往下執(zhí)行

如果在頂點著色器使用了ComputeScreenPos獲得Vs;

那么對于步驟5可以使用unity?內(nèi)部提供的兩個方法Linear01Depth进每、LinearEyeDepth?傳入屏幕紋理坐標和_ZBufferParams?來獲取觀察空間的場景深度和0-1線性深度汹粤,函數(shù)內(nèi)部的原理即為上述步驟5。這也是shaderGraph?中SceneDepth?節(jié)點的做法田晚。_ZBufferParams?是Unity?提供的內(nèi)置變量嘱兼,里面包含了遠近裁剪平面相關的預計算。?同理贤徒,頂點的坐標也可以使用這種方式來獲得芹壕,只不過頂點的深度我們可以直接通過齊次除法來取得。

源碼

Shader "Custom/Depth"

{

Properties

{

}

SubShader

{

Tags { "RenderType"="Transparent" "Queue"="Geometry" "RenderPipeline" = "UniversalRenderPipeline"}

LOD 200

Pass

{

HLSLPROGRAM

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

#pragma vertex vert

#pragma fragment frag

struct a2v

{

float4 vertex : POSITION;

};

struct v2f

{

float4 vertex : SV_POSITION;

//結合ComputeScreenPos 使用

//float4 screenUV : TEXCOORD5;

};

CBUFFER_START(UnityPerMaterial)

CBUFFER_END

TEXTURE2D_X_FLOAT(_CameraDepthTexture);

SAMPLER(sampler_CameraDepthTexture);

v2f vert(a2v i)

{

v2f o;

o.vertex = TransformObjectToHClip(i.vertex.xyz);

//需要在片元中做透視除法

//o.screenUV = ComputeScreenPos(o.vertex接奈,_ProjectionParams.x);

return o;

}

half4 frag(v2f i) : SV_TARGET

{

//頂點深度

half dv = i.vertex.w;

//頂點01 深度

half dv01 = i.vertex.w / _ProjectionParams.z;

//直接獲取屏幕紋理坐標

float2 screenUV = i.vertex.xy / _ScreenParams.xy;

//結合ComputeScreenPos 計算屏幕紋理坐標

//float2 screenUV = i.screenUV.xy / i.screenUV.w;

//深度圖中存儲的深度哪雕,需要逆推回到觀察空間

float dd = SAMPLE_TEXTURE2D_X(_CameraDepthTexture, sampler_CameraDepthTexture, screenUV).r;

//觀察空間中的0-1場景深度

float ds01 = Linear01Depth(dd, _ZBufferParams);

//觀察空間中的場景深度

float ds = LinearEyeDepth(dd, _ZBufferParams);

return ds01;

}

ENDHLSL

}

}

}

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鲫趁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌利虫,老刑警劉巖挨厚,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異糠惫,居然都是意外死亡疫剃,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門硼讽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巢价,“玉大人,你說我怎么就攤上這事∪蓝悖” “怎么了城菊?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長碉克。 經(jīng)常有香客問我凌唬,道長,這世上最難降的妖魔是什么漏麦? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任客税,我火速辦了婚禮,結果婚禮上撕贞,老公的妹妹穿的比我還像新娘更耻。我一直安慰自己,他們只是感情好捏膨,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布秧均。 她就那樣靜靜地躺著,像睡著了一般脊奋。 火紅的嫁衣襯著肌膚如雪熬北。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天诚隙,我揣著相機與錄音讶隐,去河邊找鬼。 笑死久又,一個胖子當著我的面吹牛巫延,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播地消,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼炉峰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了脉执?” 一聲冷哼從身側響起疼阔,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎半夷,沒想到半個月后婆廊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡巫橄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年淘邻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片湘换。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡宾舅,死狀恐怖统阿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情筹我,我是刑警寧澤扶平,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站崎溃,受9級特大地震影響蜻直,放射性物質發(fā)生泄漏。R本人自食惡果不足惜袁串,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一概而、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧囱修,春花似錦赎瑰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鲜漩,卻和暖如春源譬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背孕似。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工踩娘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人喉祭。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓养渴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親泛烙。 傳聞我的和親對象是個殘疾皇子理卑,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容