ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

three.js ForestGenerator 使用指南:用一次 Draw Call 在地形上种出五十万棵树的森林

2026/9/7 18:33:28 拓冰建站 浏览量
three.js ForestGenerator 使用指南:用一次 Draw Call 在地形上种出五十万棵树的森林 three.js ForestGenerator 使用指南用一次 Draw Call 在地形上种出五十万棵树的森林【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js导读ForestGenerator 是 three.js 官方示例examples/jsm/generators目录下的地形森林生成器。它基于 TerrainGenerator或任何暴露sampleHeight、sampleSlope、minY、maxY与parameters.size接口的对象铺满森林支持数十万棵树木、单次实例化绘制调用完成渲染。阅读本文后你将掌握其构造函数与全部默认参数、build( terrain )的放置流程、每帧跟随相机的随机距离剔除机制以及如何将它与 webgpu_custom_fog.html 所示的场景管线集成。核心源码位置与基本用法ForestGenerator 的实现位于 examples/jsm/generators/ForestGenerator.js它依赖 TSLThree Shading Language与MeshStandardNodeMaterial因此面向 WebGPU 渲染管线。官方完整演示见 examples/webgpu_custom_fog.html其雾效与山地地形示例中同时使用了TerrainGenerator与ForestGenerator。最小用法只需两行代码const forest new ForestGenerator( { count: 500000 } ); scene.add( forest.build( terrain ) );一棵树的成本控制为了让数十万棵树只产生一次 draw callForestGenerator 将树设计成最便宜但仍能识别为树的形态每棵树是一个约 20 面的 icosphere被压扁成上尖下粗的水滴/云杉轮廓并用少量正弦噪声制造凹凸blobNoise见源码中的blobGeometry。几何体带有一套烘焙的暗底/亮冠渐变ao属性树基为 0、树冠为 1无需逐像素噪声即可得到底部阴影、顶部受光的树冠观感。通过删除非索引几何体的uv与normal属性再调用mergeVertices来自 examples/jsm/utils/BufferGeometryUtils.js把 60 个顶点焊接为12 个顶点顶点着色器执行量约减少 5 倍。最后全部放入单个InstancedMesh50 万棵树 一次绘制调用。构造函数与参数表new ForestGenerator( parameters )构造过程会以ForestGenerator.defaults为基底合并传入参数源码 165-182 行同时创建两个可实时调优的 uniform 节点from/to供剔除使用以及内部主相机位置 uniform。参数默认值说明seed1伪随机数种子保证放置结果可复现count500000要种下的树木数量单次实例化绘制detail0icosphere 细分级别0 20 面焊接后 12 顶点radius1.3树团块底部半径世界单位height4树团块基础高度distortion0.5树冠球面的凹凸程度偏粗糙针叶形而非光滑蛋形sink0.4基准点插入地表以下的深度用于藏住根部altitudeMin0.12森林占据的归一化海拔下限高于雾海平面altitudeMax0.46森林海拔上限保持在雪线以下minSlope0.55地面最小平坦度法线 y 分量更陡处保持裸岩densityFrequency0.012林斑/空地尺度世界单位minScale0.7树的缩放下限maxScale1.8树的缩放上限from300相机距离在此以内全部绘制类似 THREE.Fog 近端to620相机距离超过此值一棵不画中间地带按随机阈值淡出castShadowfalse树冠是否投射并接收阴影50 万投射体是真实开销需主动开启注意树必须在调用build()之前配置castShadow该值会在每次重建地形时于build中写回mesh.castShadow/receiveShadow。build( terrain )拒绝采样放置流程build()内部源码 57-146 行先释放上一轮结果然后完成以下步骤读取地形接口取terrain.parameters.size、terrain.minY以maxY - minY计算海拔跨度用于归一化海拔判断。生成确定性随机数与噪声场用 mulberry32 风格的createRandom以seed初始化再创建ImprovedNoise来自 examples/jsm/math/ImprovedNoise.js以随机偏移构造低频密度场densityAt(x,z)用于把森林打散成林斑与空地。逐实例循环上限count * 14次尝试避免海拔带过窄时死循环在[-size/2, size/2]内均匀随机取(x, z)调用terrain.sampleHeight(x, z)求高度若归一化海拔超出[altitudeMin, altitudeMax]则拒绝跳过调用terrain.sampleSlope(x, z)若坡度低于minSlope则拒绝悬崖裸岩不长树用密度掩码判定密度值乘上顶端smoothBlend羽化系数让树线稀疏散开而非一条整齐直线随机数大于密度则拒绝通过后写入实例矩阵dummy.position下压sink藏根旋转上做少量倾斜±0.12 rad 自由偏航0~2π缩放采用平方偏置random() * random()多数偏小、偶有巨树并叠加 XY 轴 0.85~1.15 的不对称扰动避免复制感。烘焙逐实例剔除与区域染色数据cullvec4 实例属性xyz 树的世界位置w 来自另一个独立 PRNG种子seed ^ 0x9e3779b9的随机阈值避免扰动放置本身regionfloat 实例属性用低频 perlin 噪声烘焙的区域色偏0~1让远山不同林区呈现自然的色调差异。收尾mesh.count placed只画真正种下的数量更新instanceMatrix把cull与region写成InstancedBufferAttribute最后包进名为Forest的Group返回。返回的Group可直接加入场景可反复调用build()例如地形换种子后重建见 webgpu_custom_fog.html 中generate()的实现。每帧调用跟随相机的随机距离剔除与 Fog 风格接近的远近剔除由材质在着色器中完成from以内全画、to以外一棵不画、中间地带按每棵树烘焙的随机阈值概率淡出从而把远处树冠开销摊薄到近零。forest.setCameraPosition( camera.position ); // 驱动森林剔除每帧动画循环中必须调用setCameraPosition源码 149-153 行同步主相机位置。实现上它写入的是普通 uniform 的相机坐标而不是 TSL 内置的cameraPosition节点——原因在源码注释中说明阴影 pass 里内置节点会解析为光源位置导致按错误距离剔除树木。剔除本身在材质positionNode中完成cull.xyz与相机距离相减后除以from→to区间得到归一化t当step(t, cull.w) 0即该树随机阈值小于归一化距离时整棵树的位置乘以 0 塌缩到原点实例变换先于positionNode执行因此该坐标已是世界空间实现单实例消失。参考实现见createForestMaterial源码 293-345 行。材质设计树冠不是贴图而是程序化着色createForestMaterial返回一个MeshStandardNodeMaterialmetalness 0roughness 0.88完全由场景光照树冠双色渐变ao属性0 底 1 冠驱动deep暗绿 0x1d3318 ~ 0x2e4420向bright亮黄绿 0x4c6a2e ~ 0x6e8a40的mix两套色又分别由逐实例region值混合出区域色偏。针叶团块噪声一粗一细两档mx_noise_float3D 噪声合成clump既参与颜色扰动又参与法线凹凸基于屏幕空间导数的 surface-gradient bump见bumpNormal让树冠呈现成簇针叶、随光闪烁而非光滑外壳。距离门控clump计算被包进FnIf以smoothstep( 280, 25, distance )做detailFade——近处树满细节远处低于亚像素级别的噪声直接跳过把本就顶点受限的 50 万实例网格的着色成本进一步压低。与 TerrainGenerator 的联动ForestGenerator 是为 TerrainGenerator 设计的地表配套后者将程序化山地烘焙进单个BufferGeometry并在内部保留高度网格通过sampleHeight让散布的森林或任何东西精确落在表面上见其源码 65-67 行注释。由于build( terrain )只依赖鸭子类型接口——sampleHeight(x,z)、sampleSlope(x,z)、minY、maxY与parameters.size——任何自定义地形对象实现这五个成员即可复用本生成器无需继承TerrainGenerator。一个可运行的 WebGPU 集成片段参考 webgpu_custom_fog.html L135-155、L245-256import { WebGPURenderer } from three/webgpu; import { TerrainGenerator } from three/addons/generators/TerrainGenerator.js; import { ForestGenerator } from three/addons/generators/ForestGenerator.js; const terrain new TerrainGenerator( { seed: 1, size: 900, segments: 512 } ); const forest new ForestGenerator( { count: 500000, castShadow: true } ); const terrainGroup terrain.build(); const forestGroup forest.build( terrain ); scene.add( terrainGroup, forestGroup );需要注意的联动限制count是植树目标数实际种下的棵数取决于海拔带与坡度可容纳范围构建后读取forest.mesh.count可得到真实棵数。若要开启实时可调剔除可像 webgpu_custom_fog.html 那样把forest.from/forest.to的.value暴露给控制面板其演示范围为 50~1000 与 100~1400。dispose()会释放几何体并把内部mesh/group置空build()每次自动先调用它因此重建时无需手动清理。构建树几何所需的blobGeometry与公开导出的createForestMaterial亦可独立复用。结语从源码结构与官方示例webgpu_custom_fog.html看ForestGenerator 是一套高度工程化的生态规则放置 单实例网格 程序化着色 随机距离剔除完整方案12 顶点的水滴树冠把每棵成本压到十几到几十个三角形拒绝采样让森林严格服从海拔带、坡度与密度掩码着色器内按距离门控噪声、按实例随机淡出使 50 万棵树的森林既真实又可实时飞行穿越。需要为 WebGPU 场景生成大面积远景植被时可直接将ForestGenerator与配套的TerrainGenerator一起投入生产示例使用。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考