three.js DecoratedTorusKnot4a 解析:参数化环面纽结曲线的导入、采样与 TubeGeometry 建模实战
DecoratedTorusKnot4a 是 three.js 官方 addon(附加模块)CurveExtras.js 中提供的一条参数化三维装饰环面纽结曲线,继承自 Curve 基类,仅需在构造时传入一个 scale 缩放值即可得到一条平滑、闭合、形态复杂的样条路径。本文以 docs/pages/DecoratedTorusKnot4a.html.md 文档为主线,结合其源码实现与官方示例,完整讲解它的导入方式、构造与属性、getPoint 采样原理,以及如何用 TubeGeometry 把它变成可渲染的 3D 管道模型,让你在特效路径、样条扫掠建模等场景中直接复用这类参数曲线。
背景定位:CurveExtras 中的“装饰环面纽结”曲线族
在 three.js 中,Curve 是一条可采样的数学路径抽象,凡继承它的类都需要实现 getPoint( t, optionalTarget ) 来回答“曲线上参数 t 对应的三维坐标在哪”。官方把大量趣味参数曲线集中收纳在 examples/jsm/curves/CurveExtras.js 这一个 addon 文件中,文件头部注释就写明它是 "A bunch of parametric curves"(一组参数曲线),包括:
GrannyKnot(外婆结)HeartCurve(心形曲线)VivianiCurve(维维亚尼曲线)TrefoilKnot(三叶纽结)、TorusKnot、CinquefoilKnot(五叶纽结)TrefoilPolynomialKnot、FigureEightPolynomialKnotDecoratedTorusKnot4a、DecoratedTorusKnot4b、DecoratedTorusKnot5a、DecoratedTorusKnot5c
其中 DecoratedTorusKnot4a(装饰环面纽结 4a)与 4b / 5a / 5c 构成一组“装饰化环面纽结”子族。该文件中每个类的声明都以 @augments Curve 标注,且彼此共享相同的接口约定,说明整族曲线面向同一类应用场景:把一条参数化路径交给管状几何体或相机轨迹去使用。
导入方式:addon 必须显式 import
DecoratedTorusKnot4a 属于 three.js 的 addon(附加模块),并未被包含在核心构建中,使用前必须像 docs/pages/DecoratedTorusKnot4a.html.md 文档给出的那样显式导入:
import { DecoratedTorusKnot4a } from 'three/addons/curves/CurveExtras.js';
这一点可以通过 examples/jsm/curves/CurveExtras.js 末尾的导出列表得到印证——该文件一次性导出 GrannyKnot、HeartCurve、VivianiCurve、TrefoilKnot、TorusKnot 以及 DecoratedTorusKnot4a 等共 14 个类,任何期望使用这些曲线的代码都需要按 addon 规范从 three/addons/curves/CurveExtras.js 引入。在本地以裸 import 使用 addon 时,需要配置 import map 把 three 与 three/addons/ 映射到本地构建产物(官方示例的做法可见下文)。
构造函数与 scale 属性
构造签名
new DecoratedTorusKnot4a( scale : number )
根据文档,scale 为“The curve's scale”(曲线的缩放倍率)。仓库源码中构造函数的实际签名是带默认参数的:
constructor( scale = 40 ) {
super();
this.scale = scale;
}
这里需要注意一处文档与源码的差异:本文档 Constructor 小节把参数默认值标注为 1,而 Properties 小节与源码实现(examples/jsm/curves/CurveExtras.js#L492-L504)均明确默认值为 40。以源码为准,不传参时的实际默认缩放是 40,即 new DecoratedTorusKnot4a() 与 new DecoratedTorusKnot4a( 40 ) 等价。构造函数内仅做一件事:调用父类构造并把 scale 存到实例属性上。
.scale 属性
decoratedTorusKnot4a.scale : number
该属性保存曲线的缩放倍率,默认值 40。它只在采样阶段参与计算(见下文 getPoint 的 multiplyScalar( this.scale )),因此构造后修改 scale 会立即影响后续所有采样结果,可把它当作曲线尺寸的“一键缩放”旋钮。也正因为缩放仅作用于最终坐标,创建曲线后再改变 scale 完全安全,不必重新构造实例。
深入源码:getPoint 的实现与参数化数学
DecoratedTorusKnot4a 作为 Curve 的子类,必须实现 .getPoint(),文档标注该方法 Overrides(重写自)Curve#getPoint。仓库中的真实实现位于 examples/jsm/curves/CurveExtras.js#L513-L525:
getPoint( t, optionalTarget = new Vector3() ) {
const point = optionalTarget;
t *= Math.PI * 2;
const x = Math.cos( 2 * t ) * ( 1 + 0.6 * ( Math.cos( 5 * t ) + 0.75 * Math.cos( 10 * t ) ) );
const y = Math.sin( 2 * t ) * ( 1 + 0.6 * ( Math.cos( 5 * t ) + 0.75 * Math.cos( 10 * t ) ) );
const z = 0.35 * Math.sin( 5 * t );
return point.set( x, y, z ).multiplyScalar( this.scale );
}
方法签名约定
.getPoint( t : number, optionalTarget : Vector3 ) : Vector3
t:插值因子,表示曲线上某处位置,取值必须位于闭区间[0, 1]。0对应曲线起点,1对应终点。optionalTarget(可选):结果写入的目标向量。从源码可见它被当作局部变量复用,若省略则默认新建一个Vector3。- 返回值:曲线在该参数处的位置向量。
数学结构拆解
令 θ = 2π·t,把三个分量拆开看:
半径调制:R(θ) = 1 + 0.6 · ( cos(5θ) + 0.75 · cos(10θ) )
x(θ) = R(θ) · cos(2θ)
y(θ) = R(θ) · sin(2θ)
z(θ) = 0.35 · sin(5θ)
由此可以得到三个结构性结论:
- x/y 平面做两重回转:
cos(2θ)、sin(2θ)表示平面上相对主循环有两倍的角频率,纵向z与半径R分别以 5 倍、10 倍谐波调制,本质是一条 (2, 5) 型环面纽结的装饰化变体——先按 2、5 两个互质周期缠绕出纽结骨架,再叠加上 10 倍高频的“装饰花瓣”。这是它与普通TorusKnot曲线的核心区别:多了一层高频余弦装饰项。 - 曲线天然闭合:由于所有三角函数项的角度都是
2θ、5θ、10θ(均为2π的整数倍),t=0与t=1位置严格重合,且各阶导数连续。因此它是一条光滑闭合曲线,这为使用closed: true的管道几何体或循环相机轨迹提供了条件。 - 可推出的坐标量程:由
|cos| ≤ 1可推出R(θ)落在[1 − 0.6×1.75, 1 + 0.6×1.75] = [−0.05, 2.05]区间,z落在[−0.35, 0.35]区间,即单位曲线 x/y 大致在 ±2.05 内、z 在 ±0.35 内。因此当scale取默认值40时,最终曲线的包络约在 x/y 方向 ±82、z 方向 ±14 的场景单位内;需要更大或更小的模型体积时,直接调整scale即可按比例缩放。
从基类继承的采样体系
文档特别标注 getPoint 重写自 [Curve#getPoint],DecoratedTorusKnot4a 与 Curve 基类的协作是整个使用链的根基。Curve 基类 提供了在 getPoint 之上的完整派生采样体系:
| 方法 | 作用 | 内部调用 |
|---|---|---|
.getPoint( t, optionalTarget ) |
按参数 t(线性参数,非弧长)取点 |
子类实现,本曲线已重写 |
.getPointAt( u, optionalTarget ) |
按弧长比例 u 取点(需先建立弧长表) |
间接调用 getPoint |
.getPoints( divisions ) |
按 divisions 等分 t 返回点列 |
循环调用 getPoint( d / divisions ) |
.getSpacedPoints( divisions ) |
按弧长等距返回点列 | 循环调用 getPointAt |
.computeFrenetFrenet... |
计算 Frenet 标架(切线/法线/副法线) | 供 TubeGeometry 内部使用 |
也就是说,你只需要在子类里精确实现 getPoint 这一个纯数学方法,three.js 就会自动“赠送”弧长参数化、等距采样、长度计算等整套工具。例如:
const curve = new DecoratedTorusKnot4a();
const samples = curve.getPoints( 200 ); // 等 t 采样 201 个点,适合描线
const spaced = curve.getSpacedPoints( 200 ); // 弧长等距采样,适合粒子流、动画轨迹
const pos = new THREE.Vector3();
curve.getPoint( 0.5, pos ); // 取中点,复用外部向量避免 GC
实战:用 TubeGeometry 把曲线变成管道模型
DecoratedTorusKnot4a 最常见的用法是作为“路径”交给 TubeGeometry(管状几何体)沿曲线扫掠出三维体。TubeGeometry 的构造签名(见 TubeGeometry.js#L45):
new TubeGeometry( path, tubularSegments = 64, radius = 1, radialSegments = 8, closed = false )
一个最小可运行的组合示例:
import * as THREE from 'three';
import { DecoratedTorusKnot4a } from 'three/addons/curves/CurveExtras.js';
const scene = new THREE.Scene();
// 1. 构造曲线(scale 默认 40,包络约 ±80 场景单位)
const curve = new DecoratedTorusKnot4a();
// 2. 沿曲线挤管:因为曲线闭合,closed 建议置 true 以生成无缝管道
const tubeGeometry = new THREE.TubeGeometry(
curve, // 路径曲线
200, // tubularSegments:沿路径的采样段数,越大越平滑
2, // radius:管道半径
8, // radialSegments:截面圆细分数量
true // closed:曲线是闭合的
);
const mesh = new THREE.Mesh(
tubeGeometry,
new THREE.MeshLambertMaterial( { color: 0xff00ff } )
);
scene.add( mesh );
TubeGeometry 内部会调用 path.computeFrenetFrames() 获得路径上每个采样点的切线/法线/副法线标架,再逐点铺设圆形截面(见 TubeGeometry.js#L66-L72),所以曲线在数学上越光滑(DecoratedTorusKnot4a 各阶连续),挤出的管道表面越顺滑。
参考官方示例与可调参数
这条曲线正是官方示例 webgl_geometry_extrude_splines.html(spline extrusion,样条挤出)的备选路径之一。示例把全部曲线实例放入一个字典:
const splines = {
...
DecoratedTorusKnot4a: new Curves.DecoratedTorusKnot4a(),
DecoratedTorusKnot4b: new Curves.DecoratedTorusKnot4b(),
DecoratedTorusKnot5a: new Curves.DecoratedTorusKnot5a(),
DecoratedTorusKnot5c: new Curves.DecoratedTorusKnot5c(),
...
};
示例通过 lil-gui 暴露了四组与本曲线直接相关的实时调节参数,可作为调试此类曲线时的参考经验值:
| GUI 参数 | 取值范围(步长) | 作用 | 触发行为 |
|---|---|---|---|
spline |
全部曲线名 | 切换路径曲线 | 重建 TubeGeometry |
scale |
2 ~ 10(步进 2) | 整体网格缩放(mesh.scale) |
调用 setScale() |
extrusionSegments |
50 ~ 500(步进 50) | 对应 tubularSegments,沿路径分段数 |
重建几何体 |
radiusSegments |
2 ~ 12(步进 1) | 截面细分数 | 重建几何体 |
closed |
布尔 | 管道是否闭合 | 重建几何体 |
此外该示例还演示了把一条曲线同时当作相机飞行轨迹:一个内嵌的 splineCamera(透视相机)沿管道路径运动,CameraHelper 实时显示其视锥。这说明 DecoratedTorusKnot4a 这类参数曲线不仅能喂给 TubeGeometry 建模,还能直接充当过场动画、巡游相机的路径源,只需用 .getPointAt( t ) 或基类的弧长采样接口即可让相机匀速沿曲线行进。
同族曲线的快速对比与选型
在 CurveExtras.js 的同一段源码中,紧挨着 DecoratedTorusKnot4a 定义了 DecoratedTorusKnot4b 与 DecoratedTorusKnot5a/5c,它们的骨架写法完全一致(构造默认 scale = 40、getPoint 内 t *= Math.PI * 2),区别只在调制系数:
| 曲线 | 平面回转 | 半径/装饰项 | 纵向振荡 | 特征 |
|---|---|---|---|---|
DecoratedTorusKnot4a |
2θ |
1 + 0.6(cos5θ + 0.75cos10θ) |
0.35sin5θ |
装饰最“蓬松”,含 10 倍高频瓣 |
DecoratedTorusKnot4b |
2θ |
1 + 0.45cos3θ + 0.4cos9θ |
0.2sin9θ |
装饰更收敛,高频瓣更细 |
DecoratedTorusKnot5a |
4θ |
1 + 0.5(cos5θ + 0.4cos20θ) |
0.35sin15θ |
平面回转加倍,形态更密 |
可见“数字 + 字母”后缀只是该子族的命名序号(区分同一骨架的不同装饰频率组合),使用时可按视觉形态直接选择,接口与用法完全一致,随时可在同一段渲染代码里互换验证。
使用注意事项小结
t必须限定在[0, 1]:getPoint的入参代表曲线上的插值位置,超出范围虽不会崩溃,但采样点会沿周期曲线继续绕行,容易产生意外的自交路径,建议按文档约定在区间内取值。scale与TubeGeometry的半径互相独立:曲线scale决定路径整体大小,TubeGeometry的radius决定管道粗细,二者配合时注意换算视觉比例(示例中 GUI 的scale是对mesh.scale做二次整体缩放,与曲线的scale是两个不同层级)。- 闭合曲线的管道:
DecoratedTorusKnot4a本身是闭合光滑曲线,但TubeGeometry的closed参数默认是false;想要无缝管道记得显式传true,否则首尾会出现开口。 - 对象复用避免内存抖动:若在动画循环里高频调用
getPoint,务必传入复用的optionalTarget向量,源码中该向量会被直接set覆写并返回,可有效避免每帧产生新Vector3对象。
参考源码路径
- 官方文档:docs/pages/DecoratedTorusKnot4a.html.md
- 曲线实现与导出:examples/jsm/curves/CurveExtras.js#L479-L527
- 曲线基类(采样体系与 Frenet 标架):Curve 基类
- 管状几何体(沿曲线挤出):TubeGeometry
- 官方示例(GUI 切换曲线与挤管渲染):webgl_geometry_extrude_splines.html
- 示例截图:examples/screenshots/webgl_geometry_extrude_splines.jpg
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
