版本:Unity 6 (6000.0)
语言:英语
内置渲染管线中的全局照明表面着色器示例
内置渲染管线的表面着色器语言参考

内置渲染管线中的曲面细分表面着色器示例

在内置的 渲染管线一系列将场景内容显示在屏幕上的操作。 Unity 允许您从预制渲染管线中选择,或编写自己的渲染管线。 更多信息
参见 词汇表
中,表面着色器一种为内置渲染管线编写着色器的简化方法。 更多信息
参见 词汇表
对 DirectX 11 / OpenGL Core GPU 曲面细分具有一定的支持。

  • 曲面细分由 tessellate:FunctionName 修饰符指示。该函数计算三角形边和内部的曲面细分因子。
  • 当使用曲面细分时,“顶点修饰符”(vertex:FunctionName)在曲面细分之后调用,针对域中的每个生成的顶点 着色器在 GPU 上运行的程序。 更多信息
    参见 词汇表
    。在此,您通常会执行位移映射。
  • 表面着色器可以选择计算 Phong 曲面细分 以平滑模型表面,即使没有位移映射。

曲面细分与几何着色器

表面着色器支持简单的曲面细分和位移。当您编写自定义 着色器程序 时,可以使用完整的 DX11 着色器模型 5.0 功能集,包括几何着色器、外壳着色器和域着色器。

曲面细分支持的当前限制

  • 仅限三角形域 - 没有 四边形类似于平面但边长为一个单位的原始对象,它仅使用 4 个顶点,并且表面位于局部坐标空间的 XY 平面中。 更多信息
    参见 词汇表
    ,没有等值线曲面细分。
  • 当您使用曲面细分时,着色器将自动编译为着色器模型 4.6 目标,这将阻止在旧的图形目标上运行。

渲染管线兼容性

功能名称 通用渲染管线 (URP) 高清渲染管线 (HDRP) 自定义 SRP 内置渲染管线
表面着色器

有关在 URP 中创建着色器对象的简化方法,请参见 着色器图


有关在 HDRP 中创建着色器对象的简化方法,请参见 着色器图

固定数量的曲面细分

如果模型的面在屏幕上的大小大致相同,请向 网格Unity 中主要的图形基元。网格构成 3D 世界的大部分内容。 Unity 支持三角形或四边形多边形网格。 NURBS、NURMS、细分曲面必须转换为多边形。 更多信息
参见 词汇表
添加固定数量的曲面细分(在整个网格上使用相同的曲面细分级别)。

以下示例脚本应用固定数量的曲面细分。

    Shader "Tessellation Sample" {
        Properties {
            _Tess ("Tessellation", Range(1,32)) = 4
            _MainTex ("Base (RGB)", 2D) = "white" {}
            _DispTex ("Disp Texture", 2D) = "gray" {}
            _NormalMap ("Normalmap", 2D) = "bump" {}
            _Displacement ("Displacement", Range(0, 1.0)) = 0.3
            _Color ("Color", color) = (1,1,1,0)
            _SpecColor ("Spec color", color) = (0.5,0.5,0.5,0.5)
        }
        SubShader {
            Tags { "RenderType"="Opaque" }
            LOD 300
            
            CGPROGRAM
            #pragma surface surf BlinnPhong addshadow fullforwardshadows vertex:disp tessellate:tessFixed nolightmap
            #pragma target 4.6

            struct appdata {
                float4 vertex : POSITION;
                float4 tangent : TANGENT;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };

            float _Tess;

            float4 tessFixed()
            {
                return _Tess;
            }

            sampler2D _DispTex;
            float _Displacement;

            void disp (inout appdata v)
            {
                float d = tex2Dlod(_DispTex, float4(v.texcoord.xy,0,0)).r * _Displacement;
                v.vertex.xyz += v.normal * d;
            }

            struct Input {
                float2 uv_MainTex;
            };

            sampler2D _MainTex;
            sampler2D _NormalMap;
            fixed4 _Color;

            void surf (Input IN, inout SurfaceOutput o) {
                half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
                o.Albedo = c.rgb;
                o.Specular = 0.2;
                o.Gloss = 1.0;
                o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));
            }
            ENDCG
        }
        FallBack "Diffuse"
    }

在上面的示例中,tessFixed 曲面细分函数以单个 float4 值返回四个曲面细分因子:三个因子对应于三角形的每条边,一个因子对应于三角形的内部。

该示例返回在材质属性中设置的常量值。


基于距离的曲面细分

您还可以根据与 摄像机创建场景中特定视点的图像的组件。 输出要么绘制到屏幕上,要么作为纹理捕获。 更多信息
参见 词汇表
的距离来更改曲面细分级别。 例如,您可以定义两个距离值

  • 曲面细分达到最大值时的距离(例如,10 米)。
  • 曲面细分级别逐渐减小时的距离(例如,20 米)。
    Shader "Tessellation Sample" {
        Properties {
            _Tess ("Tessellation", Range(1,32)) = 4
            _MainTex ("Base (RGB)", 2D) = "white" {}
            _DispTex ("Disp Texture", 2D) = "gray" {}
            _NormalMap ("Normalmap", 2D) = "bump" {}
            _Displacement ("Displacement", Range(0, 1.0)) = 0.3
            _Color ("Color", color) = (1,1,1,0)
            _SpecColor ("Spec color", color) = (0.5,0.5,0.5,0.5)
        }
        SubShader {
            Tags { "RenderType"="Opaque" }
            LOD 300
            
            CGPROGRAM
            #pragma surface surf BlinnPhong addshadow fullforwardshadows vertex:disp tessellate:tessDistance nolightmap
            #pragma target 4.6
            #include "Tessellation.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float4 tangent : TANGENT;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };

            float _Tess;

            float4 tessDistance (appdata v0, appdata v1, appdata v2) {
                float minDist = 10.0;
                float maxDist = 25.0;
                return UnityDistanceBasedTess(v0.vertex, v1.vertex, v2.vertex, minDist, maxDist, _Tess);
            }

            sampler2D _DispTex;
            float _Displacement;

            void disp (inout appdata v)
            {
                float d = tex2Dlod(_DispTex, float4(v.texcoord.xy,0,0)).r * _Displacement;
                v.vertex.xyz += v.normal * d;
            }

            struct Input {
                float2 uv_MainTex;
            };

            sampler2D _MainTex;
            sampler2D _NormalMap;
            fixed4 _Color;

            void surf (Input IN, inout SurfaceOutput o) {
                half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
                o.Albedo = c.rgb;
                o.Specular = 0.2;
                o.Gloss = 1.0;
                o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));
            }
            ENDCG
        }
        FallBack "Diffuse"
    }

在此,曲面细分函数将曲面细分前三个三角形角的顶点数据作为其三个参数。

Unity 需要此信息来计算曲面细分级别,这些级别取决于顶点位置。

该示例包含一个内置的辅助文件 Tessellation.cginc,并调用该文件中的 UnityDistanceBasedTess 函数来完成所有工作。该函数计算每个顶点到摄像机的距离并推导出最终的曲面细分因子。


基于边长的曲面细分

纯基于距离的曲面细分仅在三角形大小非常相似时才有效。在上图中,具有小三角形的 游戏对象Unity 场景中的基本对象,可以表示角色、道具、场景、摄像机、路径点等等。 游戏对象的函数由附加到它的组件定义。 更多信息
参见 词汇表
被过度细分,而具有大三角形的 GameObjects 未被足够细分。

改进此问题的一种方法是根据屏幕上三角形边长来计算曲面细分级别。 Unity 应将更大的曲面细分因子应用于更长的边。

    Shader "Tessellation Sample" {
        Properties {
            _EdgeLength ("Edge length", Range(2,50)) = 15
            _MainTex ("Base (RGB)", 2D) = "white" {}
            _DispTex ("Disp Texture", 2D) = "gray" {}
            _NormalMap ("Normalmap", 2D) = "bump" {}
            _Displacement ("Displacement", Range(0, 1.0)) = 0.3
            _Color ("Color", color) = (1,1,1,0)
            _SpecColor ("Spec color", color) = (0.5,0.5,0.5,0.5)
        }
        SubShader {
            Tags { "RenderType"="Opaque" }
            LOD 300
            
            CGPROGRAM
            #pragma surface surf BlinnPhong addshadow fullforwardshadows vertex:disp tessellate:tessEdge nolightmap
            #pragma target 4.6
            #include "Tessellation.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float4 tangent : TANGENT;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };

            float _EdgeLength;

            float4 tessEdge (appdata v0, appdata v1, appdata v2)
            {
                return UnityEdgeLengthBasedTess (v0.vertex, v1.vertex, v2.vertex, _EdgeLength);
            }

            sampler2D _DispTex;
            float _Displacement;

            void disp (inout appdata v)
            {
                float d = tex2Dlod(_DispTex, float4(v.texcoord.xy,0,0)).r * _Displacement;
                v.vertex.xyz += v.normal * d;
            }

            struct Input {
                float2 uv_MainTex;
            };

            sampler2D _MainTex;
            sampler2D _NormalMap;
            fixed4 _Color;

            void surf (Input IN, inout SurfaceOutput o) {
                half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
                o.Albedo = c.rgb;
                o.Specular = 0.2;
                o.Gloss = 1.0;
                o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));
            }
            ENDCG
        }
        FallBack "Diffuse"
    }

在此示例中,您调用 Tessellation.cginc 中的 UnityEdgeLengthBasedTess 函数来完成所有工作。

出于性能原因,请改为调用 UnityEdgeLengthBasedTessCull 函数,该函数执行贴片视锥体剔除。这使得着色器稍微更昂贵,但可以为位于摄像机视图之外的网格部分节省大量 GPU 工作量。


Phong 曲面细分

Phong 曲面细分 修改细分面的位置,以便生成的表面略微遵循网格法线。这是一种使低多边形网格变得更平滑的非常有效的方法。

Unity 的表面着色器可以使用 tessphong:VariableName 编译指令自动计算 Phong 曲面细分。以下是一个示例着色器

    Shader "Phong Tessellation" {
        Properties {
            _EdgeLength ("Edge length", Range(2,50)) = 5
            _Phong ("Phong Strengh", Range(0,1)) = 0.5
            _MainTex ("Base (RGB)", 2D) = "white" {}
            _Color ("Color", color) = (1,1,1,0)
        }
        SubShader {
            Tags { "RenderType"="Opaque" }
            LOD 300
            
            CGPROGRAM
            #pragma surface surf Lambert vertex:dispNone tessellate:tessEdge tessphong:_Phong nolightmap
            #include "Tessellation.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };

            void dispNone (inout appdata v) { }

            float _Phong;
            float _EdgeLength;

            float4 tessEdge (appdata v0, appdata v1, appdata v2)
            {
                return UnityEdgeLengthBasedTess (v0.vertex, v1.vertex, v2.vertex, _EdgeLength);
            }

            struct Input {
                float2 uv_MainTex;
            };

            fixed4 _Color;
            sampler2D _MainTex;

            void surf (Input IN, inout SurfaceOutput o) {
                half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
                o.Albedo = c.rgb;
                o.Alpha = c.a;
            }

            ENDCG
        }
        FallBack "Diffuse"
    }

以下是普通着色器(顶行)与使用 Phong 曲面细分的着色器(底行)之间的比较。可以看到,即使没有位移映射,表面也变得更圆润。


  • 2018-03-20 页面修正
  • 适用于 Metal 的曲面细分 在 2018.1 中添加。
内置渲染管线中的全局照明表面着色器示例
内置渲染管线的表面着色器语言参考