ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Three.js 深度解析 CubicBezierCurve3:三次贝塞尔曲线的 API、源码实现与曲线族方法

Three.js 深度解析 CubicBezierCurve3:三次贝塞尔曲线的 API、源码实现与曲线族方法 Three.js 深度解析 CubicBezierCurve3三次贝塞尔曲线的 API、源码实现与曲线族方法【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js本文以官方文档CubicBezierCurve3页面为骨架系统讲解 Three.js 中三维三次贝塞尔曲线类的构造函数、全部属性与方法并结合 src/extras/curves/CubicBezierCurve3.js 源码与 src/extras/core/Interpolations.js 中的 Bernstein 基函数实现剖析getPoint()的逐轴插值原理同时覆盖从父类 Curve 继承而来的弧长映射、切线、Frenet 标架等关键能力。读完后你既能直接使用该类生成任意三维曲线点也能理解其采样、序列化与在TubeGeometry等几何体中的底层调用关系。一、类定位与继承关系CubicBezierCurve3是一条三维三次贝塞尔曲线A curve representing a 3D Cubic Bezier curve继承自抽象基类CurveCurve → CubicBezierCurve3从源码结构看类定义位于 src/extras/curves/CubicBezierCurve3.js并经由 src/extras/curves/Curves.js 统一导出export { CubicBezierCurve3 } from ./CubicBezierCurve3.js;Curve是所有解析曲线的抽象基类提供插值、弧长、切线等通用能力而子类只需实现getPoint()。这一设计使得CubicBezierCurve3与QuadraticBezierCurve3、CatmullRomCurve3、LineCurve3等曲线可以互换地传入TubeGeometry、ExtrudeGeometry等任何接受Curve的参数位置。二、构造函数官方文档定义的构造函数签名new CubicBezierCurve3( v0 : Vector3, v1 : Vector3, v2 : Vector3, v3 : Vector3 )四个参数含义完整继承自文档并对照源码注释参数类型含义v0Vector3起点The start pointv1Vector3第一个控制点The first control pointv2Vector3第二个控制点The second control pointv3Vector3终点The end point一个值得注意的实现细节源码中四个参数都带有默认值允许零参实例化// src/extras/curves/CubicBezierCurve3.js constructor( v0 new Vector3(), v1 new Vector3(), v2 new Vector3(), v3 new Vector3() ) { super(); this.isCubicBezierCurve3 true; this.type CubicBezierCurve3; this.v0 v0; this.v1 v1; this.v2 v2; this.v3 v3; }这意味着new CubicBezierCurve3()如单元测试中的Extending、Instancing用例所示是合法的会得到一条退化的零长度曲线此时应通过赋值curve.v0/v1/v2/v3或copy()补全控制点。三、属性3.1 .isCubicBezierCurve3 : booleanreadonly类型测试标志位默认true。常用于instanceof之外的快速类型判断if ( object.isCubicBezierCurve3 ) { /* ... */ }3.2 控制点属性 .v0 / .v1 / .v2 / .v3 : Vector3四个Vector3引用分别对应起点、控制点一、控制点二与终点。直接引用赋值意味着修改共享引用会反向影响曲线——若传入的Vector3被多处引用原地修改vector.x 1会同步改变曲线形状修改控制点之后若依赖弧长缓存的方法getPointAt、getSpacedPoints等仍需精确结果建议设置curve.needsUpdate true或调用curve.updateArcLengths()这一点由父类Curve的needsUpdate机制保障见下节。另外构造函数中还设置了this.type CubicBezierCurve3该字段由父类Curve引入用于序列化/反序列化场景中的类型识别ObjectLoader等按type还原具体类。四、核心方法 .getPoint( t, optionalTarget )4.1 文档签名.getPoint( t : number, optionalTarget : Vector3 ) : Vector3t插值因子表示曲线上的位置取值范围必须为[0,1]optionalTarget可选的目标向量结果将写入该向量并返回便于在渲染循环中复用、避免每帧产生垃圾对象返回曲线上的位置Vector3OverridesCurve#getPoint。4.2 源码实现逐轴调用 Bernstein 多项式// src/extras/curves/CubicBezierCurve3.js getPoint( t, optionalTarget new Vector3() ) { 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 ), CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) ); return point; }关键点三维曲线被拆解为三条独立的标量三次贝塞尔曲线x、y、z 分量各自独立求值后组合。其中CubicBezier来自 src/extras/core/Interpolations.jsfunction 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 ); }这正是经典的 Bernstein 基函数展开等价于数学形式B(t) (1-t)³·P0 3(1-t)²·t·P1 3(1-t)·t²·P2 t³·P3, t ∈ [0,1]因此几何性质一目了然t 0必为v0t 1必为v3v1、v2不在线上的中间段只通过权重牵引曲线走向。Interpolations.js的模块注释同时表明公式来源为维基百科的 Bézier curve 条目此处不作为外部链接给出仅作出处说明。4.3 数值验证来自单元测试test/unit/src/extras/curves/CubicBezierCurve3.tests.js 中构造了如下测试曲线curve new CubicBezierCurve3( new Vector3( - 10, 0, 2 ), new Vector3( - 5, 15, 4 ), new Vector3( 20, 15, - 5 ), new Vector3( 10, 0, 10 ) );Simple curve用例验证了均匀采样 4 段5 个点的结果可用于手工核对实现正确性t 0.00 → ( -10, 0, 2 ) t 0.25 → ( -3.359375, 8.4375, 1.984375 ) t 0.50 → ( 5.625, 11.25, 1.125 ) t 0.75 → ( 11.796875, 8.4375, 2.703125 ) t 1.00 → ( 10, 0, 10 )测试还验证了一个重要的对称性质将四个控制点倒序构造新曲线new CubicBezierCurve3( curve.v3, curve.v2, curve.v1, curve.v0 )其getPoints()结果恰为原曲线点集的逆序。这一性质可用于曲线反转reversal场景。五、从 Curve 基类继承的关键能力CubicBezierCurve3除覆写getPoint()外其余能力全部继承自 src/extras/core/Curve.js。以下方法对该类同样可用且均有单元测试覆盖。5.1 弧长系统getLengths / getLength / getUtoTmappingCurve的构造函数定义了三个弧长相关字段this.arcLengthDivisions 200; // 弧长缓存的分段数默认 200 this.needsUpdate false; // 曲线参数变化后置 true 以失效缓存 this.cacheArcLengths null; // 弧长缓存getLengths( divisions this.arcLengthDivisions )以getPoint()采样并累加相邻距离得到累计弧长数组结果缓存在cacheArcLengthsneedsUpdate为false且分段数匹配时直接复用getLength()返回弧长数组末元素getUtoTmapping( u, distance )将按弧长均匀的因子u或给定距离反解为按参数均匀的t内部采用二分查找 段内线性插值实现。测试用例getUtoTmapping验证了边界与中间值getUtoTmapping( 0, 0 )返回0getUtoTmapping( 0, curve.getLength() )返回1中间某点getUtoTmapping( 0.5, 1 )期望值约为0.021163245321323316。同一测试中曲线总长期望为39.58103024989427累计分段[0, 10.737…, 20.190…, 27.154…, 38.453…]与getLengths(4)的输出吻合。实践含义若曲线很长或曲率剧烈把arcLengthDivisions调大可以提升getPointAt/getSpacedPoints的精度修改控制点后记得触发弧长缓存更新needsUpdate true或updateArcLengths()。5.2 等距采样getPointAt / getPoints / getSpacedPointsgetPointAt( u, optionalTarget )先经getUtoTmapping( u )换算再调用getPoint( t )因此u与弧长成正比采样沿曲线等距分布getPoints( divisions 5 )按参数t均匀采样返回divisions 1个点getSpacedPoints( divisions 5 )按弧长均匀采样同样返回divisions 1个点。两者区别在于getPoints点间距随速度参数化速率变化getSpacedPoints间距恒定。测试用例getPointAt断言了u 0 / 0.3 / 0.5 / 1的具体坐标getSpacedPoints断言了默认 5 分段下 6 个点首末为(-10,0,2)与(10,0,10)。5.3 切线getTangent / getTangentAtgetTangent( t )采用有限差分取t ± 0.0001并在[0,1]内截断两点求差归一化// src/extras/core/Curve.js getTangent( t, optionalTarget ) { const delta 0.0001; // ... t1 max(0, t - delta), t2 min(1, t delta) const pt1 this.getPoint( t1 ); const pt2 this.getPoint( t2 ); // tangent normalize( pt2 - pt1 ) }getTangentAt( u )则先做u → t弧长换算再取切线保证等距语义。测试用例getTangent/getTangentAt对t 0, 0.25, 0.5, 0.75, 1五个采样点断言了精确的切线分量例如起点切线约为(0.3138715439944244, 0.9411440474105875, 0.12542940601858074)。5.4 Frenet 标架computeFrenetFrames( segments, closed )该 3D 专属方法生成切线tangents、法线normals、副法线binormals三组向量数组实现参考了 Indiana 大学技术报告 TR425 的算法见 src/extras/core/Curve.js 中注释先逐段求切线再选一个与首切线正交的初始法线取切线 xyz 绝对值最小的分量对应的坐标轴方向构造随后沿曲线用旋转矩阵做平行输运parallel transportclosed true时对闭环做额外旋转载荷以消除首尾法线不连续的扭曲。此方法是TubeGeometry与ExtrudeGeometry沿 3D 曲线生成截面的直接依赖测试用例computeFrenetFrames对curve.computeFrenetFrames( 2, false )的三组向量逐分量断言可作为复现基准。5.5 序列化与拷贝copy / clone / toJSON / fromJSONCubicBezierCurve3覆写了copy、toJSON、fromJSON完整继承文档与源码// src/extras/curves/CubicBezierCurve3.js copy( source ) { super.copy( source ); // 父类拷贝 arcLengthDivisions this.v0.copy( source.v0 ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); this.v3.copy( source.v3 ); return this; } toJSON() { const data super.toJSON(); // 含 metadata(version 4.7, type Curve) arcLengthDivisions 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; }要点toJSON输出的type为CubicBezierCurve3metadata.version为4.7四个控制点以toArray()的数组形式[x,y,z]序列化fromJSON使用fromArray将数组还原回向量返回this以支持链式调用clone()继承自Curve内部通过new this.constructor().copy( this )实现因此克隆结果自动是CubicBezierCurve3类型。序列化能力使曲线可以进入场景 JSON 体系ObjectLoader按type字段还原。但需要注意一个边界从 src/geometries/TubeGeometry.js 的源码注释可以推断内置曲线如QuadraticBezierCurve3才能被几何体序列化完整还原用户自定义曲线或CurvePath组合不会被反序列化——涉及CubicBezierCurve3作为path时同理需确认走的是内置曲线反序列化路径。六、单元测试覆盖清单test/unit/src/extras/curves/CubicBezierCurve3.tests.js 是验证本文所有结论的权威依据覆盖如下断言用例验证内容Extending实例instanceof Curve为真Instancing可零参实例化typeobject.type CubicBezierCurve3isCubicBezierCurve3标志位为trueSimple curvegetPoints(4)精确坐标 控制点倒序曲线点集逆序的对称性getLength/getLengths总长 ≈39.58103024989427分段累计弧长精确匹配getPointAtu 0/0.3/0.5/1处等弧长采样坐标getTangent/getTangentAt5 个采样点切线分量getUtoTmapping端点映射为0/1中间值 ≈0.021163245321323316getSpacedPoints默认 5 分段的等距采样点computeFrenetFramessegments 2时 tangent/normal/binormal 逐分量断言七、典型应用场景管状/挤出几何TubeGeometry的第一个参数即为path : Curve默认值为QuadraticBezierCurve3见 src/geometries/TubeGeometry.js 注释把CubicBezierCurve3传进去即可得到沿三次曲线扫掠的管道ExtrudeGeometry的extrudePath同理。两者内部均调用computeFrenetFrames求截面方向。运动轨迹与路径动画利用getPointAt( u )可以按匀速弧长参数沿曲线移动物体比直接用getPoint更符合物理直觉getTangentAt可同时给出朝向。曲线组合多个CubicBezierCurve3可加入CurvePath拼出长路径。仓库的 manual/resources/threejs-primitives.js 中即出现shape.add( new THREE.CubicBezierCurve3( ...points.slice( i, i 4 ) ) )的用法展示了每 4 个连续点构造一段三次贝塞尔并追加到路径/形状上的惯用写法。平滑样条逼近由于三次贝塞尔可由任意两端的切向量确定v1 v0 m0/3v2 v3 − m1/3的思路常用作把带切线信息的点序列转成 C1 连续曲线的中间表示此点属于常见数学实践仓库源码未直接给出该转换函数此处为方法提示而非仓库事实。八、完整示例生成曲线、均匀采样并挤出管道以下示例综合了本文涉及的 API导入路径与仓库导出结构一致CubicBezierCurve3由 src/extras/curves/Curves.js 导出import * as THREE from three; // 1. 构造三维三次贝塞尔曲线四个 Vector3起点、控制点 x2、终点 const curve new THREE.CubicBezierCurve3( new THREE.Vector3( - 10, 0, 2 ), new THREE.Vector3( - 5, 15, 4 ), new THREE.Vector3( 20, 15, - 5 ), new THREE.Vector3( 10, 0, 10 ) ); // 2. 类型判断 console.log( curve.isCubicBezierCurve3 ); // true console.log( curve.type ); // CubicBezierCurve3 // 3. 参数采样 vs 等弧长采样 const pParam curve.getPoint( 0.5, new THREE.Vector3() ); // t 均匀 const pArc curve.getPointAt( 0.5, new THREE.Vector3() ); // 弧长均匀u→t 换算 // 4. 弧长信息 const totalLength curve.getLength(); // 测试基准 ≈ 39.58103024989427 const lengths curve.getLengths( 4 ); // 累计弧长5 个元素 // 5. 切线可用于物体朝向 const tangent curve.getTangentAt( 0.25, new THREE.Vector3() ); // 6. 等距采样点列divisions 个分段 → divisions 1 个点 const spacedPoints curve.getSpacedPoints( 5 ); // 7. 沿曲线生成管状几何Frenet 标架由 TubeGeometry 内部计算 const tubeGeometry new THREE.TubeGeometry( curve, 64, 0.5, 8, false ); const tube new THREE.Mesh( tubeGeometry, new THREE.MeshStandardMaterial( { color: 0x2196f3 } ) ); scene.add( tube ); // 8. 修改控制点后的缓存更新 curve.v1.set( - 5, 20, 4 ); curve.needsUpdate true; // 或 curve.updateArcLengths()若需持久化可用curve.toJSON()/curve.fromJSON( json )完成序列化往返跨对象拷贝则用curveA.copy( curveB )或curveB.clone()。九、要点小结CubicBezierCurve3是Curve家族的三维三次贝塞尔实现构造函数接收v0/v1/v2/v3四个Vector3均有零向量默认值type固定为CubicBezierCurve3唯一的覆写核心是getPoint( t, optionalTarget )它把三维问题拆成 x/y/z 三条标量 Bernstein 多项式公式实现在 src/extras/core/Interpolations.jst按参数均匀ugetPointAt/getSpacedPoints/getTangentAt按弧长均匀二者经getUtoTmapping的二分查找 段内插值换算弧长缓存受arcLengthDivisions默认 200与needsUpdate控制序列化toJSON/fromJSONmetadata 版本 4.7与copy/clone使该类可无缝接入ObjectLoader与场景 JSON 体系computeFrenetFrames是它与TubeGeometry、ExtrudeGeometry联动的关键桥梁全部数值结论均可在 test/unit/src/extras/curves/CubicBezierCurve3.tests.js 中找到精确断言建议以该测试文件作为回归基准。参考文件src/extras/curves/CubicBezierCurve3.js、src/extras/core/Curve.js、src/extras/core/Interpolations.js、src/extras/curves/Curves.js、test/unit/src/extras/curves/CubicBezierCurve3.tests.js、src/geometries/TubeGeometry.js、src/geometries/ExtrudeGeometry.js、manual/resources/threejs-primitives.js【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表