Version: 6000.3
语言: 中文
内置渲染管线中的全局光照表面着色器示例
内置渲染管线的表面着色器语言参考

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

在内置渲染管线(Render Pipeline) 获取场景内容并将其显示在屏幕上的一系列作。Unity 允许您从预构建的渲染管道中进行选择,或编写自己的渲染管道。更多信息
请参阅术语表
,表面着色器:为内置渲染管线编写着色器的简化方法。更多信息
请参阅术语表
对 DirectX 11 / OpenGL Core GPU Tessellation 有一些支持。

  • 细分由tessellate:FunctionName修饰语。该函数计算三角形边缘和内部细分因子。
  • 使用细分时,“顶点修饰符”(vertex:FunctionName) 在细分调用,用于域中生成的每个顶点着色器在 GPU 上运行的程序。更多信息
    请参阅术语表
    .在这里,您通常会使用置换映射。
  • 表面着色器可以选择计算 phong 细分以平滑模型表面,即使没有任何位移映射。

细分和几何着色器

表面着色器支持简单的曲面细分和置换。编写自定义着色器程序时,可以使用全套 DX11 着色器模型 5.0 功能,包括几何体、外壳和域着色器。

曲面细分支持的当前限制:

  • 只有三角形域 - 没有四边形类似于平面但其边只有一个单位长的原始对象,它只使用 4 个顶点,并且表面定向在局部坐标空间的 XY 平面上。更多信息
    请参阅术语表
    ,无等值线细分。
  • 使用细分时,着色器会自动编译到着色器模型 4.6 目标中,这会阻止支持在较旧的图形目标上运行。

渲染管线兼容性

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

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


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

固定曲面细分量

如果模型的面在屏幕上的大小大致相同,请向网格Unity 的主要图形原语。网格体构成了 3D 世界的很大一部分。Unity 支持三角或四边形多边形网格。Nurbs、Nurms、Subdiv 曲面必须转换为多边形。更多信息
请参阅术语表
(整个网格体上的相同细分级别)。

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

    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 场景中的基本对象,可以表示角色、道具、风景、相机、航路点等。游戏对象的功能由附加到它的组件定义。更多信息
请参阅术语表
具有小三角形的曲面细分过多,而具有大三角形的游戏对象则曲面细分不够。

改进这一点的一种方法是根据屏幕上的三角形边缘长度计算曲面细分级别。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"
    }

在此示例中,调用UnityEdgeLengthBasedTess函数来完成所有工作。

细分系数取决于屏幕上的三角形边缘长度。Unity 将较大的曲面细分因子应用于较长的边缘。
细分系数取决于屏幕上的三角形边缘长度。Unity 将较大的曲面细分因子应用于较长的边缘。

出于性能原因,请改为调用 UnityEdgeLengthBasedTessCull 函数,该函数执行补丁视锥体剔除。这使得着色器的成本更高一些,但为摄像机视图之外的网格体部分节省了大量GPU工作。


Phong 镶嵌

Phong 细分修改细分面的位置,使生成的表面稍微跟随网格法线。这是使低多边形网格变得更加平滑的一种非常有效的方法。

Unity 的表面着色器可以使用tessphong:VariableName编译指令。下面是一个示例着色器:

    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 细分(下行)的着色器之间的比较。请注意,即使没有任何置换贴图,曲面也会变得更加圆润。

顶部图像行:没有细分的着色器。底部图像行:使用 Phong 细分的着色器。
顶部图像行:没有细分的着色器。底部图像行:使用 Phong 细分的着色器。
内置渲染管线中的全局光照表面着色器示例
内置渲染管线的表面着色器语言参考