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构造参数的HeartCurve、VivianiCurve等不同,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 构造函数 继承以下成员:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
type | string | 'Curve' | 对象类型标识,用于序列化/反序列化识别;HelixCurve 未覆写,仍为'Curve' |
arcLengthDivisions | number | 200 | 计算曲线累计弧长时使用的细分份数;曲线很大时可调高以提升精度 |
needsUpdate | boolean | false | 置true表示曲线参数已变化,下次取弧长时会强制重建缓存 |
cacheArcLengths | Array | null | null | 弧长缓存数组(私有) |
其中arcLengthDivisions直接影响按弧长等距采样(getSpacedPoints、getPointAt)的精度,属于日常最可能改动的"参数"。由于 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 活动标架 | 产出切向量/法向量/副法向量数组,供TubeGeometry、ExtrudeGeometry沿路径定向横截面(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 ) );六、使用注意事项与局限
- 几何尺寸不可配置:半径 30 / 总高 150 / 5 圈的参数内嵌于源码 常量,且类未暴露
scale之类的构造参数。需要其它尺寸时,优先通过节点mesh.scale变换,或对采样点集二次处理。 - 曲线不闭合:首末点虽在 XY 平面投影重合,但 Z 坐标相差 150,
getPoint(0)与getPoint(1)并不相等,不能作为闭合曲线看待(构造TubeGeometry时closed参数针对的是"路径本身是否首尾相接",此处应保持false)。 - t 必须归一化:文档明确 t 需在
[0,1];TubeGeometry等内部实现假定曲线以归一化参数采样,越界传入会造成不可预期的延伸方向。 - 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),仅供参考