【渲染流水線】[幾何階段]-[曲面細(xì)分]以UnityURP為例
- 細(xì)分著色器分為:曲面細(xì)分著色器(Unity在指定平臺硬件支持)、細(xì)分計算著色器。使用片面來描述一個物體形狀,并增加頂點和片面數(shù)量,使模型外觀開起來更平滑。
- ?作用?:動態(tài)細(xì)分三角面片,提升模型細(xì)節(jié)(如草地、地形渲染),優(yōu)化大場景性能 ?。
- 這是一個?可選?階段。它在頂點著色器之后運行,負(fù)責(zé)根據(jù)設(shè)定的細(xì)分因子 (Tessellation Factor) 將輸入的圖元(通常是三角形面片)動態(tài)細(xì)分為更小的三角面片,生成更多頂點。?
【從UnityURP開始探索游戲渲染】專欄-直達(dá)
三個子階段:
Hull Shader:定義每條邊和內(nèi)部分割因子(Tessellation Factor)。?
- 接收原始控制點數(shù)據(jù)并定義細(xì)分因子(Tessellation Factor)
- 通過
patch constant function計算每條邊和內(nèi)部的細(xì)分等級 - 支持分?jǐn)?shù)細(xì)分模式(fractional_odd/fractional_even)實現(xiàn)平滑過渡
目標(biāo)?:
- 定義原始控制點數(shù)據(jù)并計算細(xì)分因子(Tessellation Factor)
- 確定每個patch的邊和內(nèi)部細(xì)分等級
?輸入輸出?:
- 輸入:原始控制點數(shù)據(jù)(位置、法線、UV等)
- 輸出:
- 細(xì)分因子(
SV_TessFactor標(biāo)記邊,SV_InsideTessFactor標(biāo)記內(nèi)部) - 處理后的控制點數(shù)據(jù)(通過
INTERNALTESSPOS語義標(biāo)記)
- 細(xì)分因子(
?實現(xiàn)關(guān)鍵?:
hlsl
struct TessFactors {
float edge[3] : SV_TessFactor; // 三條邊的細(xì)分因子
float inside : SV_InsideTessFactor; // 內(nèi)部細(xì)分因子
};
?Tessellation Primitive Generator 固定功能?:硬件根據(jù) Hull Shader 輸出的因子實際執(zhí)行細(xì)分操作。?
- GPU固定功能階段,根據(jù)Hull Shader輸出的細(xì)分因子生成新頂點拓?fù)?/li>
目標(biāo)?:
- 根據(jù)Hull Shader輸出的細(xì)分因子生成新頂點拓?fù)?/li>
- 將原始patch細(xì)分為更密集的三角網(wǎng)格
?輸入輸出?:
-
輸入:Hull Shader輸出的細(xì)分因子和控制點
hlsl // 來自Hull Shader的輸出 struct TessControlPoint { float4 positionOS : INTERNALTESSPOS; float3 normalOS : NORMAL; float2 uv : TEXCOORD0; }; // 細(xì)分因子定義 struct TessFactors { float edge[3] : SV_TessFactor; // 每條邊的細(xì)分等級 float inside : SV_InsideTessFactor; // 內(nèi)部細(xì)分等級 }; -
輸出:細(xì)分后的頂點UV坐標(biāo)和拓?fù)潢P(guān)系
-
生成的重心坐標(biāo)數(shù)據(jù):
hlsl float3 baryCoords : SV_DomainLocation; // 新頂點的重心坐標(biāo) -
輸出拓?fù)漕愋陀蒆ull Shader的
[outputtopology]屬性定義(如triangle_cw)
-
?特性?:
- GPU自動執(zhí)行,無需開發(fā)者編碼
- 支持分?jǐn)?shù)細(xì)分模式實現(xiàn)平滑過渡
實現(xiàn)原理
?細(xì)分算法?
-
對原始三角面片進(jìn)行遞歸細(xì)分,采用Delaunay三角剖分原則
GPU使用的Delaunay三角剖分原則是一種優(yōu)化網(wǎng)格拓?fù)涞臄?shù)學(xué)方法
-
?Delaunay三角剖分原則?
- ?空圓特性?:任意三角形的外接圓內(nèi)不包含其他頂點
- ?最大化最小角?:避免出現(xiàn)尖銳三角形,提高網(wǎng)格質(zhì)量
- ?局部優(yōu)化?:通過邊翻轉(zhuǎn)(edge flip)逐步優(yōu)化三角網(wǎng)格
-
?URP中的實現(xiàn)特點?:
- 在Tessellation Primitive Generator階段自動應(yīng)用
- 對細(xì)分后的新頂點進(jìn)行拓?fù)鋬?yōu)化
- 保持與原網(wǎng)格的平滑過渡
-
?具體示例?:
原始三角面片(控制點A/B/C)經(jīng)過細(xì)分后:
- 計算細(xì)分因子為3時:
原始三角形: A / \ C---B 細(xì)分后拓?fù)洌? A /|\ / | \ C--D--B \ | / \|/ E- 其中D/E是新生成的頂點,所有三角形都滿足:
- ∠ADB + ∠AEB ≈ 180°
- 外接圓不包含其他頂點
-
?URP中的實際應(yīng)用?:
- 當(dāng)使用
[partitioning("fractional_odd")]時:- 會在過渡區(qū)域自動應(yīng)用Delaunay優(yōu)化
- 確保不同細(xì)分等級間的平滑連接
- 位移貼圖處理時:
- 新頂點根據(jù)Delaunay規(guī)則分布
- 位移后的法線計算更準(zhǔn)確
- 當(dāng)使用
-
這種剖分方式使得:
- 細(xì)分后的網(wǎng)格更適應(yīng)曲面變形
- 避免渲染時的褶皺現(xiàn)象
- 提升位移貼圖的視覺效果
-
-
根據(jù)分?jǐn)?shù)細(xì)分模式(fractional_odd/even)處理過渡區(qū)域
?坐標(biāo)轉(zhuǎn)換 $Pnew=P0?u+P1?v+P2?w$
- 將參數(shù)空間坐標(biāo)(u,v,w)轉(zhuǎn)換為新的頂點位置
性能優(yōu)化?
- 采用并行計算處理多個patch
- 自動剔除屏幕空間不可見的細(xì)分結(jié)果
?Domain Shader?:
將細(xì)分后位于重心坐標(biāo)系(重心坐標(biāo)內(nèi)容后續(xù)單開一篇講解)中的新頂點位置轉(zhuǎn)換到目標(biāo)空間(如世界空間或裁剪空間)。?
- 將細(xì)分后的UV坐標(biāo)映射到3D空間
- 執(zhí)行頂點位移等后期處理
目標(biāo)?:
- 將細(xì)分后的UV坐標(biāo)映射到3D空間
- 執(zhí)行頂點位移等后期處理
?輸入輸出?:
- 輸入:細(xì)分后的UV坐標(biāo)和原始控制點數(shù)據(jù)
- 輸出:最終頂點位置(
SV_POSITION)和其他頂點屬性
?實現(xiàn)示例?:
hlsl
[domain("tri")] // 聲明處理三角形patch
Varyings domain(TessFactors factors, OutputPatch<DomainAttributes, 3> patch, float3 baryCoords : SV_DomainLocation) {
Varyings OUT;
// 插值計算新頂點屬性
OUT.positionWS = TransformObjectToWorld(patch[0].positionOS * baryCoords.x + ...);
return OUT;
}
?動態(tài)控制技巧?:
-
通過攝像機(jī)距離調(diào)整細(xì)分因子:
hlsl float CalcTessFactor(float3 worldPos) { return lerp(_MaxTess, _MinTess, saturate(distance(_WorldSpaceCameraPos, worldPos) / _TessRange)); } -
結(jié)合高度圖實現(xiàn)位移效果
?配置:
- 需顯式啟用(HLSL 中聲明
hull和domain函數(shù))。 - 必須聲明
#pragma target 4.6以啟用DX11/OpenGL Core特性3 - 需手動實現(xiàn)Hull/Domain Shader,URP不提供表面著色器的簡化寫法
- 建議結(jié)合攝像機(jī)距離動態(tài)控制細(xì)分因子以優(yōu)化性能
URP中的具體實現(xiàn)步驟:
聲明編譯目標(biāo)為4.6以上:
hlsl
#pragma target 4.6
定義三個關(guān)鍵程序:
hlsl
#pragma vertex BeforeTessVert
#pragma hull HullProgram
#pragma domain DomainProgram
控制點數(shù)據(jù)結(jié)構(gòu)需包含頂點位置、法線等基礎(chǔ)屬性
?動態(tài)細(xì)分控制樣例:
- 通過距離或屏幕空間尺寸自動調(diào)整細(xì)分因子:
hlsl
// 根據(jù)攝像機(jī)距離動態(tài)計算細(xì)分因子
float CalcTessFactor(float3 worldPos) {
float dist = distance(_WorldSpaceCameraPos, worldPos);
return lerp(_MaxTess, _MinTess, saturate(dist / _TessRange));
}
常見應(yīng)用場景:
- 地形動態(tài)LOD:根據(jù)視角距離細(xì)分地面網(wǎng)格3
- 曲面平滑:將低模轉(zhuǎn)換為高模曲面2
- 動態(tài)位移:結(jié)合高度圖實現(xiàn)實時凹凸效果6
?注意事項:
- 僅支持DX11/OpenGL Core等現(xiàn)代圖形API6
- 細(xì)分過度會導(dǎo)致性能下降,需合理設(shè)置上限5
- URP中需手動實現(xiàn)Hull/Domain Shader,不同于內(nèi)置管線的表面著色器簡化寫法
Unity URP中完整的曲面細(xì)分著色器示例,包含頂點位移效果和動態(tài)細(xì)分控制
示例實現(xiàn)功能:
- 動態(tài)細(xì)分控制:根據(jù)攝像機(jī)距離自動調(diào)整細(xì)分因子
- 頂點位移效果:通過高度圖(_DispTex)驅(qū)動頂點偏移
- 分?jǐn)?shù)細(xì)分模式:使用fractional_odd實現(xiàn)平滑過渡
- 完整渲染管線:包含頂點/細(xì)分/片元全階段處理
- URP兼容性:使用URP的ShaderLibrary核心函數(shù)
使用說明:
- 創(chuàng)建材質(zhì)球并應(yīng)用此著色器
- 為_DispTex指定高度圖紋理
- 調(diào)整_TessFactor控制細(xì)分密度
- 通過_Displacement參數(shù)控制位移強(qiáng)度
TessellationExample.shader
// HLSL
Shader "Custom/TessellationExample"
{
Properties
{
_MainTex ("Base Texture", 2D) = "white" {}
_TessFactor ("Tessellation Factor", Range(1, 64)) = 4
_Displacement ("Displacement", Range(0, 1.0)) = 0.3
_DispTex ("Displacement Texture", 2D) = "gray" {}
}
SubShader
{
Tags { "RenderPipeline"="UniversalPipeline" }
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float2 uv : TEXCOORD0;
};
struct TessControlPoint
{
float4 positionOS : INTERNALTESSPOS;
float3 normalOS : NORMAL;
float2 uv : TEXCOORD0;
};
struct TessFactors
{
float edge[3] : SV_TessFactor;
float inside : SV_InsideTessFactor;
};
struct DomainOutput
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
float3 normalWS : NORMAL;
};
ENDHLSL
Pass
{
Name "ForwardLit"
Tags { "LightMode"="UniversalForward" }
HLSLPROGRAM
#pragma vertex vert
#pragma hull hull
#pragma domain domain
#pragma fragment frag
#pragma target 4.6
sampler2D _MainTex;
sampler2D _DispTex;
float _TessFactor;
float _Displacement;
TessControlPoint vert(Attributes v)
{
TessControlPoint o;
o.positionOS = v.positionOS;
o.normalOS = v.normalOS;
o.uv = v.uv;
return o;
}
[domain("tri")]
[partitioning("fractional_odd")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(3)]
[patchconstantfunc("patchConstantFunc")]
TessControlPoint hull(InputPatch<TessControlPoint, 3> patch, uint id : SV_OutputControlPointID)
{
return patch[id];
}
TessFactors patchConstantFunc(InputPatch<TessControlPoint, 3> patch)
{
TessFactors f;
float avgTess = _TessFactor * (1 - saturate(length(_WorldSpaceCameraPos - TransformObjectToWorld(patch[0].positionOS.xyz)) / 20));
f.edge[0] = f.edge[1] = f.edge[2] = avgTess;
f.inside = avgTess;
return f;
}
[domain("tri")]
DomainOutput domain(TessFactors factors, OutputPatch<TessControlPoint, 3> patch, float3 baryCoords : SV_DomainLocation)
{
DomainOutput o;
// 插值計算基礎(chǔ)屬性
float3 positionOS = patch[0].positionOS.xyz * baryCoords.x +
patch[1].positionOS.xyz * baryCoords.y +
patch[2].positionOS.xyz * baryCoords.z;
float2 uv = patch[0].uv * baryCoords.x +
patch[1].uv * baryCoords.y +
patch[2].uv * baryCoords.z;
// 從高度圖獲取位移值
float disp = tex2Dlod(_DispTex, float4(uv, 0, 0)).r * _Displacement;
positionOS += normalize(patch[0].normalOS) * disp;
// 轉(zhuǎn)換到裁剪空間
o.positionCS = TransformObjectToHClip(positionOS);
o.uv = uv;
o.normalWS = TransformObjectToWorldNormal(patch[0].normalOS);
return o;
}
half4 frag(DomainOutput i) : SV_Target
{
half4 col = tex2D(_MainTex, i.uv);
return col;
}
ENDHLSL
}
}
}
【從UnityURP開始探索游戲渲染】專欄-直達(dá)
(歡迎點贊留言探討,更多人加入進(jìn)來能更加完善這個探索的過程,??)

本文介紹了細(xì)分著色器的核心原理與實現(xiàn)方法。細(xì)分著色器分為曲面細(xì)分著色器和細(xì)分計算著色器,主要用于動態(tài)增加模型細(xì)節(jié),提升草地、地形等場景的渲染效果。文章詳細(xì)解析了細(xì)分過程的三個階段:Hull Shader定義細(xì)分因子,Tessellation Primitive Generator執(zhí)行硬件級細(xì)分操作,Domain Shader完成頂點空間轉(zhuǎn)換。重點闡述了Delaunay三角剖分原則在URP中的實現(xiàn),包括空圓特性和局部優(yōu)化方法。最后提供了一個完整的URP細(xì)分著色器示例,展示了動態(tài)細(xì)分控制、頂點位移等關(guān)鍵技術(shù),并
浙公網(wǎng)安備 33010602011771號