仄【QT】QWidget 概述与核心属性(API)B

Unity URP曲面细分技术详解

镁抑卮九三个子阶段:

Hull Shader:定义每条边和内部分割因子(Tessellation Factor)。?

接收原始控制点数据并定义细分因子(Tessellation Factor)

通过patch constant function计算每条边和内部的细分等级

支持分数细分模式(fractional_odd/fractional_even)实现平滑过渡

目标?:

定义原始控制点数据并计算细分因子(Tessellation Factor)

确定每个patch的边和内部细分等级

?输入输出?:

输入:原始控制点数据(位置、法线、UV等)

输出:

细分因子(SV_TessFactor标记边,SV_InsideTessFactor标记内部)

处理后的控制点数据(通过INTERNALTESSPOS语义标记)

?实现关键?:

hlsl

struct TessFactors {

float edge[3] : SV_TessFactor; // 三条边的细分因子

float inside : SV_InsideTessFactor; // 内部细分因子

};

?Tessellation Primitive Generator 固定功能?:硬件根据 Hull Shader 输出的因子实际执行细分操作。?

GPU固定功能阶段,根据Hull Shader输出的细分因子生成新顶点拓扑

目标?:

根据Hull Shader输出的细分因子生成新顶点拓扑

将原始patch细分为更密集的三角网格

?输入输出?:

输入:Hull Shader输出的细分因子和控制点

hlsl

// 来自Hull Shader的输出

struct TessControlPoint {

float4 positionOS : INTERNALTESSPOS;

float3 normalOS : NORMAL;

float2 uv : TEXCOORD0;

};

// 细分因子定义

struct TessFactors {

float edge[3] : SV_TessFactor; // 每条边的细分等级

float inside : SV_InsideTessFactor; // 内部细分等级

};

输出:细分后的顶点UV坐标和拓扑关系

生成的重心坐标数据:

hlsl

float3 baryCoords : SV_DomainLocation; // 新顶点的重心坐标

输出拓扑类型由Hull Shader的[outputtopology]属性定义(如triangle_cw)

?特性?:

GPU自动执行,无需开发者编码

支持分数细分模式实现平滑过渡

实现原理

?细分算法?

对原始三角面片进行递归细分,采用Delaunay三角剖分原则

GPU使用的Delaunay三角剖分原则是一种优化网格拓扑的数学方法

?Delaunay三角剖分原则?

?空圆特性?:任意三角形的外接圆内不包含其他顶点

?最大化最小角?:避免出现尖锐三角形,提高网格质量

?局部优化?:通过边翻转(edge flip)逐步优化三角网格

?URP中的实现特点?:

在Tessellation Primitive Generator阶段自动应用

对细分后的新顶点进行拓扑优化

保持与原网格的平滑过渡

?具体示例?:

原始三角面片(控制点A/B/C)经过细分后:

计算细分因子为3时:

原始三角形: A

/ \

C---B

细分后拓扑:

A

/|\

/ | \

C--D--B

\ | /

\|/

E

其中D/E是新生成的顶点,所有三角形都满足:

∠ADB + ∠AEB ≈ 180°

外接圆不包含其他顶点

?URP中的实际应用?:

当使用[partitioning("fractional_odd")]时:

会在过渡区域自动应用Delaunay优化

确保不同细分等级间的平滑连接

位移贴图处理时:

新顶点根据Delaunay规则分布

位移后的法线计算更准确

这种剖分方式使得:

细分后的网格更适应曲面变形

避免渲染时的褶皱现象

提升位移贴图的视觉效果

根据分数细分模式(fractional_odd/even)处理过渡区域

?坐标转换 $Pnew=P0?u+P1?v+P2?w$

将参数空间坐标(u,v,w)转换为新的顶点位置

性能优化?

采用并行计算处理多个patch

自动剔除屏幕空间不可见的细分结果

?Domain Shader?:

将细分后位于重心坐标系(重心坐标内容后续单开一篇讲解)中的新顶点位置转换到目标空间(如世界空间或裁剪空间)。?

将细分后的UV坐标映射到3D空间

执行顶点位移等后期处理

目标?:

将细分后的UV坐标映射到3D空间

执行顶点位移等后期处理

?输入输出?:

输入:细分后的UV坐标和原始控制点数据

输出:最终顶点位置(SV_POSITION)和其他顶点属性

?实现示例?:

hlsl

[domain("tri")] // 声明处理三角形patch

Varyings domain(TessFactors factors, OutputPatch patch, float3 baryCoords : SV_DomainLocation) {

Varyings OUT;

// 插值计算新顶点属性

OUT.positionWS = TransformObjectToWorld(patch[0].positionOS * baryCoords.x + ...);

return OUT;

}

?动态控制技巧?:

通过摄像机距离调整细分因子:

hlsl

float CalcTessFactor(float3 worldPos) {

return lerp(_MaxTess, _MinTess, saturate(distance(_WorldSpaceCameraPos, worldPos) / _TessRange));

}

结合高度图实现位移效果

?配置:

需显式启用(HLSL 中声明 hull 和 domain 函数)。

必须声明#pragma target 4.6以启用DX11/OpenGL Core特性3

需手动实现Hull/Domain Shader,URP不提供表面着色器的简化写法

建议结合摄像机距离动态控制细分因子以优化性能

URP中的具体实现步骤:

声明编译目标为4.6以上:

hlsl

#pragma target 4.6

定义三个关键程序:

hlsl

#pragma vertex BeforeTessVert

#pragma hull HullProgram

#pragma domain DomainProgram

控制点数据结构需包含顶点位置、法线等基础属性

?动态细分控制样例:

通过距离或屏幕空间尺寸自动调整细分因子:

hlsl

// 根据摄像机距离动态计算细分因子

float CalcTessFactor(float3 worldPos) {

float dist = distance(_WorldSpaceCameraPos, worldPos);

return lerp(_MaxTess, _MinTess, saturate(dist / _TessRange));

}

常见应用场景:

地形动态LOD:根据视角距离细分地面网格3

曲面平滑:将低模转换为高模曲面2

动态位移:结合高度图实现实时凹凸效果6

?注意事项:

仅支持DX11/OpenGL Core等现代图形API6

细分过度会导致性能下降,需合理设置上限5

URP中需手动实现Hull/Domain Shader,不同于内置管线的表面着色器简化写法

Unity URP中完整的曲面细分着色器示例,包含顶点位移效果和动态细分控制

示例实现功能:

动态细分控制:根据摄像机距离自动调整细分因子

顶点位移效果:通过高度图(_DispTex)驱动顶点偏移

分数细分模式:使用fractional_odd实现平滑过渡

完整渲染管线:包含顶点/细分/片元全阶段处理

URP兼容性:使用URP的ShaderLibrary核心函数

使用说明:

创建材质球并应用此着色器

为_DispTex指定高度图纹理

调整_TessFactor控制细分密度

通过_Displacement参数控制位移强度

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 patch, uint id : SV_OutputControlPointID)

{

return patch[id];

}

TessFactors patchConstantFunc(InputPatch 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 patch, float3 baryCoords : SV_DomainLocation)

{

DomainOutput o;

// 插值计算基础属性

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;

// 转换到裁剪空间

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

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值