Shader编写指南(五十八): 表面着色器(Surface Shader)DX11/OpenGL 核心曲面细分示例

在内置渲染管线中,表面着色器支持基于 DirectX 11 和 OpenGL Core 的 GPU 曲面细分(Tessellation),通过#pragma tessellate指令定义细分因子计算函数。以下是不同场景下的实现示例及技术细节:

一、曲面细分基础架构

核心指令

hlsl

#pragma surface surf <光照模型> tessellate:<细分函数> [vertex:<顶点修改函数>]  
#pragma target 4.6 // 要求Shader Model 4.6及以上  

  • 细分函数:计算三角形边缘(Edge)和内部(Inside)的细分因子。
  • 顶点修改函数:在细分后对生成的顶点进行位移(如置换贴图)。
细分因子结构

细分函数需返回float4类型,分别对应:

  • x:Edge0(顶点 0 - 顶点 1 的边缘细分因子)
  • y:Edge1(顶点 1 - 顶点 2 的边缘细分因子)
  • z:Edge2(顶点 2 - 顶点 0 的边缘细分因子)
  • w:Inside(三角形内部细分因子)

二、无细分的顶点位移(基础示例)

效果:通过置换贴图沿法线位移顶点,未启用曲面细分。

hlsl

Shader "Tessellation/DisplacementOnly" {  
    Properties {  
        _DispTex ("置换贴图", 2D) = "gray" {}  
        _Displacement ("置换强度", Range(0, 1)) = 0.3  
    }  
    SubShader {  
        Tags { "RenderType"="Opaque" }  
        CGPROGRAM  
        #pragma surface surf BlinnPhong vertex:disp nolightmap  
        #pragma target 4.6  

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

        sampler2D _DispTex;  
        float _Displacement;  

        // 顶点位移函数(无细分)  
        void disp (inout appdata v) {  
            float d = tex2Dlod(_DispTex, float4(v.uv, 0, 0)).r * _Displacement;  
            v.vertex.xyz += v.normal * d; // 沿法线位移  
        }  

        struct Input {  
            float2 uv_MainTex;  
        };  

        void surf (Input IN, inout SurfaceOutput o) {  
            o.Albedo = 1; // 纯色显示位移效果  
        }  
        ENDCG  
    }  
    Fallback "Diffuse"  
}  

要点

  • 通过vertex:disp关联顶点位移函数,直接修改原始顶点位置。
  • 未启用细分时,模型三角面数不变,仅顶点位置改变。

三、固定细分级别(全局统一细分)

效果:对整个模型应用固定细分级别,适用于屏幕空间大小一致的模型。

hlsl

Shader "Tessellation/FixedTessellation" {  
    Properties {  
        _TessLevel ("细分级别", Range(1, 32)) = 4  
        _DispTex ("置换贴图", 2D) = "gray" {}  
    }  
    SubShader {  
        Tags { "RenderType"="Opaque" }  
        CGPROGRAM  
        #pragma surface surf BlinnPhong tessellate:tessFixed vertex:disp nolightmap  
        #pragma target 4.6  

        float _TessLevel;  

        // 固定细分因子函数  
        float4 tessFixed () {  
            return float4(_TessLevel, _TessLevel, _TessLevel, _TessLevel); // 四边相同细分  
        }  

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

        struct Input {  
            float2 uv_MainTex;  
        };  

        void surf (Input IN, inout SurfaceOutput o) {  
            o.Albedo = 1;  
        }  
        ENDCG  
    }  
    Fallback "Diffuse"  
}  

要点

  • tessFixed()返回固定值,控制所有三角形的细分级别。
  • 细分后顶点数 = 原始顶点数 × 细分级别 ²,可能导致性能问题。

四、基于距离的细分(近距高细分,远距低细分)

效果:根据模型到相机的距离动态调整细分级别,优化性能。

hlsl

Shader "Tessellation/DistanceBasedTess" {  
    Properties {  
        _MaxTess ("最大细分", Range(1, 32)) = 16  
        _MinTess ("最小细分", Range(1, 16)) = 4  
        _TransitionDist ("过渡距离", Range(10, 50)) = 20  
    }  
    SubShader {  
        Tags { "RenderType"="Opaque" }  
        CGPROGRAM  
        #pragma surface surf BlinnPhong tessellate:tessDistance vertex:disp nolightmap  
        #pragma target 4.6  
        #include "Tessellation.cginc" // 引入Unity内置细分工具  

        float _MaxTess, _MinTess, _TransitionDist;  

        // 基于距离的细分因子函数  
        float4 tessDistance (appdata v0, appdata v1, appdata v2) {  
            // 使用Unity内置函数计算距离相关的细分因子  
            return UnityDistanceBasedTess(  
                v0.vertex, v1.vertex, v2.vertex,  
                _TransitionDist, // 开始降细分的距离  
                _TransitionDist * 2, // 最低细分的距离  
                _MaxTess, _MinTess  
            );  
        }  

        void disp (inout appdata v) { /* 位移逻辑同上 */ }  
        void surf (Input IN, inout SurfaceOutput o) { /* 着色逻辑同上 */ }  
        ENDCG  
    }  
    Fallback "Diffuse"  
}  

要点

  • UnityDistanceBasedTess根据顶点到相机的距离自动计算细分因子。
  • 近距离使用高细分(如_MaxTess=16),远距离使用低细分(如_MinTess=4)。

五、基于边缘长度的细分(屏幕空间自适应细分)

效果:根据三角形在屏幕上的边缘长度动态调整细分,避免小三角形过度细分。

hlsl

Shader "Tessellation/EdgeLengthTess" {  
    Properties {  
        _EdgeLength ("边缘长度阈值", Range(2, 50)) = 15 // 像素单位  
    }  
    SubShader {  
        Tags { "RenderType"="Opaque" }  
        CGPROGRAM  
        #pragma surface surf BlinnPhong tessellate:tessEdge vertex:disp nolightmap  
        #pragma target 4.6  
        #include "Tessellation.cginc"  

        float _EdgeLength;  

        // 基于边缘长度的细分因子函数  
        float4 tessEdge (appdata v0, appdata v1, appdata v2) {  
            return UnityEdgeLengthBasedTess(  
                v0.vertex, v1.vertex, v2.vertex,  
                _EdgeLength // 屏幕空间边缘长度阈值(像素)  
            );  
        }  

        void disp (inout appdata v) { /* 位移逻辑同上 */ }  
        void surf (Input IN, inout SurfaceOutput o) { /* 着色逻辑同上 */ }  
        ENDCG  
    }  
    Fallback "Diffuse"  
}  

要点

  • UnityEdgeLengthBasedTess计算三角形边缘在屏幕上的像素长度,长边缘使用高细分。
  • 推荐搭配UnityEdgeLengthBasedTessCull函数,剔除相机视野外的细分计算,提升性能。

六、Phong 曲面细分(无置换的平滑效果)

效果:通过调整细分后顶点的位置,模拟法线平滑效果,无需置换贴图。

hlsl

Shader "Tessellation/PhongTessellation" {  
    Properties {  
        _TessLevel ("细分级别", Range(1, 32)) = 8  
        _PhongStrength ("平滑强度", Range(0, 1)) = 0.5  
    }  
    SubShader {  
        Tags { "RenderType"="Opaque" }  
        CGPROGRAM  
        #pragma surface surf Lambert tessellate:tessFixed tessphong:_PhongStrength  
        #pragma target 4.6  

        float _TessLevel;  

        float4 tessFixed () {  
            return float4(_TessLevel, _TessLevel, _TessLevel, _TessLevel);  
        }  

        struct Input {  
            float2 uv_MainTex;  
        };  

        void surf (Input IN, inout SurfaceOutput o) {  
            o.Albedo = 1; // 纯色显示平滑效果  
        }  
        ENDCG  
    }  
    Fallback "Diffuse"  
}  

要点

  • tessphong:_PhongStrength指令启用 Phong 细分,通过_PhongStrength控制平滑程度。
  • 无需顶点位移,仅通过细分后的顶点位置调整实现低多边形模型平滑化。

七、性能优化与限制

  1. 硬件支持

    • 仅支持 DirectX 11 及以上、OpenGL Core 4.0 及以上显卡。
    • 使用#pragma target 4.6强制编译为高版本 Shader Model,可能不兼容旧设备。
  2. 细分因子范围

    • 建议细分级别不超过 32,过高值会导致 GPU 负载激增。
  3. 混合使用细分与位移

    • 细分增加顶点数,置换贴图增强细节,两者结合可实现高保真曲面(如地形、角色衣物)。
  4. 减少冗余计算

    • 对不可见的三角形,使用UnityEdgeLengthBasedTessCull剔除细分计算。

总结:如何选择细分策略

场景推荐细分方法关键参数
全局平滑化固定细分 + Phong 细分_TessLevel_PhongStrength
远距离优化基于距离的细分_MaxTess_TransitionDist
屏幕空间自适应细分基于边缘长度的细分_EdgeLength
高细节曲面(如岩石)细分 + 置换贴图tessellatevertex:disp

通过合理组合细分策略与顶点位移,可在性能与画质间取得平衡。注意在移动设备上谨慎使用曲面细分,优先考虑基于屏幕空间的自适应算法以减少过度细分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小李也疯狂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值