学习Three.js--曲线(Curve)

0 阅读13分钟

学习Three.js--曲线(Curve)

前置核心说明

Curve 是 Three.js 中所有曲线/直线的基类,定义了曲线的核心行为(如采样顶点、计算长度等)。所有2D/3D曲线均继承自 Curve,核心作用是「通过数学公式生成连续的顶点序列」,再基于这些顶点创建线条模型,实现任意自定义曲线的绘制。

核心规则

  1. 维度分类
    • 2D曲线:基于XY平面(Z=0),继承 THREE.Curve,如 LineCurve/ArcCurve
    • 3D曲线:基于XYZ三维空间,继承 THREE.Curve(部分别名/子类),如 LineCurve3/CatmullRomCurve3
  2. 核心流程
    创建曲线实例 → 采样顶点(getPoints) → 几何体绑定顶点 → 创建线模型 → 添加到场景
  3. 线模型类型(决定曲线渲染方式):
    模型类型核心特点适用场景
    THREE.Line按顶点顺序绘制连续线条开放曲线(如直线、贝塞尔曲线)
    THREE.LineLoop闭合线条(最后一个顶点连接第一个)封闭曲线(如椭圆、圆)
    THREE.LineSegments每两个顶点为一组绘制分段线离散线条(如网格线)
  4. 采样精度getPoints(n)n 是采样点数(非顶点数),n 越大曲线越平滑(推荐50~100,复杂曲线可设200)。

一、2D曲线(XY平面,Z=0)

所有2D曲线均基于XY平面,Z坐标默认0,核心用于绘制平面曲线。

1. LineCurve(2D直线)

核心说明

两点确定的2D直线,是最简单的2D曲线,无曲率。

构造函数参数
// 语法:new THREE.LineCurve(起点向量, 终点向量)
const lineCurve = new THREE.LineCurve(
  new THREE.Vector2(x1, y1), // 必传:起点(Vector2对象)
  new THREE.Vector2(x2, y2)  // 必传:终点(Vector2对象)
);
参数类型说明
v1THREE.Vector2直线起点(XY坐标)
v2THREE.Vector2直线终点(XY坐标)
使用示例
// 1. 创建2D直线(从(0,0)到(100, 50))
const lineCurve = new THREE.LineCurve(
  new THREE.Vector2(0, 0),
  new THREE.Vector2(100, 50)
);

// 2. 采样顶点(50个点,直线足够平滑)
const points = lineCurve.getPoints(50);

// 3. 创建几何体并绑定顶点
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints(points);

// 4. 创建线材质
const material = new THREE.LineBasicMaterial({ color: 0xff0000 });

// 5. 创建线模型(开放直线用Line)
const line = new THREE.Line(geometry, material);
scene.add(line);

2. ArcCurve(2D圆弧)

核心说明

基于圆心、半径、起始角/终止角的2D圆弧,可绘制圆、半圆、任意弧度的圆弧。

构造函数参数
// 语法:new THREE.ArcCurve(圆心X, 圆心Y, 半径, 起始角, 终止角, 是否逆时针)
const arcCurve = new THREE.ArcCurve(
  0,        // 必传:圆心X坐标
  0,        // 必传:圆心Y坐标
  50,       // 必传:圆弧半径
  0,        // 必传:起始角(弧度,0=右向X轴)
  Math.PI,  // 必传:终止角(弧度,Math.PI=180°)
  false     // 可选:是否逆时针绘制,默认false(顺时针)
);
参数类型默认值说明
aXNumber圆心X坐标
aYNumber圆心Y坐标
aRadiusNumber圆弧半径
aStartAngleNumber起始角度(弧度,0=X轴正方向,Math.PI/2=Y轴正方向)
aEndAngleNumber终止角度(弧度)
aClockwiseBooleanfalse是否顺时针绘制,true=顺时针,false=逆时针
使用示例(绘制半圆)
// 1. 创建半圆(圆心(0,0),半径50,0~π弧度)
const arcCurve = new THREE.ArcCurve(0, 0, 50, 0, Math.PI);

// 2. 采样顶点
const points = arcCurve.getPoints(50);

// 3. 几何体+材质+模型
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints(points);
const material = new THREE.LineBasicMaterial({ color: 0x00ff00 });
const line = new THREE.Line(geometry, material);
scene.add(line);

3. EllipseCurve(2D椭圆/圆)

核心说明

基于圆心、长半轴、短半轴的2D椭圆,当长半轴=短半轴时即为圆(替代ArcCurve绘制完整圆)。

构造函数参数
// 语法:new THREE.EllipseCurve(圆心X, 圆心Y, 长半轴, 短半轴, 起始角, 终止角, 是否逆时针, 旋转角)
const ellipseCurve = new THREE.EllipseCurve(
  0,          // 必传:圆心X坐标
  0,          // 必传:圆心Y坐标
  100,        // 必传:X轴方向长半轴
  50,         // 必传:Y轴方向短半轴
  0,          // 可选:起始角,默认0
  2 * Math.PI,// 可选:终止角,默认2π(完整椭圆)
  false,      // 可选:是否逆时针,默认false
  0           // 可选:椭圆旋转角(弧度),默认0
);
参数类型默认值说明
aXNumber圆心X坐标
aYNumber圆心Y坐标
xRadiusNumberX轴方向半轴长度(长半轴)
yRadiusNumberY轴方向半轴长度(短半轴)
aStartAngleNumber0起始角度(弧度)
aEndAngleNumber终止角度(弧度,2π=完整椭圆)
aClockwiseBooleanfalse是否顺时针绘制
aRotationNumber0椭圆整体旋转角度(弧度)
使用示例(优化版,用户示例升级)
// 1. 创建椭圆(圆心(0,0),长半轴100,短半轴50,完整椭圆)
const ellipseCurve = new THREE.EllipseCurve(0, 0, 100, 50);

// 2. 采样顶点(50个点,椭圆足够平滑)
const points = ellipseCurve.getPoints(50);

// 3. 创建几何体并绑定顶点
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints(points);

// 4. 线材质(红色,线宽1)
const material = new THREE.LineBasicMaterial({ 
  color: 0xff0000,
  linewidth: 1 // 注意:WebGL中线宽仅部分浏览器支持>1
});

// 5. 创建闭合线模型(椭圆用LineLoop)
const line = new THREE.LineLoop(geometry, material);
scene.add(line);

4. SplineCurve(2D样条曲线)

核心说明

通过多个控制点生成的平滑2D曲线(插值曲线),曲线会穿过所有控制点,比贝塞尔曲线更易控制。

构造函数参数
// 语法:new THREE.SplineCurve(控制点数组)
const splineCurve = new THREE.SplineCurve([
  new THREE.Vector2(x1, y1),
  new THREE.Vector2(x2, y2),
  // ... 更多控制点
]);
参数类型说明
pointsArray<THREE.Vector2>必传:2D控制点数组(至少2个,越多曲线越复杂)
使用示例
// 1. 创建2D样条曲线(4个控制点)
const splineCurve = new THREE.SplineCurve([
  new THREE.Vector2(-100, 0),  // 控制点1
  new THREE.Vector2(-50, 80),  // 控制点2
  new THREE.Vector2(50, -80),  // 控制点3
  new THREE.Vector2(100, 0)    // 控制点4
]);

// 2. 采样顶点(100个点,保证平滑)
const points = splineCurve.getPoints(100);

// 3. 几何体+材质+模型
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints(points);
const material = new THREE.LineBasicMaterial({ color: 0x0000ff });
const line = new THREE.Line(geometry, material);
scene.add(line);

5. QuadraticBezierCurve(2D二次贝塞尔曲线)

核心说明

由「起点+控制点+终点」3个点定义的2D贝塞尔曲线,单曲率,适合简单弯曲。

构造函数参数
// 语法:new THREE.QuadraticBezierCurve(起点, 控制点, 终点)
const quadBezier = new THREE.QuadraticBezierCurve(
  new THREE.Vector2(x1, y1), // 必传:起点
  new THREE.Vector2(x2, y2), // 必传:控制点(决定弯曲方向)
  new THREE.Vector2(x3, y3)  // 必传:终点
);
参数类型说明
v0THREE.Vector2起点
v1THREE.Vector2控制点(核心,决定曲线形状)
v2THREE.Vector2终点
使用示例
// 1. 创建二次贝塞尔曲线
const quadBezier = new THREE.QuadraticBezierCurve(
  new THREE.Vector2(-80, 0),  // 起点
  new THREE.Vector2(0, 80),   // 控制点(向上弯曲)
  new THREE.Vector2(80, 0)    // 终点
);

// 2. 采样顶点
const points = quadBezier.getPoints(80);

// 3. 渲染
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints(points);
const material = new THREE.LineBasicMaterial({ color: 0xffff00 });
const line = new THREE.Line(geometry, material);
scene.add(line);

6. CubicBezierCurve(2D三次贝塞尔曲线)

核心说明

由「起点+控制点1+控制点2+终点」4个点定义的2D贝塞尔曲线,双曲率,适合复杂弯曲(如字体轮廓、路径动画)。

构造函数参数
// 语法:new THREE.CubicBezierCurve(起点, 控制点1, 控制点2, 终点)
const cubicBezier = new THREE.CubicBezierCurve(
  new THREE.Vector2(x1, y1), // 必传:起点
  new THREE.Vector2(x2, y2), // 必传:控制点1
  new THREE.Vector2(x3, y3), // 必传:控制点2
  new THREE.Vector2(x4, y4)  // 必传:终点
);
参数类型说明
v0THREE.Vector2起点
v1THREE.Vector2控制点1(左侧弯曲)
v2THREE.Vector2控制点2(右侧弯曲)
v3THREE.Vector2终点
使用示例
// 1. 创建三次贝塞尔曲线
const cubicBezier = new THREE.CubicBezierCurve(
  new THREE.Vector2(-100, 0), // 起点
  new THREE.Vector2(-50, 100),// 控制点1(向上)
  new THREE.Vector2(50, -100),// 控制点2(向下)
  new THREE.Vector2(100, 0)   // 终点
);

// 2. 采样顶点
const points = cubicBezier.getPoints(100);

// 3. 渲染
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints(points);
const material = new THREE.LineBasicMaterial({ color: 0xff00ff });
const line = new THREE.Line(geometry, material);
scene.add(line);

二、3D曲线(XYZ三维空间)

3D曲线突破XY平面限制,支持XYZ三维坐标,核心用于3D路径(如飞行轨迹、管道模型)。

1. LineCurve3(3D直线)

核心说明

两点确定的3D直线,Z坐标可自定义,是3D最基础的曲线。

构造函数参数
// 语法:new THREE.LineCurve3(起点向量, 终点向量)
const lineCurve3 = new THREE.LineCurve3(
  new THREE.Vector3(x1, y1, z1), // 必传:3D起点
  new THREE.Vector3(x2, y2, z2)  // 必传:3D终点
);
参数类型说明
v1THREE.Vector33D起点(XYZ坐标)
v2THREE.Vector33D终点(XYZ坐标)
使用示例
// 1. 创建3D直线(从(0,0,0)到(100, 50, 80))
const lineCurve3 = new THREE.LineCurve3(
  new THREE.Vector3(0, 0, 0),
  new THREE.Vector3(100, 50, 80)
);

// 2. 采样顶点
const points = lineCurve3.getPoints(50);

// 3. 渲染
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints(points);
const material = new THREE.LineBasicMaterial({ color: 0x00ff00 });
const line = new THREE.Line(geometry, material);
scene.add(line);

2. CatmullRomCurve3(3D样条曲线)

核心说明

Three.js官方推荐的3D样条曲线(替代SplineCurve3),通过多个3D控制点生成平滑曲线,曲线穿过所有控制点,适合3D路径规划。

构造函数参数
// 语法:new THREE.CatmullRomCurve3(控制点数组, 是否闭合, 曲线类型, 张力)
const catmullCurve = new THREE.CatmullRomCurve3(
  [
    new THREE.Vector3(x1, y1, z1),
    new THREE.Vector3(x2, y2, z2),
    // ... 更多控制点
  ],
  false,       // 可选:是否闭合,默认false
  'centripetal',// 可选:曲线类型,默认'centripetal'
  0.5          // 可选:张力(0~1),默认0.5,值越大曲线越平缓
);
参数类型默认值说明
pointsArray<THREE.Vector3>必传:3D控制点数组(至少2个)
closedBooleanfalse是否闭合曲线
typeString'centripetal'曲线类型:'centripetal'(默认,自然)、'chordal'(更紧绷)、'catmullrom'(更平滑)
tensionNumber0.5张力(0=无张力,1=最大张力)
使用示例
// 1. 创建3D样条曲线(4个控制点,空间弯曲)
const catmullCurve = new THREE.CatmullRomCurve3([
  new THREE.Vector3(-100, 0, 0),   // 控制点1
  new THREE.Vector3(-50, 80, 50),  // 控制点2
  new THREE.Vector3(50, -80, 100), // 控制点3
  new THREE.Vector3(100, 0, 50)    // 控制点4
]);

// 2. 采样顶点(100个点,保证3D平滑)
const points = catmullCurve.getPoints(100);

// 3. 渲染
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints(points);
const material = new THREE.LineBasicMaterial({ color: 0x0000ff });
const line = new THREE.Line(geometry, material);
scene.add(line);

3. QuadraticBezierCurve3(3D二次贝塞尔曲线)

核心说明

3D版本的二次贝塞尔曲线,由「3D起点+3D控制点+3D终点」定义,支持空间弯曲。

构造函数参数
// 语法:new THREE.QuadraticBezierCurve3(起点, 控制点, 终点)
const quadBezier3 = new THREE.QuadraticBezierCurve3(
  new THREE.Vector3(x1, y1, z1), // 必传:3D起点
  new THREE.Vector3(x2, y2, z2), // 必传:3D控制点
  new THREE.Vector3(x3, y3, z3)  // 必传:3D终点
);
参数类型说明
v0THREE.Vector33D起点
v1THREE.Vector33D控制点(决定空间弯曲方向)
v2THREE.Vector33D终点
使用示例
// 1. 创建3D二次贝塞尔曲线
const quadBezier3 = new THREE.QuadraticBezierCurve3(
  new THREE.Vector3(-80, 0, 0),   // 起点
  new THREE.Vector3(0, 80, 50),   // 控制点(Z轴偏移)
  new THREE.Vector3(80, 0, 100)   // 终点
);

// 2. 采样顶点
const points = quadBezier3.getPoints(80);

// 3. 渲染
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints(points);
const material = new THREE.LineBasicMaterial({ color: 0xffff00 });
const line = new THREE.Line(geometry, material);
scene.add(line);

4. CubicBezierCurve3(3D三次贝塞尔曲线)

核心说明

3D版本的三次贝塞尔曲线,由4个3D点定义,支持复杂空间弯曲,是3D路径动画的核心曲线。

构造函数参数
// 语法:new THREE.CubicBezierCurve3(起点, 控制点1, 控制点2, 终点)
const cubicBezier3 = new THREE.CubicBezierCurve3(
  new THREE.Vector3(x1, y1, z1), // 必传:3D起点
  new THREE.Vector3(x2, y2, z2), // 必传:3D控制点1
  new THREE.Vector3(x3, y3, z3), // 必传:3D控制点2
  new THREE.Vector3(x4, y4, z4)  // 必传:3D终点
);
参数类型说明
v0THREE.Vector33D起点
v1THREE.Vector33D控制点1
v2THREE.Vector33D控制点2
v3THREE.Vector33D终点
使用示例
// 1. 创建3D三次贝塞尔曲线
const cubicBezier3 = new THREE.CubicBezierCurve3(
  new THREE.Vector3(-100, 0, 0),  // 起点
  new THREE.Vector3(-50, 100, 50),// 控制点1
  new THREE.Vector3(50, -100, 80),// 控制点2
  new THREE.Vector3(100, 0, 100)  // 终点
);

// 2. 采样顶点
const points = cubicBezier3.getPoints(100);

// 3. 渲染
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints(points);
const material = new THREE.LineBasicMaterial({ color: 0xff00ff });
const line = new THREE.Line(geometry, material);
scene.add(line);

三、CurvePath(组合曲线)

核心说明

CurvePath 是「曲线容器」,可将多个2D/3D曲线组合成一个复合曲线,支持统一采样、平移、旋转等操作,适合绘制复杂路径(如迷宫、文字轮廓)。

核心方法
方法名说明示例
add(curve)添加单个曲线到容器curvePath.add(lineCurve)
getPoints(n)统一采样所有曲线的顶点curvePath.getPoints(100)
closePath()闭合组合曲线(最后一个曲线终点连接第一个曲线起点)curvePath.closePath()
使用示例(组合2D直线+圆弧)
// 1. 创建CurvePath容器
const curvePath = new THREE.CurvePath();

// 2. 添加子曲线(直线+圆弧)
// 子曲线1:2D直线(从(0,0)到(100,0))
const lineCurve = new THREE.LineCurve(
  new THREE.Vector2(0, 0),
  new THREE.Vector2(100, 0)
);
curvePath.add(lineCurve);

// 子曲线2:圆弧(从(100,0)到(100,100),90°圆弧)
const arcCurve = new THREE.ArcCurve(
  100, 0, 100, 0, Math.PI/2, true
);
curvePath.add(arcCurve);

// 3. 闭合曲线(可选)
// curvePath.closePath();

// 4. 统一采样顶点(100个点)
const points = curvePath.getPoints(100);

// 5. 渲染组合曲线
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints(points);
const material = new THREE.LineBasicMaterial({ color: 0xff6600 });
const line = new THREE.Line(geometry, material);
scene.add(line);

四、完整实战示例(多曲线组合)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Three.js 曲线完整示例</title>
  <style>body { margin: 0; overflow: hidden; }</style>
</head>
<body>
 <script type="module">
    // 地址,升级为174版本
    import * as THREE from 'https://esm.sh/three@0.174.0';
    import { OrbitControls } from 'https://esm.sh/three@0.174.0/examples/jsm/controls/OrbitControls.js';
    import { GLTFLoader } from 'https://esm.sh/three@0.174.0/examples/jsm/loaders/GLTFLoader.js';
    // 1. 创建三大核心
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    camera.position.set(0, 0, 300); // 相机后退,看清所有曲线

    // 2. 轨道控制器(3D视角交互)
    const controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.dampingFactor = 0.05;

    // 3. 绘制2D椭圆
    const ellipseCurve = new THREE.EllipseCurve(0, 0, 80, 40);
    const ellipsePoints = ellipseCurve.getPoints(50);
    const ellipseGeo = new THREE.BufferGeometry();
    ellipseGeo.setFromPoints(ellipsePoints);
    const ellipseMat = new THREE.LineBasicMaterial({ color: 0xff0000 });
    const ellipseLine = new THREE.LineLoop(ellipseGeo, ellipseMat);
    scene.add(ellipseLine);

    // 4. 绘制3D样条曲线
    const catmullCurve = new THREE.CatmullRomCurve3([
      new THREE.Vector3(-100, 0, 0),
      new THREE.Vector3(-50, 80, 50),
      new THREE.Vector3(50, -80, 100),
      new THREE.Vector3(100, 0, 50)
    ]);
    const catmullPoints = catmullCurve.getPoints(100);
    const catmullGeo = new THREE.BufferGeometry();
    catmullGeo.setFromPoints(catmullPoints);
    const catmullMat = new THREE.LineBasicMaterial({ color: 0x0000ff });
    const catmullLine = new THREE.Line(catmullGeo, catmullMat);
    scene.add(catmullLine);

    // 5. 动画循环
    function animate() {
      requestAnimationFrame(animate);
      controls.update();
      renderer.render(scene, camera);
    }
    animate();

    // 6. 窗口适配
    window.addEventListener('resize', () => {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    });
  </script>
</body>
</html>

示例效果

c7c96963-4107-4138-a72c-08caaa36d7f3.png

  1. 场景中显示红色2D椭圆(XY平面)和蓝色3D样条曲线(空间弯曲);
  2. 支持鼠标旋转/缩放视角,查看3D曲线的空间形态;
  3. 曲线平滑无锯齿,色彩无偏色。

五、注意事项与优化

1. 常见坑点

  • 线宽限制:WebGL标准中线宽(linewidth)仅支持1px,部分浏览器支持>1但兼容性差,如需粗线条建议用 Mesh 模拟(如挤压曲线成面);
  • 3D曲线视角:3D曲线需调整相机位置(Z轴后退),否则可能看不到;
  • 采样点数:复杂曲线(如三次贝塞尔)需增加采样点数(100~200),否则会出现锯齿;
  • CurvePath闭合closePath() 仅对2D曲线有效,3D曲线闭合需手动调整控制点。

2. 性能优化

  • 复用几何体:多个曲线复用同一个 BufferGeometry(清空顶点后重新绑定);
  • 减少采样点数:简单曲线(直线、圆弧)采样点数设50即可,无需过高;
  • 批量渲染:多个曲线合并为一个 Line 模型,减少渲染调用。

核心总结

  1. 核心流程创建曲线 → getPoints采样顶点 → 几何体绑定顶点 → Line/LineLoop渲染
  2. 曲线选型
    • 2D简单曲线:LineCurve/ArcCurve/EllipseCurve;
    • 2D复杂曲线:SplineCurve/CubicBezierCurve;
    • 3D路径:CatmullRomCurve3(推荐)/CubicBezierCurve3;
    • 复合曲线:CurvePath(组合多个子曲线);
  3. 关键参数
    • getPoints(n)n 决定平滑度,推荐50~100;
    • 3D曲线需调整相机Z轴位置,确保可见;
    • 贝塞尔曲线的「控制点」是决定曲线形状的核心。