three.js GeometryUtils 指南:用 Hilbert 曲线与 Gosper 曲线生成程序化几何坐标
2026/9/9 23:55:19 网站建设 项目流程

three.js GeometryUtils 指南:用 Hilbert 曲线与 Gosper 曲线生成程序化几何坐标

【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js

GeometryUtils 是 three.js 提供的一组曲线/空间填充曲线坐标生成工具,包含hilbert2Dhilbert3Dgosper三个函数,可用于快速生成 Hilbert 曲线与 Gosper 岛的顶点序列。本指南围绕 GeometryUtils 官方文档 展开,并结合 examples/jsm/utils/GeometryUtils.js 的源码实现与官方示例,讲解每个函数的签名、参数含义、默认值与底层递归原理,最终给出可直接运行的 three.js 实战用法,帮助你用这些几何工具搭建曲线网格、虚线路径与程序化地形骨架。

模块导入方式

GeometryUtils 是 three.js 的 addon(附加模块),不会被three核心包自动导出,必须显式导入:

import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js';

在官方示例中,这一导入通常配合 importmap 使用,例如 webgl_lines_dashed.html 中的映射方式:

<script type="importmap"> { "imports": { "three": "../build/three.module.js", "three/addons/": "./jsm/" } } </script> <script type="module"> import * as THREE from 'three'; import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js'; </script>

如果你使用构建工具(如 Vite、Rollup、webpack),也可以从打包入口直接获取。在源码层面,GeometryUtils 同时通过 examples/jsm/Addons.js 以export * as GeometryUtils from './utils/GeometryUtils.js'的形式统一导出。

方法总览

该模块共导出 3 个函数,均接受纯数值/向量参数并返回坐标数组,不依赖任何渲染上下文:

方法作用返回类型
gosper( size )生成 XY 平面上的 Gosper 岛(Gosper curve)Array.<number>(扁平坐标,每 3 个一组)
hilbert2D( center, size, iterations, v0, v1, v2, v3 )生成二维 Hilbert 曲线坐标Array.<Vector3>
hilbert3D( center, size, iterations, v0...v7 )生成三维 Hilbert 曲线坐标Array.<Vector3>

文档中三个方法均标注为(inner),表示它们是模块内部 API,以几何算法工具的形式提供给用户组合使用。

.gosper( size : number ) : Array.

生成一条位于 XY 平面内的 Gosper 曲线(又称"流涡岛"曲线,属于六边形分形曲线家族)。

参数

  • size:单个 Gosper 岛的大小(边长步长),默认值为1

返回值

Gosper 岛的点序列,返回类型为扁平的Array.<number>:坐标按[x, y, z, x, y, z, ...]的顺序排列,每 3 个数值构成一个点,且所有点的 z 坐标恒为0(曲线完全落在 XY 平面)。

源码实现:L-system 重写与海龟作图

从 examples/jsm/utils/GeometryUtils.js 的实现看,gosper由两个内部函数协作完成:

  • fractalize( config ):按 L-system(Lindenmayer 系统)规则迭代重写字符串。初始公理(axiom)为'A',共执行 4 步迭代,重写规则为:
rules: { A: 'A+BF++BF-FA--FAFA-BF+', B: '-FA+BFBF++BF+FA--FA-B' }
  • toPoints( config ):采用"海龟作图"(turtle graphics)方式逐字符解释重写后的字符串:字符+表示逆时针旋转 60°(Math.PI / 3),字符-表示顺时针旋转 60°,字符F表示沿当前方向前进size距离并记录新坐标点:
} else if ( char === 'F' ) { currX += config.size * Math.cos( angle ); currY += - config.size * Math.sin( angle ); path.push( currX, currY, 0 ); }

因此size实际上控制的是每段F前进的步长,值越大,整条曲线在空间中的跨度越大;迭代步数固定为 4 步,决定了曲线的分形细节层级。

实战:直接构建 Line 几何体

webgl_framebuffer_texture.html 给出了完整用法——用gosper的输出直接构造BufferGeometry

const points = GeometryUtils.gosper( 8 ); const geometry = new THREE.BufferGeometry(); const positionAttribute = new THREE.Float32BufferAttribute( points, 3 ); geometry.setAttribute( 'position', positionAttribute ); geometry.center(); const material = new THREE.LineBasicMaterial( { vertexColors: true } ); const line = new THREE.Line( geometry, material ); line.scale.setScalar( 0.05 ); scene.add( line );

注意:gosper返回的是扁平number数组,因此用new THREE.Float32BufferAttribute( points, 3 )直接指定每 3 个元素构成一个顶点即可,无需再组装成Vector3数组。

.hilbert2D( center : Vector3, size : number, iterations : number, v0, v1, v2, v3 ) : Array.

生成沿二维 Hilbert 曲线的坐标点。Hilbert 曲线是一种空间填充曲线,能将二维空间按细分网格顺序遍历,常用于像素遍历、图像扫描与 LOD 排序等场景。

参数

参数含义默认值
centerHilbert 曲线的中心点(Vector3new Vector3( 0, 0, 0 )
sizeHilbert 曲线的总宽度10
iterations细分次数(递归层数)1(官方文档写作 10,源码实际默认值为 1,见下方说明)
v0角索引 -X, -Z0
v1角索引 -X, +Z1
v2角索引 +X, +Z2
v3角索引 +X, -Z3

返回值

Hilbert 曲线的Vector3点数组。所有点位于同一平面(y保持为中心点的y值)。

关于 iterations 默认值的说明

官方文档 module-GeometryUtils.html.md 中hilbert2Diterations默认值写作10,而当前仓库源码 examples/jsm/utils/GeometryUtils.js 中实际声明为iterations = 1(与hilbert3D保持一致)。建议以源码实现为准:不传iterations时只生成最基础的 4 个角点,需要更高密度曲线时显式传入较大值(如 3~5),并注意点数量随4^iterations增长。

源码实现:四象限递归重排

从 examples/jsm/utils/GeometryUtils.js 的实现可以看出,hilbert2D将中心点周围按 -X/-Z、-X/+Z、+X/+Z、+X/-Z 顺序排列出 4 个角向量vec_s,再按传入的v0..v3顺序重排为vec。当iterations递减后仍非负时,对 4 个子象限递归调用,每层把size减半并交换角索引组合:

if ( 0 <= -- iterations ) { return [ ...hilbert2D( vec[ 0 ], half, iterations, v0, v3, v2, v1 ), ...hilbert2D( vec[ 1 ], half, iterations, v0, v1, v2, v3 ), ...hilbert2D( vec[ 2 ], half, iterations, v0, v1, v2, v3 ), ...hilbert2D( vec[ 3 ], half, iterations, v2, v1, v0, v3 ) ]; } return vec;

这一"重排-递归-拼接"结构正是 Hilbert 曲线保持空间连续性的核心:相邻子曲线的连接处首尾相接,整条曲线可以一笔画遍历整个区域。

.hilbert3D( center : Vector3, size : number, iterations : number, v0...v7 ) : Array.

生成沿三维 Hilbert 曲线的坐标点,是hilbert2D的立体推广,曲线在三维空间内逐格遍历。

参数

参数含义默认值
centerHilbert 曲线的中心点(Vector3new Vector3( 0, 0, 0 )
sizeHilbert 曲线的总宽度10
iterations细分次数(递归层数)1
v0角索引 -X, +Y, -Z0
v1角索引 -X, +Y, +Z1
v2角索引 -X, -Y, +Z2
v3角索引 -X, -Y, -Z3
v4角索引 +X, -Y, -Z4
v5角索引 +X, -Y, +Z5
v6角索引 +X, +Y, +Z6
v7角索引 +X, +Y, -Z7

返回值

Hilbert 曲线的Vector3点数组。八个角索引的坐标方向见 examples/jsm/utils/GeometryUtils.js:先以center为中心按 ±X、±Y、±Z 构造正方体 8 个顶点vec_s,再按v0..v7顺序重排。

源码实现:八象限递归重排

与二维版本类似,hilbert3D 在递归时对 8 个子立方体分别传入不同的角索引组合:

if ( -- iterations >= 0 ) { return [ ...hilbert3D( vec[ 0 ], half, iterations, v0, v3, v4, v7, v6, v5, v2, v1 ), ...hilbert3D( vec[ 1 ], half, iterations, v0, v7, v6, v1, v2, v5, v4, v3 ), // ... 其余 6 个子立方体 ]; } return vec;

点数量随8^iterations增长,因此迭代次数不宜过大;官方示例普遍使用iterations = 1(生成 8 个顶点)或recursion = 1,配合样条插值获得平滑路径。

实战:三维 Hilbert 曲线 + 虚线渲染

webgl_lines_dashed.html 展示了完整的三维曲线构建流程:先由hilbert3D生成控制点,再用CatmullRomCurve3做样条细分,最后以LineDashedMaterial渲染虚线:

const subdivisions = 6; const recursion = 1; const points = GeometryUtils.hilbert3D( new THREE.Vector3( 0, 0, 0 ), 25.0, recursion, 0, 1, 2, 3, 4, 5, 6, 7 ); const spline = new THREE.CatmullRomCurve3( points ); const samples = spline.getPoints( points.length * subdivisions ); const geometrySpline = new THREE.BufferGeometry().setFromPoints( samples ); const line = new THREE.Line( geometrySpline, new THREE.LineDashedMaterial( { color: 0xffffff, dashSize: 1, gapSize: 0.5 } ) ); line.computeLineDistances(); scene.add( line );

同样的hilbert3D调用模式也出现在 webgl_lines_colors.html、webgl_lines_fat.html、webgpu_lines_fat.html 与 webgpu_instance_points.html 中,可见它是构建三维空间填充路径的标准入口。在这些示例中iterations均为1,仅通过修改size(20~200)控制曲线在场景中的整体跨度。

常见问题与注意事项

  • 返回类型不一致gosper返回扁平number数组,需配合Float32BufferAttribute( points, 3 )使用;hilbert2D/hilbert3D返回Vector3数组,可直接传给CatmullRomCurve3setFromPointsLine
  • 迭代次数与顶点规模:二维曲线点数为4^iterations,三维曲线点数为8^iterations,指数增长非常快。官方示例默认只用1次迭代,若要增加细节建议配合样条细分(如CatmullRomCurve3.getPoints)而非盲目提高迭代次数。
  • v0..v7的作用:这些参数用于指定遍历立方体/正方形顶点的顺序,默认按坐标顺序排布;修改它们会改变曲线走向与旋转方向,可用于对齐到特定坐标轴习惯。
  • 模块为 addon:使用前必须显式 import(见 导入方式),核心包three不会自动包含该模块。

延伸阅读

  • 源码:examples/jsm/utils/GeometryUtils.js
  • 官方文档:docs/pages/module-GeometryUtils.html.md
  • 二维/三维 Hilbert 曲线的原始实现思路分别参考 openprocessing.org 上 sketch 15493 与 user 5654 的作品,Gosper 曲线参考 nitaku 的 gist(上述来源已在源码 JSDoc 中标注)
  • 相关示例:webgl_lines_dashed.html、webgl_lines_fat.html、webgl_framebuffer_texture.html、webgpu_instance_points.html

【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询