前幾天嘗試寫一個傳送門的shader,發現本身對座標之間的變換掌握的不夠熟練,趁着這陣子想整理shader相關的知識點,先把各類空間及之間轉換整理一下。編程
建模時在模型空間進行,模型自帶的座標均爲模型空間下的表示。
當模型被放到世界座標系中時,表達某個模型的位置使用的是世界空間下的座標,因此模型上對應的某一個點,必須相應的轉化爲世界空間下的座標。
從模型空間到世界空間的變換 叫作 模型變換。
Unity的Shader中,模型空間的座標由Renderer直接提供,做爲頂點着色器的輸入,語義爲POSITION。如:app
struct appdata { float4 vertex : POSITION; }
於咱們能看到的渲染圖像均是經過攝像機獲得的,爲了方便後續裁剪、投影等操做,因此在將模型從模型空間變換到世界空間以後,還須要將其轉換到觀察空間。所謂的觀察空間,就是以攝像機位置爲原點,攝像機局部座標軸爲座標軸的座標系。
從世界空間到觀察空間的變換叫作觀察變換(視圖變換)。this
座標轉換到觀察空間後,因爲直接使用攝像機的平截頭體進行裁剪比較複雜(平截頭體的邊界方程求交困難),因此須要將其轉化到裁剪空間。裁剪空間變換的思路是,對平截頭體進行縮放,使近裁剪面和遠裁剪面變成正方形,使座標的w份量表示裁剪範圍,此時,只須要簡單的比較x,y,z和w份量的大小便可。
從觀察空間到裁剪空間的變換叫作投影變換。
注意,雖然叫作投影變換,可是投影變換並無進行真正的投影。spa
在頂點着色器中,模型、觀察、裁剪空間的相關變換矩陣通常爲如下幾個:.net
別名 | 定義 | 含義 |
---|---|---|
UNITY_MATRIX_M | unity_ObjectToWorld | 模型變換矩陣 |
UNITY_MATRIX_V | unity_MatrixV | 視圖變換矩陣 |
UNITY_MATRIX_P | glstate_matrix_projection | 投影變換矩陣 |
UNITY_MATRIX_VP | unity_MatrixVP | 視圖投影變換矩陣 |
UNITY_MATRIX_MV | mul(unity_MatrixV, unity_ObjectToWorld) | 模型視圖變換 |
UNITY_MATRIX_MVP | mul(unity_MatrixVP, unity_ObjectToWorld) | 模型視圖投影變換 |
注意,最新版本中(筆者用的是Unity5.6.3p4),官方建議將座標點從模型空間轉換到裁剪空間時,應使用UnityObjectToClipPos方法,該方法內部定義爲:code
mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(pos, 1.0));
上述方法比起下面的順序更有效率。blog
mul(UNITY_MATRIX_MVP, appdata.vertex);
在定點着色器中,輸出即爲裁剪空間下的座標。接口
通過裁剪操做以後,咱們須要將裁剪空間的座標投影到屏幕空間。
這裏主要分紅兩個步驟:
1. 齊次除法
使用xyz分別處以w份量,獲得NDC(歸一化設備座標),通過這一步,可以看到的座標點變成了一個xy邊長爲1,z爲-1到1的立方體。
2. 屏幕映射
使用NDC座標和屏幕長寬像素直接映射,獲得屏幕空間下的xy座標。注意,雖然屏幕空間沒有深度,但屏幕空間下的座標仍然保留了z的深度值,能夠進行深度檢測或其餘處理。圖片
從裁剪空間到屏幕空間由unity直接進行。這裏還要記住,從裁剪空間到屏幕空間,插值是硬件直接進行的。ip
以後咱們從像素着色器中得到的語義爲SV_POSITION的輸入,其座標基本沒有太多用處了。
若是但願得到此時的屏幕座標,可使用VPOS或者WPOS,在Unity中,這二者同義。固然,使用上述語義須要
#pragma target 3.0
VPOS中的xy表明屏幕空間中的像素座標,注意,這裏的像素座標是中心點座標,不是整數,如屏幕分辨率爲400*300,則x的範圍爲[0.5, 400.5],y的範圍爲[0.5, 300.5]。z的範圍爲[0, 1],0是近裁剪面,1是遠裁剪面。w的範圍須要根據攝像機投影類型劃分,若是是透視投影,則其範圍爲[1/near,1/far],若是是正交投影,則恆爲1。
這裏還須要注意的是,若是使用VPOS或者WPOS做爲fs的輸入,就沒法同時使用SV_POSITION做爲輸入,因此vs和fs須要寫成以下方式:
Shader "Unlit/Screen Position" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 3.0 // note: no SV_POSITION in this struct struct v2f { float2 uv : TEXCOORD0; }; v2f vert ( float4 vertex : POSITION, // vertex position input float2 uv : TEXCOORD0, // texture coordinate input out float4 outpos : SV_POSITION // clip space position output ) { v2f o; o.uv = uv; outpos = UnityObjectToClipPos(vertex); return o; } sampler2D _MainTex; fixed4 frag (v2f i, UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target { // screenPos.xy will contain pixel integer coordinates. // use them to implement a checkerboard pattern that skips rendering // 4x4 blocks of pixels // checker value will be negative for 4x4 blocks of pixels // in a checkerboard pattern screenPos.xy = floor(screenPos.xy * 0.25) * 0.5; float checker = -frac(screenPos.r + screenPos.g); // clip HLSL instruction stops rendering a pixel if value is negative clip(checker); // for pixels that were kept, read the texture and output it fixed4 c = tex2D (_MainTex, i.uv); return c; } ENDCG } } }
攝像機和屏幕相關經常使用的內置變量以下表所示。
別名 | 類型 | 含義 |
---|---|---|
_WorldSpaceCameraPos | float3 | |
_ProjectionParams | float4 | |
_ScreenParams | float4 | |
_ZBufferParams | float4 | |
unity_OrthoParams | float4 | |
unity_CameraProjection | float4x4 | |
unity_CameraInvProjection | float4x4 | |
unity_CameraWroldClipPlanes[6] | float4 |
熟悉OpenGL編程的同窗都知道gl接口有一個glViewport,該接口其實就是肯定視口空間。視口空間是一個將屏幕座標對應到(0, 0)到(1, 1)範圍內的空間。
若是想獲得視口空間座標,則能夠經過下面兩種方法。
fixed4 frag(float4 sp : VPOS) : SV_Target { return fixed(sp.xy/_ScreenParams.xy, 0.0, 1.0); }
這裏的_ScreenParams中保存了屏幕分辨率。
另外一種方法以下:
struct vertOut { float4 pos : SV_POSITION; float4 srcPos : TEXCOORD0; }; vertOut vert(appdata_base v) { vertOut o; o.pos = UnityObjectToClipPos(v.vertex); o.srcPos = ComputeScreenPos(o.pos); return o; } fixed4 frag(vertOut i) : SV_Target { float2 wcoord = (i.srcPos.xy/i.scrPos.w); return fixed4(wcoord, 0.0, 1.0); }
這裏,ComputeScreenPos並無直接進行透視除法,緣由是插值是線性的,必須在透視除法以後進行,因此,咱們必須在fs中手動進行。
幾篇不錯的參考資料以下:
http://blog.csdn.net/wangdingqiaoit/article/details/51594408
http://blog.csdn.net/wangdingqiaoit/article/details/51589825