欢迎光临散文网 会员登陆 & 注册

编写 Unity Shader 的时候,语义 POSITION 和 SV_POSITION 的区别?

2023-05-30 14:55 作者:游戏开发RAIN  | 我要投稿

在编写 Unity Shader 的时候,经常会遇到语义 POSITION 和 SV_POSITION,它们都是表示顶点位置的语义。但是,它们之间有着一些微妙的区别。本文将详细介绍这两个语义的区别,并给出相应的示例。

一、POSITION

首先,我们来看一下 POSITION。在 Unity Shader 中,POSITION 表示的是顶点在模型空间中的位置。也就是说,这个语义表示的是从模型空间到世界空间的变换后的顶点位置。在顶点着色器中,我们可以使用 float4 类型的 POSITION 变量来表示顶点的位置:

void vert(inout appdata_full v, out Input o) {    // 计算顶点在模型空间中的位置    float4 pos = mul(unity_ObjectToWorld, v.vertex);        // 将顶点位置赋值给 POSITION    o.vertex = pos;    o.uv = TRANSFORM_TEX(v.uv, _MainTex); }


在上面的代码中,我们首先使用 unity_ObjectToWorld 矩阵将顶点从模型空间转换到世界空间,然后将变换后的顶点位置赋值给了 POSITION。这个 POSITION 变量将会被传递到像素着色器中,用来计算最终的颜色。

二、SV_POSITION

接下来,我们来看一下 SV_POSITION。在 Unity Shader 中,SV_POSITION 表示的是顶点在屏幕空间中的位置。也就是说,这个语义表示的是从世界空间到屏幕空间的变换后的顶点位置。在顶点着色器中,我们可以使用 float4 类型的 SV_POSITION 变量来表示顶点在屏幕空间中的位置:

void vert(inout appdata_full v, out Input o) {    // 计算顶点在模型空间中的位置    float4 pos = mul(unity_ObjectToWorld, v.vertex);        // 计算顶点在屏幕空间中的位置    o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);        // 将顶点位置赋值给 SV_POSITION    o.vertex = ComputeGrabScreenPos(o.vertex);    o.uv = TRANSFORM_TEX(v.uv, _MainTex); }


在上面的代码中,我们首先使用 unity_ObjectToWorld 矩阵将顶点从模型空间转换到世界空间,然后使用 UNITY_MATRIX_MVP 矩阵将顶点从世界空间转换到屏幕空间。最后,我们将变换后的顶点位置赋值给了 SV_POSITION。这个 SV_POSITION 变量将会被传递到像素着色器中,用来计算最终的颜色。

三、POSITION 和 SV_POSITION 的区别

从上面的示例中,我们可以看出 POSITION 和 SV_POSITION 的区别。具体来说,它们的区别如下:

  1. POSITION 表示的是顶点在模型空间中的位置,而 SV_POSITION 表示的是顶点在屏幕空间中的位置。

  2. POSITION 变量在顶点着色器中使用,用来计算最终的颜色,而 SV_POSITION 变量在像素着色器中使用,用来计算最终的颜色。

  3. POSITION 变量的值是从模型空间到世界空间的变换后的顶点位置,而 SV_POSITION 变量的值是从世界空间到屏幕空间的变换后的顶点位置。

四、示例

下面我们来看一个示例,来更好地理解 POSITION 和 SV_POSITION 的区别。假设我们有一个简单的立方体模型,我们需要将其绘制到屏幕上,并且在每个面上显示不同的颜色。我们可以使用以下的 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) {            // 计算顶点在模型空间中的位置            float4 pos = IN.worldPos;            // 将顶点位置赋值给 POSITION            o.Normal = pos.xyz;            o.Albedo = pos.xyz;            o.Alpha = 1.0;        }        ENDCG    }    FallBack "Diffuse" }


在上面的代码中,我们使用了 worldPos 变量来传递顶点在世界空间中的位置。在 surf 函数中,我们将 worldPos 赋值给了 POSITION,这样就可以在像素着色器中使用这个变量了。

接下来,我们需要修改一下顶点着色器,来计算顶点在屏幕空间中的位置,并将其赋值给 SV_POSITION 变量。修改后的代码如下:


void vert (inout appdata_full v) {    // 计算顶点在模型空间中的位置    float4 pos = v.vertex;    // 将顶点位置赋值给 POSITION    v.normal.xyz = pos.xyz;    v.tangent.xyz = pos.xyz;    v.texcoord.xy = TRANSFORM_TEX(v.texcoord, _MainTex);    // 计算顶点在屏幕空间中的位置    v.vertex = mul(UNITY_MATRIX_MVP, v.vertex);    // 将顶点位置赋值给 SV_POSITION    ComputeGrabScreenPos(v.vertex); }


在上面的代码中,我们首先计算了顶点在模型空间中的位置,然后将其赋值给了 normal 和 tangent 变量。这里使用 normal 和 tangent 变量来传递顶点的位置是因为 Surface Shader 中没有 POSITION 变量。接着,我们使用 UNITY_MATRIX_MVP 矩阵将顶点从世界空间转换到屏幕空间,并将变换后的顶点位置赋值给了 SV_POSITION 变量。

最后,我们需要在像素着色器中使用 SV_POSITION 变量来计算最终的颜色。修改后的代码如下:


void surf (Input IN, inout SurfaceOutputStandard o) {    // 计算世界空间中的顶点位置    float3 worldPos = mul(unity_ObjectToWorld, IN.worldPos).xyz;    // 计算顶点在屏幕空间中的位置    float4 screenPos = ComputeGrabScreenPos(IN.worldPos);    // 根据屏幕空间中的位置来计算颜色    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 矩阵将顶点从模型空间转换到世界空间,然后计算顶点在屏幕空间中的位置,并根据屏幕空间中的位置来计算颜色。这里我们将 x 坐标小于 0.5 的面设置为红色,将 x 坐标在 0.5 到 1.0 之间的面设置为绿色,将 x 坐标大于 1.0 的面设置为蓝色。

五、总结

在编写 Unity Shader 的时候,POSITION 和 SV_POSITION 都是表示顶点位置的语义。它们之间的区别在于,POSITION 表示的是顶点在模型空间中的位置,而 SV_POSITION 表示的是顶点在屏幕空间中的位置。在顶点着色器中,我们可以使用 POSITION 变量来表示顶点的位置,在像素着色器中,我们可以使用 SV_POSITION 变量来表示顶点的位置。在实际应用中,我们需要根据具体的需求来选择使用哪个语义。


编写 Unity Shader 的时候,语义 POSITION 和 SV_POSITION 的区别?的评论 (共 条)

分享到微博请遵守国家法律