threejs多点位贝塞尔曲线实现, 多阶贝塞尔曲线的生成

threejs的贝塞尔曲线的生成方法不论是THREE.CubicBezierCurve3还是THREE.QuadraticBezierCurve3本质上都是 调用内部的方法计算返回曲线上的点,所以要实现多点位的贝塞尔曲线就是调用数学方法生成曲线的点。

关于贝塞尔曲线的数学公式方法网上有很多这里不赘述了,下面是整理计算好的生成方法,可以通过传入的点位数组来生成曲线上的点。


    /**
  * 多阶贝塞尔曲线的生成
  * @param {*} anchorpoints 贝塞尔基点
  * @param {*} pointsAmount 生成的点数
  * @returns 路径点的Array
  */
    CreateBezierPoints(anchorpoints, pointsAmount) {
        // let last = anchorpoints[anchorpoints.length-1]
        var points = [];
        for (var i = 0; i < pointsAmount; i++) {
            var point = this.MultiPointBezier(anchorpoints, i / pointsAmount);
            points.push(point);
        }
        return points;
    }

    MultiPointBezier(points, t) {
        var len = points.length;
        var x = 0, y = 0, z = 0;
        var erxiangshi = function (start, end) {
            var cs = 1, bcs = 1;
            while (end > 0) {
                cs *= start;
                bcs *= end;
                start--;
                end--;
            }
            return (cs / bcs);
        };
        for (var i = 0; i < len; i++) {
            var point = points[i].position;
            x += point.x * Math.pow((1 - t), (len - 1 - i)) * Math.pow(t, i) * (erxiangshi(len - 1, i));
            y += point.y * Math.pow((1 - t), (len - 1 - i)) * Math.pow(t, i) * (erxiangshi(len - 1, i));
            z += point.z * Math.pow((1 - t), (len - 1 - i)) * Math.pow(t, i) * (erxiangshi(len - 1, i));
        }
        return { x: x, y: y, z: z };
    }

下面我们来调用生成这个方法来创造曲线

creatBezier() {
        // 创建贝塞尔曲线对象,并添加到场景中。
        // 创建控制点
        const positions = [
            { x: 0, y: 0, z: 0 },   // 起点
            { x: 2, y: 4, z: 0 },   // 控制点1
            { x: 4, y: 0, z: 0 },   // 控制点2
            { x: 6, y: 0, z: 0 },   // 控制点3
            { x: 6, y: 0, z: 0 },   // 控制点4
            { x: 8, y: 4, z: 0 },   // 控制点5
            { x: 10, y: 0, z: 0 },  // 控制点6
            { x: 12, y: 0, z: 0 }   // 终点
        ];
        //这里是循环创建控制点和开始结束点,方便在页面上可视化,这个是红色小方块
        for (var i = 0; i < positions.length; i++) {
            var geometry = new THREE.BoxGeometry(0.1, 0.1, 0.1);
            var material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
            var sphere = new THREE.Mesh(geometry, material);
            sphere.position.set(positions[i].x, positions[i].y, positions[i].z);
            sphere.modelType = 1;
            this.scene.add(sphere);
            this.controlPoints.push(sphere);
        }
        //这里调用方法传入点的数组,曲线的点位切成1000份传出
        this.points = this.CreateBezierPoints(this.controlPoints, 1000)
        // // 创建管道
        var geometry = new THREE.BufferGeometry().setFromPoints(this.points);
        // 创建线条材质
        var material = new THREE.LineBasicMaterial({ color: 0xff0000 });
        // 创建线条对象
        this.curveObject = new THREE.Line(geometry, material);
        // 将曲线对象添加到场景中,到这里已经可以在页面上看见曲线了
        this.sceneHelpers.add(this.curveObject);
     
    }

但是光看见还不够,还要可以交互,我们将曲线的更新函数加到场景的刷新函数中

   // 更新贝塞尔曲线和控制点
    updateBezier() {
        // 更新控制点的位置
        this.points = this.CreateBezierPoints(this.controlPoints, 1000)
        // 重新计算曲线路径
        this.curveObject.geometry.setFromPoints(this.points);
    }

 这样我们就可以在拖动控制点的时候,同步更新曲线的位置,弯曲程度了。

 多阶贝塞尔曲线的生成本来到这里就结束了,但是threejs生成的曲线可以有封装好的方法可以直接拿到点位的切线向量,咱这个手搓的没有,没有这个方法的话就不能让模型沿着曲线方向移动,这不能忍,咱也不能少了。

// 这个是原生的贝塞尔曲线的方法 贝塞尔曲线对象 curve,并且有一个表示曲线上某个位置的参数 t
const t = 0.5; // 曲线上的位置参数,范围为 0 到 1

// 获取曲线上特定位置处的切线向量
const tangent = curve.getTangentAt(t);

// 现在 tangent 包含了曲线上特定位置处的切线向量
console.log(tangent);
//其实原理也很简单,就是取相近的2个点来计算近似的曲线
//在这里传入想要计算的点位在曲线的下标
getVectorQuantity(index) {
        // 获取相邻点的位置坐标
        const p1 = this.points[index - 1];//这里的this.options是之前生成的曲线1000点
        const p2 = this.points[index];

        // 计算相邻点之间的方向向量,作为切线向量的近似值
        const tangent = new THREE.Vector3().subVectors(p2, p1).normalize();

        // 现在 tangent 包含了曲线上特定点处的切线向量的近似值
        console.log(tangent);
        return tangent; // 返回切线向量
    }

 现在有了向量咱就可以让模型沿着曲线运动了

updatebox() {
        //这里的点位要向下取整不然数组识别不出来
        let index = Math.floor(this.time * 1000)
        if (index == 0) {
            index = 1
        }
            const pointOnCurve = this.points[index]; // 你的管道曲线坐标
            this.selected.position.copy(pointOnCurve);//选中的模型

            // 计算模型朝向
            const tangent = this.getVectorQuantity(index);//调用计算方向的方法
            this.selected.lookAt(new THREE.Vector3().addVectors(pointOnCurve, tangent));

            // 更新 t,使方块沿着管道移动
            this.time += 0.001; // 调整移动速度

            // 如果 t 超出范围,重置为 0
            if (this.time > 1) {
                this.time = 0;
            }

    }
//将这个方法也添加到更新函数中即可

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值