UE5.8 Mesh Terrain 与真 3D 地形:悬岩、隧道与浮岛
场景美术小林接到一个新关卡需求:一片破碎的高原,边缘要有向外延伸的悬岩,山体中间要开出一条可供载具穿行的隧道,远处天空还要漂浮几块长满苔藓的巨石。拿到文档后,他先按老办法拖了一个 Landscape Actor,刷了十分钟高度图,很快就卡住了。Landscape 做不出倒悬的岩壁,也做不出山体内部的空洞,更做不出凭空漂浮的岛屿。他把鼠标一扔,在 Slack 里问主程:这次是不是得换 Mesh 拼?
主程回了一句:试试 UE5.8 的 Mesh Terrain,它还在实验阶段,但正好能处理这种真三维地形。
1. 高度图的天花板
传统 Landscape 的本质是一张高度图。每个像素存储一个海拔值 ,渲染时把这张二维网格拉起成三维表面。这种表示方法在开阔起伏地形上非常高效:GPU 可以按规则网格批量绘制,LOD 切换简单,贴图混合也很成熟。
但它的几何表达能力被严格限制在一个函数 上。同一个水平坐标 只能对应一个高度 。这意味着:
- 无法表达垂直悬岩,因为高度函数在水平方向必须单值。
- 无法表达隧道或洞穴,因为山体内部的空间被高度图直接忽略。
- 无法表达浮岛,因为空中岛屿与下方地面共享同一套 坐标。
要用 Landscape 实现这些结构,通常只能拼 Static Mesh,或者在山体侧面挂贴花假装有深度。小林早就经历过这种痛苦:一个看起来还行的悬崖,走近一看是张薄片;一个山洞入口,进去两步就穿帮。Mesh Terrain 的出现,正是为了补上 Landscape 在真三维表达上的缺口。
下表把两种方案的核心差异列了出来。
| 特性 | Landscape | Mesh Terrain |
|---|---|---|
| 数据表示 | 高度图 | 任意三角形网格 |
| 真三维结构 | 不支持悬垂/镂空/浮岛 | 支持 |
| 编辑方式 | 笔刷高度、层刷 | 雕刻、拉伸、布尔、Modifier |
| 典型适用场景 | 开阔大地形 | 悬崖、洞穴、破碎地形、浮岛 |
| 渲染管线 | 专用地形渲染 | Nanite 虚拟几何 |
| 碰撞生成 | 自动 | 依赖 Mesh 碰撞设置 |
flowchart TD
subgraph ue ["Unreal Editor"]
landscape["Landscape Actor"]
tools["地形编辑工具"]
pcg["程序化生成"]
end
subgraph runtime ["运行时"]
nanite["Nanite 渲染"]
physx["物理碰撞"]
nav["导航网格"]
end
landscape --> nanite
mesht --> nanite
tools --> landscape
tools --> mesht
pcg --> mesht
nanite --> physx
nanite --> nav2. UE5.8 实验性 Mesh Terrain
UE5.8 把 Mesh Terrain 作为实验性功能放出,目标不是取代 Landscape,而是在 Landscape 无法胜任的地方提供另一种选择。它的核心思路是把地形当作普通 Static Mesh 来编辑,但保留地形工具的操作习惯。
在编辑器中,Mesh Terrain 以专用 Actor 的形式存在。创建后,你会得到一个可被雕刻、拉伸、切割的三维网格体。与 Landscape 不同,它不再受高度图约束,顶点可以朝任意方向移动,面片可以任意连接,因此能直接雕出悬岩、隧道、浮岛这类结构。
Mesh Terrain 依赖 Nanite 做渲染。Nanite 的虚拟几何体会把高模自动切分成簇,按屏幕空间误差动态选择 LOD。对地形来说,这意味着你可以在远景保持低面数,在近景保留岩石细节,而不需要手工制作多级 LOD。小林最喜欢的一点就是:他可以疯狂雕刻岩石裂缝,不用担心三角面爆炸导致性能崩盘。
不过实验性功能的代价是工具链还在补全。当前版本的雕刻笔刷手感与 Landscape 略有不同,部分贴花与植被系统需要额外适配,碰撞生成也比 Landscape 更依赖手动设置。团队决定先用它做关卡的视觉重点区域,背景大平原仍然交给 Landscape。
小林第一次打开 Mesh Terrain 面板时,发现选项比 Landscape 少很多。没有现成的 Grass Type 按钮,也不能直接刷植被。他先用传统方式在 Landscape 区域铺草,再在 Mesh Terrain 顶部手动放 Grass Actor。Sculpt 笔刷的撤销堆栈也偶尔会出现延迟,特别是在面数超过两百万之后。这些问题都在可接受范围内,但确实提醒团队:别把核心路径全部押在实验性功能上。
3. 悬岩、隧道、浮岛的实现
小林把关卡拆成三块来处理:高原边缘的悬岩、山体中部的隧道、远处的浮岛。每一块都用 Mesh Terrain 的不同能力解决。
3.1 悬岩
悬岩的关键是垂直甚至倒悬的几何面。在 Mesh Terrain 编辑模式下,小林选中高原边缘一圈面,沿着法线方向向外挤出,再向下拖出垂面。因为顶点不受高度图限制,岩壁可以任意陡峭,甚至形成倒钩。他把挤出厚度控制在 1.5 米左右,保证近景不会产生单面穿帮。
为了让岩壁有自然裂缝,他叠加了一个 Noise Modifier,频率设得较低、振幅较小,只在局部制造不规则凹凸。过大的噪声会让悬崖看起来像是融化的奶酪,太小又看不出细节。他反复在视口切换线框模式观察三角面分布,确认没有出现细长三角形导致的纹理拉伸。
3.2 隧道
隧道需要山体内部出现连续的空洞。小林先用 Box Brush 在山体上画出一个粗略通道,然后用 Mesh Terrain 的布尔减工具切出隧道轮廓。布尔运算后,他把切口边缘的三角面稍微倒角,避免玩家靠近时看到锋利的硬边。
隧道内壁单独给了另一套材质,比外侧山体更潮湿、更暗。因为 Mesh Terrain 的 UV 是自动展开的,小林并没有手工拆 UV,而是靠 Triplanar 映射加 World Aligned Texture 来处理接缝。隧道入口与外部山体之间保留了 0.3 米厚的过渡环,防止光照在洞口出现明显跳变。
3.3 浮岛
浮岛是完完全全脱离地面的体积。小林在场景远处新建了几个独立的 Mesh Terrain Actor,用球体或变形立方体作为起点,雕刻成被风化侵蚀的巨石。每个浮岛底部挂上垂藤和苔藓贴花,顶部撒上草丛和碎石。
浮岛的渲染依赖 Nanite,所以即使面数偏高,远景也不会拖垮帧率。小林给浮岛单独设置了光照层,让底部暗、顶部亮,配合 Lumen 全局光照,整体氛围比单纯贴图烘焙要自然得多。
flowchart TD
A["从关卡需求出发"] --> B{是否需要真三维结构?}
B -->|"是"| C["使用 Mesh Terrain"]
B -->|"否"| D["使用 Landscape"]
C --> E["挤出/雕刻悬岩"]
C --> F["布尔减隧道"]
C --> G["独立 Actor 制作浮岛"]
E --> H["叠加 Noise Modifier"]
F --> I["处理内壁材质与过渡环"]
G --> J["底部贴花 + 顶部植被"]
H --> K["导入场景并设置碰撞"]
I --> K
J --> K
D --> L["大面积背景地形"]
L --> K
K --> M["烘焙 NavMesh 与光照"]4. 非破坏性 Modifier
Modifier 是 Mesh Terrain workflow 里小林最看重的部分。它允许你对网格体应用一系列可调整、可重排、可开关的修改器,而不是一次性把形状刻死。整个 Modifier Stack 的顺序会直接影响最终形状,因此理解它们如何叠加非常重要。
下表列出了 UE5.8 当前支持的几种常见 Modifier。
| Modifier | 作用 | 典型参数 |
|---|---|---|
| Noise | 在顶点位置叠加随机噪声 | Frequency、Amplitude、Octaves |
| Smooth | 磨平尖锐棱角 | Iterations、Strength |
| Flatten | 把选定区域压平到参考高度 | Target Height、Falloff |
| Erode | 模拟风化和侵蚀 | Iterations、Hardness |
| Extrude | 沿法线方向挤出面片 | Distance、Taper |
小林的工作流程通常是这样:先用 Extrude 拉出大体结构,再用 Erode 制造风化感,接着用 Smooth 轻微柔化硬边,最后用 Noise 补细节。每个 Modifier 的参数都保留在面板上,客户说悬崖太陡,他就把 Extrude 的距离调小;策划说岩石太光滑,他就增加 Erode 重复次数。不需要重新雕一遍。
Modifier 的顺序之所以关键,是因为几何操作不满足交换律。先 Smooth 再 Noise 与先 Noise 再 Smooth,得到的结果完全不同。前者的细节会被保留,后者的细节会被磨掉。小林养成了一个习惯:给每个 Modifier 起明确的名字,比如 CliffErode_01、TunnelSmooth_02,方便后续追溯。
sequenceDiagram
participant Artist as "美术"
participant Base as "Base Mesh"
participant E1 as "Extrude Modifier"
participant E2 as "Erode Modifier"
participant S1 as "Smooth Modifier"
participant N1 as "Noise Modifier"
participant Final as "最终网格"
Artist->>Base: 创建初始地形块
Artist->>E1: 添加挤出,拉出悬崖主体
Base->>E1: 输入原始网格
E1->>E2: 输出加高后的网格
Artist->>E2: 添加侵蚀,制造风化
E2->>S1: 输出侵蚀后的网格
Artist->>S1: 添加平滑,柔化硬边
S1->>N1: 输出平滑后的网格
Artist->>N1: 添加噪声,补充细节
N1->>Final: 输出最终地形
Artist->>Final: 烘焙 Nanite / 设置碰撞5. 与 Landscape 的混合使用
Mesh Terrain 不是 Landscape 的替代品。小林的做法是:用 Landscape 铺出关卡的整体起伏,包括远处的山脉、脚下的平原、缓坡草地;在需要真三维结构的位置,把 Mesh Terrain 嵌入进去。
两种地形之间的接缝处理是重点。Landscape 的表面与 Mesh Terrain 的边缘必须平滑对接,否则玩家经过时会看到明显的缝隙或高度跳变。小林用了两种技巧:
一是在 Mesh Terrain 底部做一个向下延伸的裙边,插入 Landscape 内部。这样即使两者边缘有微小错位,也会被裙边遮挡。
二是在接缝处使用与 Landscape 相同的材质层。Mesh Terrain 的材质同样基于 Layer Blend,把草地、岩石、泥土三种层按权重混合。权重可以通过顶点色或贴图控制,小林通常会在接缝处手动刷过渡,让岩石层逐渐淡出,草地层逐渐增强。
graph LR
A["Landscape 基础地形"] --> B["Mesh Terrain 悬崖/隧道/浮岛"]
B --> C["统一材质层系统"]
C --> D["草地层"]
C --> E["岩石层"]
C --> F["泥土层"]
A --> G["远景 LOD 与贴图"]
B --> H["Nanite 动态 LOD"]
G --> I["最终渲染画面"]
H --> I6. 材质与贴花
Mesh Terrain 的材质处理比 Landscape 更自由,因为你可以直接给它分配任意 Material Instance,而不受 Landscape Layer 的限制。小林给悬崖做了一套基于 World Position 的 Triplanar 岩石材质,自动根据世界空间方向采样纹理,避免传统 UV 展开带来的拉伸。
材质的混合权重可以用顶点色控制。他在雕刻阶段就把 R 通道留给苔藓,G 通道留给潮湿痕迹,B 通道留给裸露岩石。材质里用这三个通道做 Layer Blend,得到自然的风化分布。
贴花在 Mesh Terrain 上同样有效。隧道内壁的潮湿水渍、浮岛底部的垂藤、悬崖顶部的积雪,全部用 Decal 实现。小林特别留意 Decal 的投影方向:垂直面用世界 Z 轴投影容易在侧面拉伸,改用 Mesh Decal 或贴花贴附到局部法线方向会更稳。
为了统一管理,小林把悬崖材质做成一个主 Material Instance,把岩石基础色、法线强度、湿润度都暴露成参数。这样关卡不同区域的悬崖可以共享同一套母材质,只通过 Instance 调整色调,既减少 Draw Call 也方便后期统一调色。他还在材质里加了一个基于世界高度的渐变,让高处偏灰白、低处偏黄褐,模拟长期风化的效果。
下面是一段简化的材质函数示例,展示如何在自定义节点里用 World Position 做 Triplanar 混合。它只有几行,实际项目里会封装成 Material Function。
// Custom HLSL node for triplanar rock blend
float3 Pos = WorldPosition * Tiling;
float3 Weights = abs(Normal);
Weights = saturate(Weights - 0.2);
Weights /= dot(Weights, float3(1,1,1));
float4 X = Tex_Sample(Pos.yz);
float4 Y = Tex_Sample(Pos.xz);
float4 Z = Tex_Sample(Pos.xy);
return X * Weights.x + Y * Weights.y + Z * Weights.z;代码里的 Weights 计算对应一个常见的归一化公式:
其中 是世界空间法线, 是一个小的偏移量,用来削弱轴向权重过小的采样贡献。把偏移量设为 时,侧面过渡会比较干净。
7. 碰撞与导航
Landscape 的碰撞是自动生成的,Mesh Terrain 则需要手动设置。小林在 Mesh Terrain Actor 的碰撞属性里选择 Use Complex Collision As Simple,这样 NavMesh 和物理查询都会使用实际网格面。这对隧道尤其重要:如果用最简包围盒,玩家走到洞口就会被空气墙挡住。
复杂碰撞的代价是运行时开销更高。小林只在玩家能到达的区域开启复杂碰撞,远景悬崖和浮岛则用简化的凸包碰撞体代理。他还会把碰撞网格单独导出一份低面数版本,通过 Mesh 的 Collision Mesh 插槽指定,避免 Nanite 渲染网格同时承担碰撞查询。
导航网格烘焙也比 Landscape 慢一些。因为 Mesh Terrain 的拓扑不规则,Recast 需要更多时间体素化。小林把地形分成了多个 NavMesh Bounds Volume,只覆盖可行走区域。浮岛之间的悬空路径他用手动放置的 NavLink Proxy 连接,让 AI 知道哪些位置可以跳跃或飞行。
在隧道里,小林还要处理一个问题:NavMesh 默认不会把低矮悬空面标记为可行走。他在隧道地面刷了一层 NavMesh Modifier Volume,强制把内部地面标记为 Walkable,同时把洞顶标记为 Not Walkable,防止 AI 试图跑到天花板上。
8. 性能与平台考虑
Mesh Terrain 的自由度是用性能换的。小林在开始做性能预算前,先算了算三角面。假设一块 512x512 的 Landscape 在最高 LOD 下有 个三角形。而同样面积的 Mesh Terrain,如果雕刻密集,面数可能轻易突破百万。好在 Nanite 会自动处理簇化与 LOD,实际屏幕上的三角面数可以用一个简化公式估算:
这里 是模型完整三角面数, 是物体在屏幕上的像素高度, 是观察距离, 是视野角。这个估算忽略了 Nanite 的簇合并与遮挡,但能大致说明:远处物体在屏幕上的贡献会快速下降。
平台方面,小林主要考虑两点:
- Nanite 支持:Mesh Terrain 的远景 LOD 依赖 Nanite,如果目标平台不支持 Nanite,比如部分移动设备或旧主机,就需要回退到传统 LOD 流程,美术制作成本会上升。
- 内存占用:Mesh Terrain 会保留 editable mesh 的源数据,内存占用比 Landscape 高。小林会定期把不再编辑的 Mesh Terrain 转换为普通 Static Mesh,删掉编辑历史,释放内存。
他还会用 Stat Nanite 和 RenderDoc 抓帧,确认 Draw Call 和三角形数量在预算内。一个经验是:把大型地形拆成多个 Mesh Terrain Actor,让 Nanite 的剔除粒度更细。单个巨型 Mesh Terrain 容易在视锥剔除上吃亏。
具体到小林的关卡,他把悬崖切成三块,隧道独立一块,浮岛各一块。烘焙后整关最高面数的 Mesh Terrain 控制在八百万三角以内,运行时平均屏幕三角数在四十万以下。这个数值在目标硬件上能稳定跑满六十帧。
graph TD
A["性能预算"] --> B["三角面统计"]
A --> C["Draw Call 分析"]
A --> D["内存占用"]
B --> E["依赖 Nanite LOD"]
C --> F["拆分多个 Actor"]
D --> G["烘焙为 Static Mesh"]
E --> H["目标平台是否支持 Nanite?"]
H -->|"支持"| I["直接发布"]
H -->|"不支持"| J["生成传统 LOD"]
F --> I
G --> I
J --> I9. 小林关卡的最终面貌
经过两周打磨,小林的关卡终于跑了起来。玩家从平原出发,沿着 Landscape 铺出的缓坡靠近高原。到了边缘,地面突然向外延伸成一片倒悬岩台,岩壁上的裂缝和苔藓用 Mesh Terrain 雕出。钻进山体,一条足够两辆越野车并行的隧道穿过岩层,内壁潮湿阴暗,出口处豁然开朗。抬头看,几块浮岛飘在天际,底部垂着藤蔓,顶部覆盖着稀疏草地。
整个关卡没有因为用了两种地形系统而显得割裂。Landscape 负责大尺度起伏,Mesh Terrain 负责戏剧性结构,两者通过统一材质层和接缝裙边衔接。Modifier Stack 让客户可以随时调整悬崖坡度,布尔减让隧道的尺寸修改变得简单,Nanite 则让浮岛在远景也不会变成纸片。
Mesh Terrain 仍然带着实验性标签,工具链还有不少粗糙边缘。但对于需要真三维地形的项目,它已经是一个值得认真评估的选项。小林在文档末尾加了一条备注:下个版本如果布尔运算能支持实时预览, workflow 还能再快一倍。
玩家在社区截图里留言,说这片浮岛让人想起某部经典动画。小林笑了笑,把截图保存下来放进项目复盘文档。对他来说,Mesh Terrain 最大的价值不是做出了多复杂的形状,而是让美术不再被高度图捆住手脚。