找回密码
 立即注册
首页 业界区 业界 从分形到森林——使用 Three.js 创建逼真的 3D 树木 ...

从分形到森林——使用 Three.js 创建逼真的 3D 树木

注思 昨天 21:50
本文是由 EZ-Tree 作者撰写的一篇文章的译文。EZ-Tree 是一款基于 three.js 的插件,能够生成高度逼真的树木模型。本文详细阐述了作者在创作 EZ-Tree 过程中的一些实践经历与核心思路,读者可从中汲取相关技术知识,获取有益的创作灵感。
探索 EZ-Tree 如何利用程序生成和 Three.js 创建逼真的 3D 树模型背后的算法。

自从14岁开始学习编程以来,我就一直对如何用代码模拟现实世界着迷。大学二年级时,我挑战自己,尝试编写一个能够生成树的3D模型的算法。这是一个有趣的实验,也取得了一些有趣的成果,但最终代码还是被束之高阁,尘封在了我的移动硬盘深处。
几年后,我重新发现了那段原始代码,并决定将其移植到 JavaScript(并进行了一些改进!),以便可以在 Web 上运行它。
最终成果是EZ-Tree,一个基于 Web 的应用程序,您可以在其中设计自己的 3D 树,并将其导出为 GLB 或 PNG 格式,以便在您自己的 2D/3D 项目中使用。您可以在这里找到 GitHub 代码库。EZ-Tree 使用Three.js进行 3D 渲染,Three.js 是一个基于 WebGL 的流行库。
在本文中,我将详细介绍我用来生成这些树的算法,并解释每个部分如何对最终的树模型做出贡献。

什么是程序生成?

首先,了解什么是程序生成可能会有所帮助。
程序生成本质上就是根据一组数学规则创建“某物”。以树为例,我们首先观察到的是,树干会分叉成一个或多个树枝,每个树枝又会分叉成一个或多个树枝,以此类推,最终形成一片树叶。从数学/计算机科学的角度来看,我们可以将其建模为一个递归过程。
让我们继续以这个例子为例。
如果我们观察自然界中树木的一根树枝,我们会发现一些事情。



  • 分支的半径和长度都比它所连接的分支要小。
  • 树枝的粗细向末端逐渐变细。
  • 根据树的种类,树枝可以是笔直的,也可以是扭曲的,向各个方向弯曲。
  • 枝条往往会朝着阳光的方向生长。
  • 树枝从树干水平伸展时,重力会将它们向下拉向地面。这种拉力的大小取决于树枝的粗细和树叶的数量。
所有这些观察结果都可以被归纳成各自的数学规则。然后,我们可以将所有规则组合起来,创造出类似树枝的形状。这就是所谓的涌现行为,它指的是许多简单的规则可以组合在一起,创造出比各个部分更复杂的事物。

L系统

数学中有一个领域试图将这类自然过程形式化,称为林登迈尔系统,或更常见的L系统。L系统是一种创建复杂模式的简单方法,常用于模拟植物、树木和其他自然现象的生长。它们从一个初始字符串(称为公理)开始,并反复应用一组规则来重写该字符串。这些规则定义了字符串的每个部分如何转换为新的序列。然后,可以使用绘图指令将生成的字符串转换为视觉模式。
虽然我即将向您展示的代码没有使用 L 系统(当时我根本不知道它们),但原理非常相似,两者都基于递归过程。

                                           使用 L 系统生成的树的示例(来源:维基百科)

理论就说到这里,让我们直接来看代码吧!

树生成过程

树的生成过程始于该generate()方法。该方法初始化用于存储分支和叶子几何形状的数据结构,设置随机数生成器(RNG),并通过将树干添加到分支队列来启动该过程。
  1. // The starting point for the tree generation process
  2. generate() {
  3.   // Initialize geometry data
  4.   this.branches = { };
  5.   this.leaves = { };
  6.   // Initialize RNG
  7.   this.rng = new RNG(this.options.seed);
  8.   // Start with the trunk
  9.   this.branchQueue.push(
  10.     new Branch(
  11.       new THREE.Vector3(),              // Origin
  12.       new THREE.Euler(),                // Orientation
  13.       this.options.branch.length[0],    // Length
  14.       this.options.branch.radius[0],    // Radius
  15.       0,                                // Recursion level
  16.       this.options.branch.sections[0],  // # of sections
  17.       this.options.branch.segments[0],  // # of segments
  18.     ),
  19.   );
  20.   // Process branches in the queue
  21.   while (this.branchQueue.length > 0) {
  22.     const branch = this.branchQueue.shift();
  23.     this.generateBranch(branch);
  24.   }
  25. }
复制代码
Branch数据结构

该Branch数据结构保存了生成分支所需的输入参数。每个分支都使用以下参数表示:

  • origin– 定义三维空间中分支的起始点(x, y, z)。
  • orientation– 使用欧拉角指定分支的旋转(pitch, yaw, roll)。
  • length– 树枝从根部到顶端的总长度
  • radius– 设置树枝的粗细
  • level – 表示递归深度,主干从第 0 层开始。
  • sectionCount– 定义树干沿其长度方向被分割的次数。
  • segmentCount– 通过设置树干周长周围的分段数来控制平滑度。

了解分支队列

这branchQueue是树生成过程中至关重要的一部分。它保存着所有待生成的分支。第一个分支从队列中取出,并生成其几何形状。然后,我们递归地生成Branch子分支的对象,并将它们添加到队列中以便稍后处理。这个过程会一直持续到队列被填满为止。

生成分支

该generateBranch()函数是树生成过程的核心。它包含了根据Branch对象中包含的输入创建单个分支几何形状所需的所有规则。
让我们来看一下这个函数的关键部分。

三维几何入门

在生成树枝之前,我们首先需要了解 Three.js 中是如何存储 3D 几何体的。
在表示三维物体时,我们通常使用索引几何体,它通过减少冗余来优化渲染。几何体由四个主要部分组成:

  • 顶点——三维空间中定义物体形状的点列表。每个顶点都由一个THREE.Vector3包含其 x、y 和 z 坐标的数组表示。这些点构成了几何体的“基本组成单元”。
  • 索引——一个整数列表,用于定义顶点如何连接形成面(通常是三角形)。索引引用已有的顶点,而不是为每个面存储重复的顶点,从而显著降低内存使用量。例如,三个索引 [0, 1, 2] 使用顶点列表中的第一个、第二个和第三个顶点构成一个三角形。
  • 法线——“法线”向量描述了顶点在三维空间中的方向;简而言之,就是表面指向的方向。法线对于光照计算至关重要,因为它们决定了光线如何与表面相互作用,从而产生逼真的阴影和高光。
  • UV坐标——一组二维坐标,用于将纹理映射到几何体上。每个顶点都被赋予一对介于0.0和1.0之间的UV值,这些值决定了图像或材质如何包裹物体表面。这些坐标使纹理能够与几何体的形状正确对齐。
该generateBranch()函数逐节生成分支顶点、索引、法线和 UV 坐标,并将结果附加到各自的数组中。
  1. this.branches = {
  2.   verts: [],
  3.   indices: [],
  4.   normals: [],
  5.   uvs: []
  6. };
复制代码
几何体全部生成后,将这些数组组合成一个网格,该网格完整地表示了树的几何形状及其材质。

  1.                   _<font >左图:单个树枝的线框图;右图:应用了简单平面光照模型后的同一树枝。</font>_
复制代码
从上图可以看出,树枝沿其长度方向由 10 个独立的节段组成,每个节段又有 5 个边(或线段)。我们可以调整树枝的节段数和线段数,从而控制最终模型的细节程度。数值越高,模型越平滑,但性能也会相应降低。
既然如此,让我们深入了解一下树生成算法吧!
初始化

[code]let sectionOrigin = branch.origin.clone();let sectionOrientation = branch.orientation.clone();let sectionLength = branch.length / branch.sectionCount;let sections = [];for (let i = 0; i

相关推荐

您需要登录后才可以回帖 登录 | 立即注册