three.js 参数曲线解析:HelixCurve 螺旋曲线指南
2026/9/8 20:18:22 网站建设 项目流程

three.js 参数曲线解析:HelixCurve 螺旋曲线指南

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

导读

HelixCurve 是 three.js 中一个直接继承自抽象基类 Curve 的参数化三维螺旋曲线(helix curve),可在空间中精确描述弹簧、DNA 双螺旋、盘绕管道等螺旋形路径。本篇将以官方 API 文档 docs/pages/HelixCurve.html.md 为主线,结合仓库中 examples/jsm/curves/CurveExtras.js 的真实源码与 examples/webgl_geometry_extrude_splines.html 示例,讲清其数学模型、getPoint采样原理、构造方法及如何配合TubeGeometry/ExtrudeGeometry生成实体网格,读完即可在你的 three.js 场景中直接落地使用。

一、HelixCurve 是什么:一条"裸"的参数曲线类

官方文档对 HelixCurve 的类注释只有一句话——"A helix curve"(一条螺旋曲线)。从源码看,它是一个纯粹的数学曲线类:

// examples/jsm/curves/CurveExtras.js(第 182-214 行,注释已精简) class HelixCurve extends Curve { getPoint( t, optionalTarget = new Vector3() ) { const point = optionalTarget; const a = 30; // 螺旋半径 radius const b = 150; // 总高度 height const t2 = 2 * Math.PI * t * b / 30; const x = Math.cos( t2 ) * a; const y = Math.sin( t2 ) * a; const z = b * t; return point.set( x, y, z ); } }

关键事实:

  • 继承关系class HelixCurve extends Curve,它复用了基类Curve全部采样、求长、求切线的方法(详见下文第四节)。
  • 不持有任何自有属性:与同文件中带scale构造参数的HeartCurveVivianiCurve等不同,HelixCurve没有自定义构造函数,半径a = 30与高度b = 150都是getPoint内部的局部硬编码常量。也就是说,创建new HelixCurve()无需(也无法)传入任何参数,得到的尺寸是固定的。
  • 所属模块:它并非 three.js 核心库的一部分,而是以"附加模块(addon)"形式与 GrannyKnot、KnotCurve、TrefoilKnot 等十余条经典曲线共同收在 CurveExtras.js 中,并在文件末尾随 export 列表 一起导出。

数学模型解读

t2 = 2π·t·(b/30)代入,当t从 0 扫到 1 时:

  • t2变化量为2π × 150/30 = 10π,即绕 Z 轴旋转 5 整圈(每圈对应高度约 30 个单位,圈距 = b / 圈数 = 150 / 5 = 30);
  • x = a·cos(t2)y = a·sin(t2):在 XY 平面上描出半径a = 30的圆周;
  • z = b·t:沿 Z 轴从 0 线性升高到 150。

因此 HelixCurve 描述的是一条沿 Z 轴上升、半径 30、总高 150、共 5 圈的右手螺旋线。若直接读取a/b两处常量,可确认这是一条典型的等距圆柱螺旋(参数方程形式为(a·cos ωt, a·sin ωt, b·t))。

二、显式引入与可用性前提

文档明确指出:"HelixCurve is an addon, and must be imported explicitly"。这是因为 three.js 的附加模块默认不打包进three核心包,需按 addon 约定路径引入:

import { HelixCurve } from 'three/addons/curves/CurveExtras.js';

在 HTML 中直接使用示例时,需要先配置 import map(参见 examples/webgl_geometry_extrude_splines.html):

<script type="importmap"> { "imports": { "three": "../build/three.module.js", "three/addons/": "./jsm/" } } </script>

随后:

import * as THREE from 'three'; import * as Curves from 'three/addons/curves/CurveExtras.js'; const helix = new Curves.HelixCurve();

官方示例 webgl_geometry_extrude_splines.html 正是采用import * as Curves的方式把包括HelixCurve在内的一组曲线登记进一个splines字典,再由 GUI 下拉切换。

三、构造函数与对象属性

new HelixCurve()

官方 API 页给出的构造函数签名即为new HelixCurve(),无参数。调用后实例从基类 Curve 构造函数 继承以下成员:

属性类型默认值说明
typestring'Curve'对象类型标识,用于序列化/反序列化识别;HelixCurve 未覆写,仍为'Curve'
arcLengthDivisionsnumber200计算曲线累计弧长时使用的细分份数;曲线很大时可调高以提升精度
needsUpdatebooleanfalsetrue表示曲线参数已变化,下次取弧长时会强制重建缓存
cacheArcLengthsArray | nullnull弧长缓存数组(私有)

其中arcLengthDivisions直接影响按弧长等距采样(getSpacedPointsgetPointAt)的精度,属于日常最可能改动的"参数"。由于 HelixCurve 自身常量不可改,若需要不同半径/高度的螺旋,可在外层对HelixCurve的取样结果做multiplyScalar等变换(官方示例正是对最终网格使用mesh.scale而非修改曲线本身,见 webgl_geometry_extrude_splines.html)。

四、getPoint(t, optionalTarget):唯一的自有方法

.getPoint( t : number, optionalTarget : Vector3 ) : Vector3

这是 HelixCurve唯一覆写的方法(基类中为抽象占位,见 Curve.js 的 getPoint 定义),负责把归一化插值因子映射为三维坐标,是所有后续采样能力的基础。

参数说明(对齐官方文档):

  • t:代表曲线上位置的插值因子,取值范围必须落在[0, 1]t = 0对应螺旋起点(30, 0, 0)t = 1对应终点(回到(30, 0, 150),因为 10π 是 2π 的整数倍)。
  • optionalTarget:可选的目标向量。若传入,结果会直接写入该向量并返回它,从而复用对象、避免 GC 压力;若省略,方法内部会用optionalTarget = new Vector3()兜底新建(见 CurveExtras.js 第 197 行)。
  • 返回值:曲线上该位置的Vector3
  • 覆写自Curve.getPoint,语义为"给定归一化因子返回曲线上的点"。

推荐写法——在动画循环等高频路径中复用向量:

const helix = new HelixCurve(); const tmp = new THREE.Vector3(); // 每帧让物体沿螺旋推进 helix.getPoint( progress, tmp ); // progress ∈ [0,1] mesh.position.copy( tmp );

从 getPoint 到曲线采样体系

基类 Curve.js 在getPoint之上构建了整套采样 API,HelixCurve 实例可全部直接调用:

方法作用实现要点
getPointAt(u, target)弧长等距取点先经getUtoTmapping把弧长因子换算回参数 t 再调getPoint(Curve.js#L83-L88)
getPoints(divisions)按参数均匀取点循环调用getPoint(d/divisions),默认 divisions=5,返回divisions+1个点(Curve.js#L97-L109)
getSpacedPoints(divisions)按弧长等距取点循环调用getPointAt(Curve.js#L121-L133)
getLength()曲线总弧长末尾累计值(Curve.js#L140-L145)
getTangent(t)该参数处单位切向量t±0.0001两点差分近似并归一化(Curve.js#L293-L313)
getTangentAt(u)弧长等距处的切向量结合getUtoTmapping(Curve.js#L323-L328)
computeFrenetFrames(segments, closed)生成 Frenet 活动标架产出切向量/法向量/副法向量数组,供TubeGeometryExtrudeGeometry沿路径定向横截面(Curve.js#L338-L450)

提示:getPointAt等弧长相关方法内部会基于arcLengthDivisions把曲线切成divisions + 1段并累计相邻点距离(getLengths 实现)。若希望螺旋轨迹上的物体匀速前进,请用getPointAt而非getPoint,因为按 t 均匀取值时,螺旋投影点之间的直线弦长并不均匀。

五、实战:把 HelixCurve 变成可见的 3D 几何

曲线本身不可渲染,需要与 three.js 的"沿路径构造几何体"能力组合。仓库官方示例 webgl_geometry_extrude_splines.html 给出的核心链路是:

const extrudePath = splines[ params.spline ]; // 例如 new Curves.HelixCurve() // TubeGeometry(path, tubularSegments, radius, radialSegments, closed) // 沿曲线扫出管状网格 const tubeGeometry = new THREE.TubeGeometry( extrudePath, // 路径曲线 100, // 轴向分段数(示例 GUI 范围为 50-500) 2, // 管道半径 3, // 径向分段数(范围 2-12) true // 是否闭合 ); const mesh = new THREE.Mesh( tubeGeometry, new THREE.MeshLambertMaterial( { color: 0xff00ff } ) ); scene.add( mesh );

在这个示例中,通过 lil-gui 下拉把spline'GrannyKnot'切换到'HelixCurve',即可直观看到由 TubeGeometry 依据 HelixCurve 采样并计算出沿路径的 Frenet 标架后扫出的螺旋管道。除了管状网格,ExtrudeGeometry也能直接接收曲线路径,因此 HelixCurve 也常用于:螺旋楼梯扶手、弹簧线圈、绕线动画、相机沿螺旋轨迹飞行(computeFrenetFrames即服务于此类相机跟随)。

一个自包含的极简可运行片段:

import * as THREE from 'three'; import { HelixCurve } from 'three/addons/curves/CurveExtras.js'; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 60, innerWidth / innerHeight, 0.1, 1000 ); camera.position.set( 80, 80, 180 ); const renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.setSize( innerWidth, innerHeight ); document.body.appendChild( renderer.domElement ); scene.add( new THREE.AmbientLight( 0xffffff, 0.6 ) ); const dir = new THREE.DirectionalLight( 0xffffff, 1.5 ); dir.position.set( 0, 1, 1 ); scene.add( dir ); const helix = new HelixCurve(); // 半径 30、总高 150、5 圈 const geo = new THREE.TubeGeometry( helix, 200, 2, 8, false ); scene.add( new THREE.Mesh( geo, new THREE.MeshStandardMaterial( { color: 0x2194ce } ) ) ); renderer.setAnimationLoop( () => renderer.render( scene, camera ) );

六、使用注意事项与局限

  1. 几何尺寸不可配置:半径 30 / 总高 150 / 5 圈的参数内嵌于源码 常量,且类未暴露scale之类的构造参数。需要其它尺寸时,优先通过节点mesh.scale变换,或对采样点集二次处理。
  2. 曲线不闭合:首末点虽在 XY 平面投影重合,但 Z 坐标相差 150,getPoint(0)getPoint(1)并不相等,不能作为闭合曲线看待(构造TubeGeometryclosed参数针对的是"路径本身是否首尾相接",此处应保持false)。
  3. t 必须归一化:文档明确 t 需在[0,1]TubeGeometry等内部实现假定曲线以归一化参数采样,越界传入会造成不可预期的延伸方向。
  4. addon 形态决定了引入路径:不要试图从three核心包解构HelixCurve——它只存在于 CurveExtras.js 这一附加模块中(该文件同时导出了 GrannyKnot、HeartCurve、VivianiCurve、KnotCurve、TrefoilKnot 等同族曲线,若做曲线集合类演示可一并引入)。

七、进一步阅读

  • 本篇 API 文档原文:docs/pages/HelixCurve.html.md
  • 螺旋曲线源码与数学参数:examples/jsm/curves/CurveExtras.js#L182-L214
  • 抽象基类Curve的完整采样/求长/切线与 Frenet 标架实现:src/extras/core/Curve.js
  • 官方可运行示例(含 HelixCurve 的 GUI 切换与扫掠演示):examples/webgl_geometry_extrude_splines.html
  • 曲线家族基类文档:Curve.html.md

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

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

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

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

立即咨询