How to Develop a Game
Terrain
PublishDate: 2025-06-01 | CreateDate: 2025-06-01 | LastModify: 2025-06-01 | Creator:ljf12825

在Unity中,Terrain是一个专门用于制作大规模、自然风格场景的强大工具

什么是Terrain

Terrain是Unity提供的一个内置组件,用于在场景中创建可编辑的地形
它由多个部分组成:

Terrain的核心结构

模块功能
高度图(Heightmap)决定地形的高低起伏
绘制材质(Layers)地面纹理(如草地、岩石、雪)混合涂刷
细节对象(Details)草、石头、花等低多边形细节(大量渲染优化)
树木系统(Trees)批量放置支持 LOD 的树
碰撞体自动生成地形碰撞
光照支持支持烘焙光照图、Light Probe、反射探针

Terrain Data

Terrain Data是地形的“后端数据容器”,和Terrain组件一起工作,一个Terrain组件绑定一个TerrainData资源

Terrain terrain = GetComponent<Terrain>();
TerrainData data = terrain.terrainData;

TerrainData保存的内容

高度图(Heightmap)

float[,] heights = terrainData.GetHeights(0, 0, width, height);

纹理图层(Splatmaps)

float[,,] alphamaps = terrainData.GetAlphamaps(x, y, w, h);

细节对象(Details/Grass)

int[,] detailMap = terrainData.GetDetailLayer(x, y, w, h, detailLayerIndex);

树对象(Trees)

TreeInstance[] trees = terrainData.treeInstances;

分辨率信息

terrainData.heightmapResolution
terrainData.alphamapResolution
terrainData.detailResolution

示例:运行时生成地形

void GenerateTerrain()
{
  TerrainData terrainData = new TerrainData();
  terrainData.heightmapResolution = 513;
  terrainData.size = new Vector3(1000, 100, 1000);

  float[,] heights = new float[513, 513];
  for (int x = 0; x < 513; ++x)
  for (int y = 0; y < 513; ++y)
    heights[x, y] = Mathf.PerlinNoise(x * 0.01f, y * 0.01f) * 0.2f;
  
  terrainData.SetHeights(0, 0, heights);

  GameObject terrainGO = Terrain.CreateTerrainGameObject(terrainData);
}

注意事项

限制说明
只能在主线程读写不支持多线程访问(除非用 NativeArray Hack)
修改后需刷新修改 terrainData 后需设置给 Terrain 才生效
不可跨 Terrain 共享贴图索引每个地形的 TerrainLayer[] 不能乱用,需要独立处理

Terrain in Inspector

场景中的Terrain对象一般会包含以下模块

Create Neighbor Terrains

CreateNeighborTerrains

这是什么

Unity的每一个Terrain是一个地形快(chunk),当你需要构建更大的世界地图时:

你可以将多个地形快拼接在一起,形成一个无缝连接的大地图 Create Neighbor Terrains就是一个快捷工具,让你快速在上下左右形成新的Terrain,并自动设置它们之间的连接关系(Neighbor)

Unity会做什么

TerrainsNeighbor

创建后的Terrain有哪些特征

为什么需要Neighbor信息

Unity Terrain在运行时做:

也就是说,如果你手动创建了多个Terrain,没有设置Neighbor,会出现:

Tips

多Terrain的场景通常配合:

PaintTerrain

PaintTerrain

这是Unity Terrain系统用于手动“绘制”地形的工具,类似PS中的笔刷,但是用于三维地形,可以用它来:

Sub Tools

1.Raise or Lower Terrain

用笔刷将地形向上或向下推动

配合Shift反向操作

2.Set Height

把地形调整到某个统一的高度(例如建平台)

可以使用Flatten一键铺平当前Terrain

3.Smooth Height(平滑地形)

平滑地形的高低差,让地形更自然

4.Paint Texture(涂地表材质)

把不同纹理“画”到地形上,实现草地、雪地、沙漠的混合过渡

示例层:

支持自定义笔刷、混合过渡控制(Opacity)
Add Layer添加纹理层,选用PBR材质(支持BaseMap、NormalMap)

5.Terrain Holes

画出洞,让地形有缺口(可用于洞穴、地道入口)

Paint Trees

PaintTrees

Paint Trees是Unity Terrain中用于批量种植树木的工具,它支持使用不同树模型的自动随机分布、缩放、选择、LOD显示等,是创建森林、山地植被的关键工具

添加树模型

点击 “Edit Trees” → “Add Tree…”:

要求:

种树参数设置

添加好树之后,就可以开始“画树”了,主要参数如下:

参数作用
Brush Size控制笔刷直径(影响范围)
Tree Density控制单位面积内种植的数量
Tree Height/Width控制树的缩放范围(有最小和最大)
Color Variation控制颜色的随机扰动(偏红、偏绿、偏亮等)
Random Rotation勾选后自动随机 Y 轴旋转树(防止重复感)

每画一次,就会在当前区域根据密度、大小、颜色等参数生成多个树

树木渲染和性能优化

Unity的Terrain树有专门的渲染优化方案

Billboard(看板树)
LOD支持
GPU Instancing

树的碰撞

默认树时没有碰撞器的,但你可以启用碰撞

适用于:

几种树的比较

工具特点
Tree CreatorUnity 内置的老式树编辑器,支持风、LOD,效率不高,(2022版本已弃用)
SpeedTree高级树木生成工具,效果好,适合中大型项目(但是商业插件)
普通 Prefab 树自己做的 FBX + LOD,灵活但需优化得当
Terrain 树专为大量地形种树优化,性能最佳但不支持动画/刚体

Paint Details

PaintDetails

Paint Details是Unity Terrain系统中用于在地形上批量绘制“小型自然细节”的工具,它与Paint Trees类似,但用于体积更小、密度更高的细节对象,是构建自然场景的关键补充

如何添加细节类型

点击面板中的:Edit Details → Add Detail Mesh / Add Grass Texture

Unity提供两种添加方式

类型描述适用
Detail Mesh3D Mesh 物体小石头、蘑菇、落叶、低模草
Grass TextureBillboard(摄像机对面)草贴图草地、麦田、灌木等
Add Grass Texture

用于画贴图式草,性能最高

Add Detail Mesh

注意:

Paint参数

参数描述
Brush Size笔刷范围(单位为世界坐标)
Opacity笔刷强度(一次能刷多少)
Target Strength总体密度(越高越密)
Random Color / Size自动扰动草的颜色、尺寸,提升自然感
Noise Spread控制颜色/透明度变化分布程度(更逼真)

渲染与性能优化

优化特性
技术说明
Billboard 渲染仅用平面草贴图,看起来像 3D 实际是贴图
GPU InstancingDetail Mesh 会批处理,显著降低 Draw Call
LOD 剔除距离过远会剔除渲染,节省性能

注意:

风动效果设置

默认Terrain草是静态烘焙的,运行时不支持动态添加 但可以:

Terrain Settings

TerrainSetting_1 TerrainSetting_2

Basic Terrain

参数说明
Grouping ID地形自动拼接的组编号。多个地形设置相同 ID,可自动连接边界。
Auto Connect自动连接相邻地形边缘(启用后根据 Grouping ID)。
Draw控制地形是否可见。取消勾选可临时隐藏地形。
Draw Instanced使用 GPU Instancing 渲染瓦片,节省 draw call,推荐开启。
Enable Ray Tracing Support支持光线追踪(HDRP下使用)。
Pixel Error地形 LOD 精度。数值越低越精细,越高越省性能(建议 PC: 15,移动端:515)。
Minimum/Maximum Detail Limit限制 LOD 变化范围(较少使用,可保持默认)。
Base Map Dist.多远使用低分辨率贴图(节省远处渲染消耗)。
Cast Shadows地形是否投射阴影。
Reflection Probes是否使用反射探针,推荐使用 Blend Probes
Material使用的地形材质,默认是 TerrainLitDefault-Terrain-Standard

Tree & Detail Objects

参数说明
Draw控制是否渲染树木和草丛等细节。
Bake Light Probes For Trees树木是否参与光照探针烘焙。
Remove Light Probe Ringing防止光照探针边界产生光晕伪影。
Preserve Tree Prototype保持原始树模型设置。
Tree Motion Vectors设置树木是否支持运动矢量(用于动效或后期处理)。
Detail Distance草/细节渲染距离(推荐 60~100,性能关键)。
Detail Density Scale草的密度百分比(1 = 全密度,调小省性能)。
Tree Distance树木的最大渲染距离(推荐 PC: 500~1000)。
Billboard Start多远开始使用看板树(节省性能)。
Fade LengthBillboard 淡入淡出的距离,提升视觉平滑度。
Max Mesh Trees同屏最多渲染多少真实 Mesh 树,超过则不渲染。
Detail Scatter Mode控制草丛分布策略(Coverage 更均匀,推荐)。

Wind Settings for Grass

参数说明
Speed / Size / Bending控制风的速度、大小、弯曲程度。
Grass Tint草的全局色调调整(可以统一偏绿、偏黄)。

Mesh Resolution

参数说明
Terrain Width / Length / Height地形尺寸(单位:米)
Detail Resolution Per Patch每块 Detail Patch 中草的分布密度。
Detail Resolution整体地形上草丛/细节的划分格子数。越高越细腻但越耗。

Holes Settings

参数说明
Compress Holes Texture对地形挖洞贴图进行压缩,节省内存。

Texture Resolutions

参数说明
Heightmap Resolution地形高度图精度,建议:513 或 1025(越高越细腻)
Control Texture Resolution混合贴图精度,控制多种材质混合(如草+土+雪)
Base Texture Resolution远距离使用的 Base Map 分辨率(节省远处性能)

Lighting

参数说明
Contribute Global Illumination地形是否参与全局光照(用于 Lightmap/GI)
Receive Global Illumination接收 GI 的方式:LightmapsLight Probes

Lightmapping

参数说明
Scale In Lightmap地形占 Lightmap 的比例。越高越清晰(但更占用空间)
Lightmap Parameters控制烘焙时的精度、抗锯齿、间距等,可选低中高 preset。

Quality Settings

参数说明
Ignore Quality Settings是否忽略当前全局 Quality 设置的控制,强制使用当前面板设定。

主要地形制作方式

1.手动笔刷绘制(适用于小项目、快速原型)

2.外部DCC工具制作 -> 导入高度图(专业项目常用)

工具说明
World Machine高级噪声算法,真实山脉地貌生成
Gaea节点式地形设计工具,艺术控制更强
Blender / Houdini使用建模 + 程序化节点生成复杂地形
Photoshop手绘/合成灰度高度图
Substance Designer高度图 + Splatmap生成

3.程序生成地形(Runtime或编译器内)

float[,] heights = new float[width, height];
for (int x = 0; x < width; ++x)
for (int y = 0; y < height; ++y)
    heights[x, y] = Mathf.PerlinNoise(x * 0.05f, y * 0.05f);
terrainData.SetHeights(0, 0, heights);

插件生成(最强大的方式)

Unity商城中有许多强大的地形插件:

插件功能特色
Gaia Pro一站式地形生成、生态系统、海洋、天气
MapMagic 2节点图式、程序化地形、支持多地形拼接
Terrain Composer 2高自由度、数据驱动生成
MicroSplat / MegaSplat高级地形贴图混合、溶解、湿度等效果
Vegetation Studio Pro大规模草木渲染优化

Terrain LOD 编写/调优

在Unity中,Terrain的LOD是通过高度图分辨率、Pixel Error、以及细节分布分辨率来自动控制网格细分级别。

但Unity也允许通过Shader或代码进一步控制、调优甚至自定义LOD系统

Unity Terrain的LOD原理

Unity的内置Terrain使用以下机制实现LOD:

控制项功能
Pixel Error控制地形 LOD 变化的灵敏度(越高越糙但更快)。
Base Map Distance多远开始只用 base map 贴图(低分辨率预览)
Detail Resolution Per Patch每块 patch 的细节分辨率,影响草和细节的 LOD。
Detail Distance / Tree Distance控制草木显示距离。

Unity Terrain会自动把地形划分为多个Patch(块),并对每块Patch做LOD

自定义地形LOD Shader

如果想完全控制地形LOD,特别是在自写Mesh Terrain时,可以通过Shader + C#实现

示例:通过高度图动态LOD网格

// 顶点 shader 中使用 distance 控制 mesh LOD 精度
float distanceToCamera = distance(_WorldSpaceCameraPos.xyz, v.vertex.xyz);
float lodLevel = saturate(distanceToCamera / _LodRange); // 0~1
// 然后根据lodLevel选择不同精度的高度图采样

也可以用 Tessellation Shader 来实现更智能的细分控制

[domain("tri")]
[partitioning("fractional_even")]
[outputtopology("triangle_cw")]
[patchconstantfunc("PatchConstantFunction")]
[outputcontrolpoints(3)]
void TessellationHullFunction(...)
{
  // 自定义 LOD 控制 tessellationFactor
}

LOD调优策略

调优目标技术手段
提高性能增大 Pixel Error、减小 Patch 分辨率、加大 base map 距离
增加视觉质量减少 Pixel Error、开启实时阴影、使用高精度高度图
跨平台支持低端设备禁用草地、禁用阴影、减少树距离
编程控制使用 terrain.detailObjectDistance = xterrain.heightmapPixelError = x 动态调节

Terrain GPU Instancing(草、树渲染优化)

Terrain GPU Instancing是Unity用于优化草和树渲染性能的关键技术,它能显著减少Draw Call数量,加速大量重复物体的渲染,非常适合用于大场景、开放世界、森林等

什么是GPU Instancing

GPU Instancing(GPU 实例化)是指:

一次提交,多次绘制相同物体,减少CPU->GPU的调用次数

在Unity Terrain中的应用

Details GPU Instancing

Unity 2018+起支持:

操作方法:
1.打开Terrain面板->Paint Details工具

2.点击Edit Details -> Add Detail Mesh

3.在弹窗中:

4.Unity自动启用GPU实例渲染

注意:草贴图(billboard类型)默认不支持GPU Instancing,需改为Mesh类型或自定义Shader

Trees GPU Instancing

树的GPU Instancing 需要满足两个条件:

操作:
1.打开Paint Trees工具

2.添加树种时选择普通Mesh树(非SpeedTree)

3.确保其材质上开启GPU Instancing

注意事项

限制 / 要点说明
草的材质必须使用 支持 Instancing 的 Shader(如 URP Lit 或自写)
Instancing 不等于合批,它是“并行绘制”,但仍然占用 GPU VRAM
不支持完全随机模型或颜色(需通过 Shader 控制实例差异)
若草量极大,结合 LOD、Culling、地形剔除工具 更有效

高阶优化

技术功能
LOD + Crossfade减少远距离树模型复杂度
Vegetation Studio Pro / Nature Renderer插件增强 GPU 草渲染(支持更远距离、风、阴影)
Compute Shader Grass(高级)自己写草系统,全 GPU 控制,风、互动等

示例:自定义URP Shader支持Instancing

Tags {"RenderType" = "Opaque"}
Pass{
  ...
  CGPROGRAM
  #pragma vertex vert
  #pragma fragment frag
  #pragma multi_compile_instancing

  UNITY_INSTANCING_BUFFER_START(Props)
  UNITY_INSTANCING_BUFFER_END(Props)

  struct appdata
  {
    float4 vertex : POSITION;
    UNITY_VERTEX_INPUT_INSTANCE_ID
  };

  struct v2f
  {
    float4 pos : SV_POSITION;
    UNITY_VERTEX_INPUT_INSTANCE_ID
  };

  v2f vert(appdata v)
  {
    UNITY_SETUP_INSTANCE_ID(v);
    ...
  }
  ...
  ENDCG
}

使用Terrain Toolkit插件或Gaia等地形生成工具

TODO

多地形拼接、无缝过渡

Unity的Terrain是单独的GameObject,每个最多4097*4097高度图/贴图分辨率,单地形很难承载整个世界,因此需要:

将世界划分为多个Terrain块,拼接形成一个整体大地图

拼接方式

方法一:Unity内建 Create Neighbor Terrains

见上文

方法二:代码方式设置Neighbor

terrain.SetNeighbor(left, top, right, bottom);

Unity的渲染系统通过SetNeighbors方法了解哪些Terrain相邻,从而实现:

如何实现高度无缝

方法一:在编辑时使用同步工具

方法二:手动同步边界高度

当你修改Terrain A的边缘时,要同步更新Terrain B相邻边缘的高度(可通过代码或工具完成)

如何实现纹理无缝

使用相同的SplatMap(地形图层)

生态系统(草/树)边界过渡

LOD无缝过渡与裂缝问题(Crack)

Unity Terrain会自动处理LOD,但如果没有设置邻居关系或高度差距大,会出现裂缝
解决办法:

结合导航烘焙(NavMesh + Terrain)

Navigation

Runtime地形修改(Voxel、地形破坏)

运行时地形修改的主流方案概览

模式原理优点缺点
Voxel Terrain使用体素数据构建地形并动态生成 Mesh灵活,可完全修改、破坏、挖掘实现复杂,需要自定义系统
Custom Mesh Terrain自定义网格,允许直接编辑 Mesh 顶点控制精细,可用于挤压、凹陷不适合大地图或地形细节
Shader-Based Deformation用 Shader 或 Compute Shader 动态修改高度图实时效率高,可 GPU 运算精度有限,修改不可永久保存
Unity Terrain Heightmap 修改修改 TerrainData.SetHeights()保留 Unity Terrain 优势修改粒度受限,效率较低

方案一:体素地形(Voxel Terrain)

类似Minecraft风格的可破坏地形

实现流程:
1.创建体素数据结构(3D数组)

byte[,,] voxels = new byte[width, height, depth];

2.定义每种方块类型(空气、土、石头等)

3.使用Marching Cubes或Greedy Mesh算法生成Mesh

4.实现挖掘、填充逻辑(修改数组,重新生成Mesh)

5.使用Chunk分块机制优化性能

方案二:修改Unity Terrain高度图(SetHeights)

适用于地形凹陷、爆炸坑、地形塑形

示例

void DigHole(Terrain terrain, Vector3 worldPos, float radius, float depth)
{
  TerrainData tData = terrain.terrainData;

  int xRes = tData.heightmapResolution;
  int yRes = tData.heightmapResolution;

  Vector3 terrainPos = worldPos = terrain.transform.position;
  int x = (int)((terrainPos.x / tData.size.x) * xRes);
  int y = (int)((terrainPos.z / tData.size.z) * yRes);

  int r = Mathf.RoundToInt((radius / tData.size.x) * xRes);
  float[,] heights = tData.GetHeights(x - r, y - r, r * 2, r * 2);

  for (int i = 0; i < r * 2; ++i)
  {
    for (int j = 0; j < r * 2; j++)
    {
      float dist = Vector2.Distance(new Vector2(i, j), new Vector2(r, r));
      if (dist < r) heights[i, j] -= depth * (1f - dist / r);
    }
  }

  tData.SetHeights(x - r, y - r, heights);
}

注意:

方案三:自定义Mesh地形修改

可以用Procedural Mesh创建“可破坏”的Mesh地面,然后直接修改顶点高度

核心点:

GPU地形修改(Shader/Compute Shader)

适合需要大量实时修改的场景,如地面波纹、动态地形变化

常用技巧:

可参考:Unity官方例子GPU Terrain Stamp或Amplify Shader Editor示例

Terrain API

Static Properies

属性名中文说明
activeTerrain当前活动的 Terrain(场景中主地形)
activeTerrains场景中所有激活的 Terrain 列表
compressedHolesFormat压缩后地形洞孔纹理的图形格式
compressedHolesTextureFormat压缩后地形洞孔纹理的纹理格式
heightmapFormat地形高度图的图形格式
heightmapRenderTextureFormat地形高度图的 RenderTextureFormat
holesFormat未压缩的地形洞孔纹理的图形格式
holesRenderTextureFormat地形洞孔纹理的 RenderTextureFormat
normalmapFormat地形法线贴图的图形格式
normalmapRenderTextureFormat地形法线贴图的渲染纹理格式
normalmapTextureFormat地形法线贴图的纹理格式

Properties

属性名中文说明
allowAutoConnect是否自动连接邻近地形
bakeLightProbesForTrees是否为树烘焙内部光照探针(仅编辑器)
basemapDistance超过该距离时使用预计算的低分辨率底图
bottomNeighbor下方邻接地形
collectDetailPatches从内存中收集细节贴片
deringLightProbesForTrees去除树上的光照探针振铃(仅编辑器)
detailObjectDensity草和细节对象的密度
detailObjectDistance草和细节对象的最大可视距离
drawHeightmap是否绘制地形几何(高度图)
drawInstanced是否启用 GPU Instancing 渲染地形
drawTreesAndFoliage是否绘制树和细节对象
editorRenderFlags控制地形在编辑器中显示哪些内容
enableHeightmapRayTracing是否启用地形高度图的光线追踪加速结构
groupingID地形自动连接的分组 ID
heightmapMaximumLOD地形最大渲染 LOD 级别
heightmapMinimumLODSimplification最简化的渲染精度
heightmapPixelErrorLOD 切换时的误差控制
ignoreQualitySettings是否忽略 QualitySettings 中的地形配置
keepUnusedRenderingResources是否在一定帧数后释放未使用的摄像机渲染资源
leftNeighbor左侧邻接地形(X 方向负方向)
lightmapIndex静态光照贴图索引
lightmapScaleOffset静态光照贴图的缩放和偏移
materialTemplate渲染地形使用的材质模板
normalmapTexture从高度图生成的法线贴图
patchBoundsMultiplier地形边界框的缩放倍率
preserveTreePrototypeLayers树实例的图层处理方式
realtimeLightmapIndex实时光照贴图索引
realtimeLightmapScaleOffset实时光照贴图的缩放和偏移
reflectionProbeUsage反射探针使用方式
renderingLayerMask地形渲染器的渲染图层遮罩
rightNeighbor右侧邻接地形(X 方向正方向)
shadowCastingMode地形阴影模式
terrainData地形数据资源,包含高度图、纹理等
topNeighbor上方邻接地形
treeBillboardDistance树木转为 billboard 的距离
treeCrossFadeLength树从 Mesh 到 billboard 的过渡距离
treeDistance树的最大渲染距离
treeLODBiasMultiplier树 LOD 偏差乘数
treeMaximumFullLODCount完整 LOD 树的最大数量
treeMotionVectorModeOverrideSpeedTree 的运动矢量渲染模式

Public Methods

方法名中文说明
AddTreeInstance(TreeInstance)向地形添加一个树实例
Flush()强制刷新 Terrain 更改
GetClosestReflectionProbes(List)获取与地形相交的反射探针及其权重
GetKeepUnusedCameraRenderingResources()查询摄像机资源是否被保留
GetPosition()获取地形在世界空间中的位置
GetSplatMaterialPropertyBlock()获取混合材质的参数设置
SampleHeight(Vector3)采样地形在某世界位置的高度
SetKeepUnusedCameraRenderingResources(bool)设置是否保留摄像机的地形资源
SetNeighbors(left, top, right, bottom)设置地形的邻居(用于 LOD 缝合)
SetSplatMaterialPropertyBlock()设置混合材质渲染属性

Static Methods

方法名中文说明
CreateTerrainGameObject(TerrainData)TerrainData 创建带碰撞器的地形 GameObject
GetActiveTerrains(List<Terrain>)填充一个列表,获取当前激活的所有 Terrain
SetConnectivityDirty()标记当前连接状态为无效(需重新连接)

详见UnityScripting Terrain