three.js CubicBezierCurve 深度解析:2D 三次贝塞尔曲线的 API、采样实现与弧长参数化

发布时间:2026/10/25 3:08:26

three.js CubicBezierCurve 深度解析:2D 三次贝塞尔曲线的 API、采样实现与弧长参数化 three.js CubicBezierCurve 深度解析2D 三次贝塞尔曲线的 API、采样实现与弧长参数化【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js本篇技术指南围绕 three.js 官方 API 文档 CubicBezierCurve 展开系统讲解这个 2D 三次贝塞尔曲线类的构造方式、核心属性与getPoint插值原理并结合 src/extras/curves/CubicBezierCurve.js 源码与 QUnit 测试用例 验证其采样行为。读完后你将掌握如何用四个向量定义一条三次贝塞尔曲线、如何用getPoints将其渲染为THREE.Line以及如何利用父类Curve的弧长机制做等距采样getPointAt/getSpacedPoints。类定位Curve 体系中的 2D 三次贝塞尔曲线CubicBezierCurve是 three.jsextras模块下曲线路径curves家族的一员官方文档将其定义为 A curve representing a 2D Cubic Bezier curve即一条由四个点确定的2D三次贝塞尔曲线。它直接继承自抽象基类 CurveCurve (src/extras/core/Curve.js) └── CubicBezierCurve (src/extras/curves/CubicBezierCurve.js)同一个 src/extras/curves 目录下还有一组相关实现理解它们的分工有助于选对类类维度控制点数量说明QuadraticBezierCurve2D3二次贝塞尔曲线CubicBezierCurve2D4本文主角三次贝塞尔曲线QuadraticBezierCurve33D3二次的 3D 变体CubicBezierCurve33D4三次贝塞尔曲线的 3D 版本LineCurve/LineCurve32D / 3D2直线段SplineCurve/CatmullRomCurve3/EllipseCurve/ArcCurve——其他解析曲线三次贝塞尔曲线由起点v0、终点v3以及两个控制点v1、v2完全确定。控制点决定了曲线的走向曲线始终被包裹在由四个控制点构成的凸包内并在t 0与t 1处分别与v0、v3相切。构造函数与属性new CubicBezierCurve( v0, v1, v2, v3 )构造函数接收四个Vector2参数v0起点start pointv1第一个控制点first control pointv2第二个控制点second control pointv3终点end point。对照 CubicBezierCurve.js#L37-L80 的源码构造函数有一个值得注意的细节四个参数都有默认值new Vector2()。也就是说new CubicBezierCurve()不传任何参数也是合法的会得到一条从原点到原点、退化为一点的曲线这在单元测试中直接体现了type与isCubicBezierCurve属性可在空构造下断言class CubicBezierCurve extends Curve { constructor( v0 new Vector2(), v1 new Vector2(), v2 new Vector2(), v3 new Vector2() ) { super(); this.isCubicBezierCurve true; this.type CubicBezierCurve; this.v0 v0; this.v1 v1; this.v2 v2; this.v3 v3; } // ... }构造完成后实例上会有以下属性.isCubicBezierCurve : boolean (readonly)类型测试标志默认true。three.js 大量使用isXxx标志做鸭子类型判断方便在不依赖instanceof的场景下识别对象类型。.v0 / .v1 / .v2 / .v3 : Vector2分别对应起点、第一控制点、第二控制点与终点。注意源码中这四个属性直接引用传入的向量对象而非拷贝因此持有这些向量的其他代码如果后续修改它们会直接影响曲线形状若需要独立副本请传入前自行clone()。此外源码还定义了this.type CubicBezierCurve该字段与父类Curve的序列化机制配合用于 JSON 反序列化时识别具体曲线类型下文toJSON一节会说明。getPointBernstein 多项式插值的核心.getPoint( t : number, optionalTarget : Vector2 ) : Vector2这是CubicBezierCurve唯一覆写自基类的抽象方法Overrides:Curve#getPoint返回参数t对应的曲线位置。t插值因子表示曲线上的位置必须位于[0, 1]区间optionalTarget可选的目标向量结果会写入该向量并返回便于在循环中复用临时向量、避免 GC 压力。源码实现CubicBezierCurve.js#L89-L102非常精炼getPoint( t, optionalTarget new Vector2() ) { const point optionalTarget; const v0 this.v0, v1 this.v1, v2 this.v2, v3 this.v3; point.set( CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) ); return point; }可以看到它对 x、y 两个分量分别调用 Interpolations.js 中导出的标量函数CubicBezier再把结果写回一个Vector2。而CubicBezier的实现就是标准的三次 Bernstein 多项式// src/extras/core/Interpolations.js function CubicBezierP0( t, p ) { const k 1 - t; return k * k * k * p; } function CubicBezierP1( t, p ) { const k 1 - t; return 3 * k * k * t * p; } function CubicBezierP2( t, p ) { return 3 * ( 1 - t ) * t * t * p; } function CubicBezierP3( t, p ) { return t * t * t * p; } function CubicBezier( t, p0, p1, p2, p3 ) { return CubicBezierP0( t, p0 ) CubicBezierP1( t, p1 ) CubicBezierP2( t, p2 ) CubicBezierP3( t, p3 ); }合并起来即经典公式P(t) (1-t)³·v0 3(1-t)²t·v1 3(1-t)t²·v2 t³·v3四个基函数在t 0和t 1处分别只有一项为 1因此曲线必然精确地通过v0与v3中间项的权重则平滑地把曲线拉向两个控制点。用官方示例把曲线渲染成一条线官方文档给出的最小可用示例如下完整保留了从曲线到可渲染对象的链路const curve new THREE.CubicBezierCurve( new THREE.Vector2( - 0, 0 ), new THREE.Vector2( - 5, 15 ), new THREE.Vector2( 20, 15 ), new THREE.Vector2( 10, 0 ) ); const points curve.getPoints( 50 ); const geometry new THREE.BufferGeometry().setFromPoints( points ); const material new THREE.LineBasicMaterial( { color: 0xff0000 } ); // Create the final object to add to the scene const curveObject new THREE.Line( geometry, material );逐行说明getPoints( 50 )是继承自Curve的方法它以d / divisionsd 0 … divisions为参数循环调用getPoint因此返回divisions 1个点51 个。注意getPoints按参数 t 均匀采样由于贝塞尔曲线参数化并非匀速生成的线段在曲线上疏密并不均匀BufferGeometry.setFromPoints直接以Vector2/Vector3数组填充position属性LineBasicMaterialTHREE.Line是最轻量的曲线呈现方式。如果想要等弧长的采样例如沿线匀速运动的粒子、等间距的装饰点应改用getSpacedPoints( divisions )它内部通过getPointAt( u )实现机制见下一节。弧长参数化getPointAt 与 getUtoTmappingCurve基类解决的核心问题是参数 t 均匀 ≠ 弧长均匀。以官方测试中的曲线v0(-10,0) v1(-5,15) v2(20,15) v3(10,0)为例getPoint( 0.5 )给出的中点并不是几何意义上的曲线中点。three.js 的应对策略是采样 累加 二分查找三件套getLengths( divisions )Curve.js#L153-L184把曲线按divisions段切分累加相邻采样点的欧氏距离生成累积弧长数组cacheArcLengths受arcLengthDivisions默认200与needsUpdate标志控制缓存复用——修改了控制点之后必须调用updateArcLengths()或把needsUpdate置为true否则拿到的仍是旧缓存getUtoTmapping( u, distance? )Curve.js#L208-L281给定弧长因子u或绝对距离distance在累积弧长数组上做二分查找定位所在区间再用线性内插折算回参数t供getPoint使用getPointAt( u ) / getSpacedPoints( divisions ) / getTangentAt( u )都是先经getUtoTmapping折算再委托给对应的getPoint/getTangent版本。单元测试CubicBezierCurve.tests.js给出了这组 API 的精确预期值可直接作为数值回归基准// 曲线总长getLength() ≈ 36.64630888504102 // 弧长中点getPointAt( 0.5 ) ≈ ( 3.4718554735926617, 11.07899406116314 ) // 与参数中点 getPoint( 0.5 ) ( 5.625, 11.25 ) 明显不同 —— 这正是弧长重映射的意义 // getUtoTmapping( 0.5, 1 ) ≈ 0.02130029182257093 // 表示沿曲线走 1 个单位弧长对应的参数位置getSpacedPoints()默认 5 段在该测试曲线上的六个点为(-10, 0)→(-6.168, 6.170)→(-0.059, 10.124)→(7.124, 11.155)→(12.302, 6.809)→(10, 0)相邻点间弧长近似相等可作为等距撒点实现的验收标准。参数校验之外的实用方法copy、toJSON、fromJSON官方 API 页面的 Methods 一节只列出了getPoint但源码中CubicBezierCurve还实现了三个序列化与复制相关的方法在实际工程尤其是与 three.js 的ObjectLoader/场景 JSON 生态配合时会用到// 复制逐个拷贝四个向量 copy( source ) { super.copy( source ); this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); this.v3.copy( source.v3 ); return this; } // 序列化在 Curve 的 metadata 基础上追加四个向量数组 toJSON() { const data super.toJSON(); data.v0 this.v0.toArray(); data.v1 this.v1.toArray(); data.v2 this.v2.toArray(); data.v3 this.v3.toArray(); return data; } // 反序列化按数组恢复四个向量 fromJSON( json ) { super.fromJSON( json ); this.v0.fromArray( json.v0 ); this.v1.fromArray( json.v1 ); this.v2.fromArray( json.v2 ); this.v3.fromArray( json.v3 ); return this; }父类Curve.prototype.toJSON会输出metadata含type: Curve与生成器信息、arcLengthDivisions与子类覆写的type CubicBezierCurve反序列化时type字段正是用来找回具体子类的依据clone()继承自Curve实现为new this.constructor().copy( this )因此curve.clone()会得到一条独立的新曲线。单元测试中的完整数值行为test/unit/src/extras/curves/CubicBezierCurve.tests.js 以v0(-10,0) v1(-5,15) v2(20,15) v3(10,0)这条曲线为基准覆盖了以下断言可作为你本地验证实现的标准答案参数采样getPoints(4)[ (-10, 0), (-3.359375, 8.4375), (5.625, 11.25), (11.796875, 8.4375), (10, 0) ]该测试还验证了对称性把四个控制点逆序构造new CubicBezierCurve( v3, v2, v1, v0 )采样结果恰好是原曲线采样结果的逆序——这是三次贝塞尔曲线的基本几何性质也是快速手写反向曲线的技巧。切线getTangent / getTangentAtgetTangent使用 ±0.0001 的差分近似见 Curve.js#L293-L313测试给出的getTangent(0.5)恰为(1, 0)与曲线在参数中点处切向水平的直觉一致getTangentAt则是先做弧长重映射再差分因此中间采样值与getTangent不同如u 0.25处分别为(0.839, 0.544)与(0.779, 0.626)。继承性断言object instanceof Curve trueobject.type CubicBezierCurveobject.isCubicBezierCurve true与文档Properties一节的描述完全对应。使用建议与注意事项区分getPoint与getPointAt动画中若希望对象匀速沿曲线运动请基于getPointAt或getUtoTmappinggetPoint驱动而不是直接用线性递增的t修改控制点后刷新弧长缓存v0~v3是引用属性改动后记得curve.needsUpdate true或调用curve.updateArcLengths()否则getPointAt系列仍基于旧弧长表2D 还是 3DCubicBezierCurve的点全部是Vector2若曲线要作为TubeGeometry的 spine 等需要 3D Frenet 标架的场景computeFrenetFrames要求 3D 曲线定义应改用同目录的CubicBezierCurve3复用目标向量在每帧循环中调用getPoint( t, tmpVec2 )传入常驻的Vector2可以避免每次调用都新建对象带来的分配开销——这也是optionalTarget参数存在的意义。小结CubicBezierCurve是 three.js 曲线体系中控制点显式指定的 2D 三次贝塞尔曲线实现构造时给定v0/v1/v2/v3四个Vector2getPoint(t)通过 Interpolations.js 中的 Bernstein 基函数对 x、y 分量分别插值父类 Curve 则在其上提供getPoints参数采样、getPointAt/getSpacedPoints弧长采样、getTangent切线与toJSON/fromJSON序列化等通用能力。配合 单元测试 中给出的精确数值你可以完整验证从定义曲线到渲染成 Line再到等弧长驱动动画的整条链路。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/24 13:20:32

CMSIS-DSP源码深度拆解:从FFT优化到工业固件落地实践

做嵌入式这些年,凡是跟电机控制、音频处理、振动分析、电能质量检测沾边的活,基本都绕不开 CMSIS-DSP 。早几年我也只把它当黑盒调用——FOC电流环里查一下PID,跑FFT时填一下结构体,完事。直到有一次要在没有硬件FPU的Cortex-M0…

2026/10/25 3:07:59

传统开发者转型AI应用开发:RAG、Agent与工程化闭环

这两年,只要在技术社区里刷一会儿,就会反复撞见类似“转到人工智能是我这辈子做过的最正确的决定”的标题。有人从 Java 后端转去做大模型应用,有人从算法岗转到应用工程,也有人从运维、测试转到 AI 平台方向。这类分享读多了容易…

2026/10/25 3:07:59

Odoo17后台仪表盘源码:二开最后一公里看板方案

简介:Odoo17 自定义仪表盘开发示例源码面向具备一定 Python 与前端基础的 Odoo 二次开发者,用于解决后台首页仪表盘从零搭建时模块结构不清、图表数据接入无从下手的问题。压缩包共 25 个文件、约 16KB,其中 9 个 js 脚本负责图表渲染与交互逻…

2026/10/25 3:07:59

生成式AI落地核心:基础模型、提示工程与本地化部署

简介:本资源是一份面向AI初学者与技术从业者的生成式人工智能入门指南,聚焦ChatGPT背后的大语言模型原理、发展脉络与商业落地路径。内容系统梳理了人工智能从机器学习、深度学习到生成式AI的演进里程碑,深入解析基础模型如何破解语言复杂性、…

2026/10/25 3:07:59

claude-mem记忆层设计:从抽取到检索的工程实践

1. 从"claude-mem"这个名字说起:它到底想解决什么第一次看到claude-mem这个命名,我的直觉是:这是一个围绕 Claude 生态做"记忆层"的项目。名字拆开看很直白——claude指向模型侧,mem是 memory 的缩写。合在一…

2026/10/25 3:02:59

解码多Agent协作:构建像虚拟团队一样工作的agency-agents

去年我在做内部自动化工具的时候,第一次认真思考"agency-agents"该怎么落地。当时的需求并不复杂:给出一份杂乱的需求文档,让大模型把它拆成可执行的开发任务,再针对每个任务写出实现说明,最后整体自检一遍。…

2026/10/25 0:02:22

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/25 0:02:23

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/25 0:02:23

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/25 0:02:22

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/25 0:02:23

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/25 0:02:23

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑