资讯动态

three.js TerrainGenerator 完全指南:在场景中生成程序化山脉地形(烘焙、侵蚀、TSL 自着色与表面采样)

发布时间:2026/9/10 15:39:37 来源:尧图企业网站定制
three.js TerrainGenerator 完全指南在场景中生成程序化山脉地形烘焙、侵蚀、TSL 自着色与表面采样【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.jsTerrainGenerator 是 three.js 官方示例集中提供的一个程序化地形生成器它把一段虚假侵蚀分形高度场烘焙成一张单一的三维网格 BufferGeometry并返回一个可以直接scene.add的THREE.Group。本文以 TerrainGenerator.html.md 为骨架结合其 源码实现完整讲解它的构造函数与全部参数、从分形高度场到热侵蚀再到网格化的构建管线、TSL 自着色原理以及如何通过sampleHeight/sampleSlope让森林等场景元素精准贴合地面读完后你可以独立生成、参数化调优并二次开发一套免贴图的山脉地形。什么是 TerrainGenerator从官方文档的类描述可以看出它的定位非常明确将一段程序化山脉地形烘焙进单个THREE.BufferGeometry返回一个开箱即用、可直接加入场景的THREE.Group整个地形不需要任何材质贴图或外部纹理——表面颜色与法线全部在着色器中由 TSL 实时推导烘焙后的高度网格通过TerrainGenerator#sampleHeight暴露给外部方便散布森林或其他对象精确坐在地表上。也就是说TerrainGenerator 是几何生成CPU 端烘焙与着色GPU 端 TSL分工的典型CPU 只负责一次性算出高度场并构建网格GPU 则负责把高度、坡度、距离等信息实时解释成草甸、森林、岩壁、碎石和雪的颜色层次。基本用法与代码示例官方文档给出的最小可用示例只有两行const terrain new TerrainGenerator( { seed: 1 } ); scene.add( terrain.build() );第一行用种子seed: 1构造生成器第二行调用build()完成一次完整的烘焙内部会自动dispose上一次的结果并把生成的山脉分组加入场景。由于生成过程是完全确定性的由 mulberry32 伪随机数驱动见 TerrainGenerator.js同样的参数总是烘焙出同一座山脉想要换一座山只需改seed或其他参数后再次调用build()即可。构造函数与全部参数new TerrainGenerator( parameters )构造时传入一个可选的参数对象内部通过与默认值合并得到完整配置this.parameters Object.assign( {}, TerrainGenerator.defaults, parameters );构造阶段还会创建两个uniformminHeight/maxHeight初始 0 和 1以及一个基于 TSL 的MeshStandardNodeMaterial——该材质在build()烘焙出真实高度范围后会被更新。源码见 TerrainGenerator.js。默认参数表全部默认参数定义在TerrainGenerator.defaultsTerrainGenerator.js下表完整列出了参数、默认值与源码注释给出的含义参数默认值含义依据源码注释seed1随机种子决定山脉形态size200方形地形的边长世界单位segments192每边的网格四边形数顶点数 (segments 1)²heightScale65峰谷高度夸张系数世界单位frequency0.01基础噪声频率一座山的占地面积octaves5分形叠加的倍频层数lacunarity1.97逐层倍频的频率步长刻意偏离 2避免倍频间栅格锁定gain0.5逐层倍频的振幅步长即 persistenceerosion0.7导数阻尼强度越大越能压平谷底、锐化山脊warp0.35域扭曲强度噪声单位让山脊河谷蜿蜒valleyBias1.2作用在高度上的幂曲线用于压平雾区谷底seaLevel0.150..1在缩放前先减去使谷底沉到y 0之下talus1热侵蚀的休止角rise/run越小地面越平缓talusPasses12热侵蚀迭代次数0表示关闭在真实示例里地形常用更大的规模例如 webgpu_custom_fog.html 用size: 900, segments: 512, frequency: 0.0065, heightScale: 150生成了约 512×512 顶点、峰值高达约 130 的巨型山脉并通过 inspector GUI 暴露了erosion与valleyBias的实时调节。build()完整的烘焙管线build()TerrainGenerator.js在一次调用中串起了整个生成流程按顺序包括五步计算坐标网格根据segments与size计算每条世界坐标线coord[i] i / segments * size - size/2使地形以原点为中心铺在 XZ 平面上Y 轴向上边长size。采样烘焙高度场对N × NN segments 1个网格点逐一调用heightField(p)生成的函数height(x, z)把结果写入Float32Array这就是后续用于sampleHeight双线性采样的烘培高度网格。热侵蚀可选当talusPasses 0时调用thermalErode把超过休止角的坡面松弛掉。铺设顶点并计算高度范围把每个网格点写成(x, y, z)顶点同时统计全场min/max高度写入this.minHeight、this.maxHeight两个 uniform——它们会被着色器引用使色带始终贴合真实的谷底与峰顶。生成索引并构建网格按菱形花纹交错生成三角形索引构造BufferGeometry、写入position属性、setIndex后调用computeVertexNormals()再以MeshStandardNodeMaterial创建Mesh开启投射与接收阴影最后包进一个名为Terrain的Group返回。build()会保留内部状态heights、gridSize、minY、maxY等字段供后续表面采样使用重复调用会先dispose()上一轮的几何体因此同一实例可安全地反复重新生成参考示例的regenerate按钮逻辑 webgpu_custom_fog.html。高度场算法原理导数阻尼分形求和Quilez 式虚假侵蚀官方文档把这套高度场概括为derivative-damped fractal sumQuilezs fake erosion核心实现在heightField与eroded函数TerrainGenerator.js。可以拆成几个机制来理解1. 确定性噪声与种子去相关。底层使用的是ImprovedNoisethree.js 自身的柏林噪声实现其置换表固定因此种子只能平移采样窗口。源码通过 mulberry32 PRNG 抽取offsetX、offsetZ两个平移量以及一个逐倍频的 z 切片slice让不同种子生成互不相关的山脉。2. 导数阻尼是虚假侵蚀的核心。传统 fBm 只是逐倍频叠加噪声。这里在叠加第i个倍频前先通过有限差分步长e 0.004链式法则乘上freq累计当前运行梯度dX/dZ再把本倍频振幅除以(1 erosion * (dX² dZ²))sum amp * n / ( 1 erosion * ( dX * dX dZ * dZ ) );效果正如文档所说在坡度已经陡峭的地方压制后续倍频的高频细节把细节集中到被风化过的山脊线上谷底则因运行梯度小而保留平滑。erosion越大谷底越平、山脊越锐利。3. 逐倍频域旋转打散网格对齐。每个倍频采样前都对域做一次约 37° 的旋转矩阵[0.8 -0.6; 0.6 0.8]打破柏林噪声固有的轴对齐栅格感同时每层频率乘lacunarity、振幅乘gain。lacunarity刻意取 1.97 而非 2就是为了防止倍频之间栅格锁定产生人工痕迹。4. 低频域扭曲让山脊蜿蜒。主采样位置先被一个两倍频、低频的低幅场warpField在 x/z 两个方向各偏移warp个噪声单位于是山脊与河谷不再笔直延伸而呈现自然蜿蜒。5. 幂曲线与海平面。最终高度经valleyBias次幂压缩低地压平成雾床并乘以 1.1、clamp 到 1 后减去seaLevel再乘heightScale得到世界高度——谷底因此沉到y 0以下。热talus侵蚀把分形针峰安顿成真实山脊分形求和天然会产生四周都陡峭的针状尖峰。文档指出热侵蚀用talus岩屑休止角概念把这些尖峰松弛为真实山脊。其实现thermalErodeTerrainGenerator.js作用于已烘焙的高度网格机制如下先算出单个格元允许的最大高度落差drop talus * cellSizecellSize size / segments即两格之间的休止角每一轮遍历每个格点相对 4 邻域的超高量hi - h[neighbor] - drop边界处视为 0累加得到sum与最大峰值peak每轮把carry 0.5倍的最陡超高量从高点扣除并按各下坡邻域超出量的比例分配到邻域delta缓冲区分摊所有格点统计完成后统一把delta加回高度场保证物质守恒且结果与格子遍历顺序无关。由于尖峰四周同时过陡多轮迭代下它们流失最快而单侧宽阔的坡面能保留形态。默认 12 轮、talus 1设talusPasses 0可完全关闭该阶段。菱形交替对角线网格消除单向纹理网格采用交错四边形对角线alternating quad diagonals三角化索引生成逻辑以(ix iz) % 2为判据在每两个相邻四边形间切换对角线的方向TerrainGenerator.js。这样网格被读作菱形而非带单向纹理的斜纹文档强调其好处是即使网格很粗也能保持轮廓外形且没有方向性颗粒感one-way grain。表面着色纯 TSL零贴图文档中grass, forest, rock, scree and snow, with detail normals and aerial perspective全部由terrainMaterialTerrainGenerator.js以MeshStandardNodeMaterial的节点方式程序化实现。光照本身太阳、天光填充、积雪的冷暖色调仍来自场景中的光源与环境贴图这里只创作颜色、粗糙度与细节法线。两个驱动量归一化海拔altitude (positionWorld.y - minHeight)/(maxHeight - minHeight)谷底 0 → 峰顶 1与地表平坦度flatness normalWorld.y平地 1垂直崖面 0。两者由build()写入的真实高度 uniform 驱动色带永远贴合实际地形。生态色带干鼠尾草橄榄绿草甸、暗色森林带落在flatness高的中坡恰好是后续散布树所在之处、暖灰棕岩壁、亮色碎石、青苔、新鲜积雪与风积雪多处用mx_noise_float生成细碎抖动让林线/雪线呈现破碎过渡而非干净等高线。岩层纹理两段不同频率的垂直正弦带叠加噪声摆动模拟岩壁的分层岩床strata低处平缓岩面长出地衣遮罩陡崖保持裸露灰岩。细节法线基于 Mikkelsen 表面梯度法bumpNormalTerrainGenerator.js用屏幕空间导数扰动法线叠加三倍频世界空间噪声浮雕并按距离淡出detailFade且把噪声采样放在真实If分支内让远处占绝大多数的片元跳过昂贵的噪声采样。空中透视aerial perspective远处地面被去饱和并抬向0xcfc8ba冷雾色使山脉向雾霭深处退去、层次分明smoothstep(180, 820, distance)。一个值得注意的细节普通bumpMap是通过偏移 UV 来读取高度图的对以世界坐标索引的高度毫无意义所以这里直接喂入屏幕空间导数height.dFdx()/dFdy()把扰动算在视图法线上。表面采样 APIsampleHeight 与 sampleSlope为了让散布的森林或其他对象恰好坐在地表上地形烘焙网格被完整保留并对外暴露采样接口sampleHeight( x, z )TerrainGenerator.js在build()后按世界坐标查询地表高度。实现先把(x, z)夹取到地形范围换算成网格坐标再做四角双线性插值h00/h10/h01/h11返回平滑、连续的世界y值。sampleSlope( x, z )TerrainGenerator.js用size/segments的有限差分步在 x/z 方向取两个sampleHeight差分返回法线 y 分量近似——平地接近 1、悬崖接近 0。注释特别说明它是零分配实现方便廉价地批量测试大量候选植树点。实战协同让 ForestGenerator 的森林坐上山坡官方文档明确写道a scattered forest ( or anything else ) can sit exactly on the surface。这正是 ForestGenerator 的用法它接受任何暴露了sampleHeight、sampleSlope、minY/maxY与parameters.size的对象也就是TerrainGenerator实例。其build( terrain )内部用拒绝采样散布数十万棵树的单实例网格随机取(x, z)→sampleHeight求y→ 用归一化海拔判断是否落在允许的altitudeMin/altitudeMax区间雾床之上、雪线之下→ 用sampleSlope拒绝过陡的崖面 → 再叠加低频密度遮罩决定是否着床ForestGenerator.js。因此 TerrainGenerator 的高度场、坡度场和参数实际上定义了整套生态系统的底盘。一个可完整运行的参考场景是 webgpu_custom_fog.html它用TerrainGenerator生成 900 世界单位、heightScale: 150的大地形再在其上种植count: 500000的森林并配合自定义 TSL 雾效其generate()展示了地形重建 → 森林随地形重建的标准联动写法terrainGroup terrain.build(); forestGroup forest.build( terrain ); scene.add( terrainGroup ); scene.add( forestGroup );下图即该示例的运行效果——程序化山脉、坡面森林带与自定义雾谷的组合完整示例见 examples/webgpu_custom_fog.html使用前提与注意事项依赖 WebGPU 渲染路径与 TSLTerrainGenerator 源码从three/webgpu导入MeshStandardNodeMaterial、从three/tsl导入着色节点因此它面向 three.js 的 WebGPU 渲染器WebGPURenderer使用示例中统一用import * as THREE from three/webgpu。参数在 build 时生效所有地形形态参数如erosion、valleyBias、seed在build()时被读取烘焙改动后需要重新调用build()才会生效。阴影更新地形与森林都是静态几何。示例中把平行光阴影的autoUpdate关闭在地形重建或太阳移动时才把shadow.needsUpdate置真、手动重渲一次阴影贴图避免每帧重复开销webgpu_custom_fog.html。内存释放dispose()释放上一轮BufferGeometry森林端也需相应释放其InstancedMesh几何体。反复重建前应先移除旧group并 dispose避免 GPU 内存累积。更深入可直接阅读生成器的全部源码与同目录姊妹生成器TerrainGenerator.js、ForestGenerator.js以及同目录下的 TreeGenerator.js、CityGenerator.js后者同样是输出可加入场景的Group的对象生成器可在其中找到更多程序化内容生成的参考模式。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价