Threejs 两点生成三阶贝塞尔曲线-平面上

884 阅读2分钟

吹水

threejs 学习一段时间了,之前在一直在潜水默默地学习

就在前几天实现平面上飞线效果遇到小小问题,还未解决就阳了,遗憾带阳休息了5天,发烧的两天里满脑袋都是飞线……

三阶贝塞尔曲线

关于贝塞尔曲线看这一篇就够了: 这一篇让你彻底搞懂贝塞尔曲线的原理 - 掘金 (juejin.cn) 这位大佬写得非常好。

参考过的两篇文章

Threejs模仿实现滴滴/github官网首页地球动画 - 掘金 (juejin.cn)

Threejs实现酷炫3D地球技术点汇总 - 知乎 (zhihu.com)

网上的文章基本上都是基于球体上实现的,直接拿来用肯定翻车。

代码

获取两点的中心点

const getVCenter = (v1: THREE.Vector3, v2: THREE.Vector3) => {
  const v = v1.add(v2);
  return v.divideScalar(2);
}

很简单的算法,两向量相加再除2

获取两向量之间的任意一向量

const getLenVector = (v1: THREE.Vector3, v2: THREE.Vector3, len: number) => {
  let v1v2Len = v1.distanceTo(v2);
  return v1.lerp(v2, len / v1v2Len);
}

获取两向量之间的任意一向量,具体是哪一点,由lerp的第二个参数决定,范围 0.0 ~ 1.0, 即:

  • 0 = 初始点;
  • 0.5 = 中间点;
  • 1 = 终点;

len与两点的距离归一化为 0.0 ~ 1.0 (理论上)

根据二点生成三阶贝塞尔曲线的两点控制点

// 获取贝塞尔控制点
const getBezierPoint = (v0: THREE.Vector3, v3: THREE.Vector3) => {

  // 角度
  const angle = v0.angleTo(v3)   * 180 / Math.PI; // 0 ~ Math.PI//
  // 使用 1.2 和 10 来调整弧度
  // 角度值与长度值
  const aLen = angle * 1.2, hLen = angle * angle * 10;

  // 两点的中心位置
  const centerPoint = getVCenter(v0.clone(), v3.clone())

  // 法线向量、使用中心点和向上的向量
  const rayLine = new THREE.Ray(centerPoint, new THREE.Vector3(0, 1, 0));

  // API 更新后,Ray类的 at 方法需要两个参数
  const temp  = new THREE.Vector3(0, 0, 0);

  // 计算位置
  const at = hLen / rayLine.at(1,temp).distanceTo(centerPoint);
  // 顶点坐标
  const vTop = rayLine.at(at, temp);

  // 控制点坐标
  const v1 = getLenVector(v0.clone(), vTop, aLen);
  const v2 = getLenVector(v3.clone(), vTop, aLen);
  return [v1, v2]
}

上面的 1.2 和 10 来调整弧度

球体上区别

// 射线的第一个参数(原点)为0点
// 第二个参数(法线向量)为两点的中心点
const centerPoint = getVCenter(v0.clone(), v3.clone())
const rayLine = new THREE.Ray(new THREE.Vector3(), centerPoint);

平面上的区别

// 射线的第一个参数(原点)为两点的中心点
// 第二个参数(法线向量)为向上的向量
const centerPoint = getVCenter(v0.clone(), v3.clone())
const rayLine = new THREE.Ray(centerPoint, new THREE.Vector3(0, 1, 0));

使用

const v0 = new THREE.Vector3(-126, 10, 54);
const v3 = new THREE.Vector3(226, 10, 54);
const [v1, v2] = getBezierPoint(v0, v3);
const curve2 = new THREE.CubicBezierCurve3(
  v0, v1, v2, v3
);
const points = curve2.getPoints(50);
const lineGeometry2 = new THREE.BufferGeometry().setFromPoints(points);
const material2 = new THREE.LineBasicMaterial({color: new THREE.Color("#163dff")})
const curveObject = new THREE.Line(lineGeometry2, material2);
scene.add(curveObject)

效果图

二点生成贝塞尔曲线.png

效果