发布时间:2026/9/7 18:40:35
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/9/7 18:40:35

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

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

2026/9/7 19:40:46

MySQL事务提交失败处理实战:回滚、重试与幂等设计

在开发中遇到“MySQL事务提交失败”这类问题,几乎是每个后端工程师都绕不过去的坎。尤其是涉及订单、库存、支付这类核心链路时,一旦事务在提交阶段爆出异常,很多人第一反应就是“回滚不就完了”,但真正落地时却发现,情…

2026/9/7 19:40:46

数据库课程为何从C++热身开始?CMU 15-445 Project #0解析

1. 为什么一门数据库课程要把第一个项目做成C热身很多人第一次看到CMU 15-445的Project #0时都有同一个疑问:我明明是来学数据库的,为什么第一个任务不是写SQL解析器,也不是实现存储引擎,而是先做一堆C练习?这个问题想…

2026/9/7 19:35:46

ETL设计实战:从分层架构到增量同步与性能优化

做数据集成这行越久,我越觉得 ETL 不只是一门“技术活”,它更像是在给企业数据大厦浇筑地基。不管是传统数仓还是现在的数据湖仓一体,数据要能真正用起来,第一步永远绕不开抽取、转换、加载这几个动作。很多刚入门的朋友会问我&am…

2026/9/7 0:47:43

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/7 0:14:19

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/7 0:14:17

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/7 0:03:36

基于YOLOv8和PyQt5的麦穗稻穗检测识别系统设计与实现

这次我们来看一个把目标检测算法和桌面端工具结合得很典型的项目:基于 YOLOv8 PyQt5 的麦穗稻穗检测识别系统。这个项目本身不是新概念,但它的价值在于落地形态很完整。YOLOv8 负责核心的麦穗稻穗目标检测,PyQt5 负责提供可视化的桌面交互界…

2026/9/7 0:03:36

UL 1642锂电池安全标准全解析:测试项目、认证流程与避坑指南

简介:UL 1642是锂电池安全领域的重要规范,本中文版资源适合锂电池制造商、检测机构工程师及产品认证相关人员阅读,用于理解电池在设计与制造层面的安全要求、测试方法与合规要点。资源共1个PDF文件,压缩包大小834KB,便…

2026/9/7 0:03:36

BS EN 13814-1-2019游乐设施安全标准:设计与制造核心要点解析

简介:BS EN 13814-1:2019是英国采纳欧洲标准EN 13814-1:2019的正式版本,由BSI标准出版,重点规定游乐设施和游乐设备在设计与制造环节的安全准则,与BS EN 13814-2:2019、BS EN 13814-3:2019共同取代旧版BS EN 13814:2004。该标准面…

2026/9/7 16:23:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/6 19:33:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/6 10:19:40

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…