編寫 Unity Shader 的時候,語義 POSITION 和 SV_POSITION 的區(qū)別?
在編寫 Unity Shader 的時候,經(jīng)常會遇到語義 POSITION 和 SV_POSITION,它們都是表示頂點(diǎn)位置的語義。但是,它們之間有著一些微妙的區(qū)別。本文將詳細(xì)介紹這兩個語義的區(qū)別,并給出相應(yīng)的示例。
一、POSITION
首先,我們來看一下 POSITION。在 Unity Shader 中,POSITION 表示的是頂點(diǎn)在模型空間中的位置。也就是說,這個語義表示的是從模型空間到世界空間的變換后的頂點(diǎn)位置。在頂點(diǎn)著色器中,我們可以使用 float4 類型的 POSITION 變量來表示頂點(diǎn)的位置:
void vert(inout appdata_full v, out Input o)
{
? ?// 計算頂點(diǎn)在模型空間中的位置
? ?float4 pos = mul(unity_ObjectToWorld, v.vertex);
? ?
? ?// 將頂點(diǎn)位置賦值給 POSITION
? ?o.vertex = pos;
? ?o.uv = TRANSFORM_TEX(v.uv, _MainTex);
}
在上面的代碼中,我們首先使用 unity_ObjectToWorld 矩陣將頂點(diǎn)從模型空間轉(zhuǎn)換到世界空間,然后將變換后的頂點(diǎn)位置賦值給了 POSITION。這個 POSITION 變量將會被傳遞到像素著色器中,用來計算最終的顏色。
二、SV_POSITION
接下來,我們來看一下 SV_POSITION。在 Unity Shader 中,SV_POSITION 表示的是頂點(diǎn)在屏幕空間中的位置。也就是說,這個語義表示的是從世界空間到屏幕空間的變換后的頂點(diǎn)位置。在頂點(diǎn)著色器中,我們可以使用 float4 類型的 SV_POSITION 變量來表示頂點(diǎn)在屏幕空間中的位置:
void vert(inout appdata_full v, out Input o)
{
? ?// 計算頂點(diǎn)在模型空間中的位置
? ?float4 pos = mul(unity_ObjectToWorld, v.vertex);
? ?
? ?// 計算頂點(diǎn)在屏幕空間中的位置
? ?o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
? ?
? ?// 將頂點(diǎn)位置賦值給 SV_POSITION
? ?o.vertex = ComputeGrabScreenPos(o.vertex);
? ?o.uv = TRANSFORM_TEX(v.uv, _MainTex);
}
在上面的代碼中,我們首先使用 unity_ObjectToWorld 矩陣將頂點(diǎn)從模型空間轉(zhuǎn)換到世界空間,然后使用 UNITY_MATRIX_MVP 矩陣將頂點(diǎn)從世界空間轉(zhuǎn)換到屏幕空間。最后,我們將變換后的頂點(diǎn)位置賦值給了 SV_POSITION。這個 SV_POSITION 變量將會被傳遞到像素著色器中,用來計算最終的顏色。
三、POSITION 和 SV_POSITION 的區(qū)別
從上面的示例中,我們可以看出 POSITION 和 SV_POSITION 的區(qū)別。具體來說,它們的區(qū)別如下:
POSITION 表示的是頂點(diǎn)在模型空間中的位置,而 SV_POSITION 表示的是頂點(diǎn)在屏幕空間中的位置。
POSITION 變量在頂點(diǎn)著色器中使用,用來計算最終的顏色,而 SV_POSITION 變量在像素著色器中使用,用來計算最終的顏色。
POSITION 變量的值是從模型空間到世界空間的變換后的頂點(diǎn)位置,而 SV_POSITION 變量的值是從世界空間到屏幕空間的變換后的頂點(diǎn)位置。
四、示例
下面我們來看一個示例,來更好地理解 POSITION 和 SV_POSITION 的區(qū)別。假設(shè)我們有一個簡單的立方體模型,我們需要將其繪制到屏幕上,并且在每個面上顯示不同的顏色。我們可以使用以下的 Shader:
Shader "Custom/ColorCube" {
? ?Properties {
? ? ? ?_MainTex ("Texture", 2D) = "white" {}
? ?}
? ?SubShader {
? ? ? ?Tags { "RenderType"="Opaque" }
? ? ? ?CGPROGRAM
? ? ? ?#pragma surface surf Standard
? ? ? ?struct Input {
? ? ? ? ? ?float2 uv_MainTex;
? ? ? ? ? ?float4 worldPos;
? ? ? ?};
? ? ? ?sampler2D _MainTex;
? ? ? ?void surf (Input IN, inout SurfaceOutputStandard o) {
? ? ? ? ? ?// 計算頂點(diǎn)在模型空間中的位置
? ? ? ? ? ?float4 pos = IN.worldPos;
? ? ? ? ? ?// 將頂點(diǎn)位置賦值給 POSITION
? ? ? ? ? ?o.Normal = pos.xyz;
? ? ? ? ? ?o.Albedo = pos.xyz;
? ? ? ? ? ?o.Alpha = 1.0;
? ? ? ?}
? ? ? ?ENDCG
? ?}
? ?FallBack "Diffuse"
}
在上面的代碼中,我們使用了 worldPos 變量來傳遞頂點(diǎn)在世界空間中的位置。在 surf 函數(shù)中,我們將 worldPos 賦值給了 POSITION,這樣就可以在像素著色器中使用這個變量了。
接下來,我們需要修改一下頂點(diǎn)著色器,來計算頂點(diǎn)在屏幕空間中的位置,并將其賦值給 SV_POSITION 變量。修改后的代碼如下:
void vert (inout appdata_full v) {
? ?// 計算頂點(diǎn)在模型空間中的位置
? ?float4 pos = v.vertex;
? ?// 將頂點(diǎn)位置賦值給 POSITION
? ?v.normal.xyz = pos.xyz;
? ?v.tangent.xyz = pos.xyz;
? ?v.texcoord.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
? ?// 計算頂點(diǎn)在屏幕空間中的位置
? ?v.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
? ?// 將頂點(diǎn)位置賦值給 SV_POSITION
? ?ComputeGrabScreenPos(v.vertex);
}
在上面的代碼中,我們首先計算了頂點(diǎn)在模型空間中的位置,然后將其賦值給了 normal 和 tangent 變量。這里使用 normal 和 tangent 變量來傳遞頂點(diǎn)的位置是因為 Surface Shader 中沒有 POSITION 變量。接著,我們使用 UNITY_MATRIX_MVP 矩陣將頂點(diǎn)從世界空間轉(zhuǎn)換到屏幕空間,并將變換后的頂點(diǎn)位置賦值給了 SV_POSITION 變量。
最后,我們需要在像素著色器中使用 SV_POSITION 變量來計算最終的顏色。修改后的代碼如下:
void surf (Input IN, inout SurfaceOutputStandard o) {
? ?// 計算世界空間中的頂點(diǎn)位置
? ?float3 worldPos = mul(unity_ObjectToWorld, IN.worldPos).xyz;
? ?// 計算頂點(diǎn)在屏幕空間中的位置
? ?float4 screenPos = ComputeGrabScreenPos(IN.worldPos);
? ?// 根據(jù)屏幕空間中的位置來計算顏色
? ?if (screenPos.x < 0.5) {
? ? ? ?o.Albedo = float3(1, 0, 0);
? ?} else if (screenPos.x < 1.0) {
? ? ? ?o.Albedo = float3(0, 1, 0);
? ?} else {
? ? ? ?o.Albedo = float3(0, 0, 1);
? ?}
? ?o.Normal = worldPos;
? ?o.Alpha = 1.0;
}
在上面的代碼中,我們首先使用 unity_ObjectToWorld 矩陣將頂點(diǎn)從模型空間轉(zhuǎn)換到世界空間,然后計算頂點(diǎn)在屏幕空間中的位置,并根據(jù)屏幕空間中的位置來計算顏色。這里我們將 x 坐標(biāo)小于 0.5 的面設(shè)置為紅色,將 x 坐標(biāo)在 0.5 到 1.0 之間的面設(shè)置為綠色,將 x 坐標(biāo)大于 1.0 的面設(shè)置為藍(lán)色。
五、總結(jié)
在編寫 Unity Shader 的時候,POSITION 和 SV_POSITION 都是表示頂點(diǎn)位置的語義。它們之間的區(qū)別在于,POSITION 表示的是頂點(diǎn)在模型空間中的位置,而 SV_POSITION 表示的是頂點(diǎn)在屏幕空間中的位置。在頂點(diǎn)著色器中,我們可以使用 POSITION 變量來表示頂點(diǎn)的位置,在像素著色器中,我們可以使用 SV_POSITION 變量來表示頂點(diǎn)的位置。在實(shí)際應(yīng)用中,我們需要根據(jù)具體的需求來選擇使用哪個語義。