Vue+Three.js 入门七(模型根据轨迹自动移动和转向)

LZQ plus

发布于 2020.03.06 18:14 阅读 5664 评论 0

前期的思路分享

  为了实现模型给定像上图中蓝色的轨迹之后来回走动的效果(卫兵巡逻),我一开始的思路就是根据模型坐标和模型的下一个坐标计算出夹角(v1.angleTo(v2);加上特定的方式计算出模型向左转还是向右转),然后用模型的rotation累加累减的方式实现转向效果,实际上模型是在不停转动,并且代码繁多,十分不可取,所以分享一下我这个失败的思路。下面是成功实现效果的经验。

核心知识点

  1、Matrix4 (四维矩阵):它是一个变换矩阵,可以通过它反应向量,如平移、旋转、剪切、缩放、反射、正交或透视投影等。这就是把矩阵应用到向量上。

  2、Euler (欧拉角):欧拉角描述一个旋转变换,通过指定轴顺序和其各个轴向上的指定旋转角度来旋转一个物体。

  3、Quaternion (四元数):根据四元数来表示旋转,这个地方我还没有搞太懂,暂时知道是这么用的。

  4、.getPoint(*):获取轨迹的下一个点。

示例代码

1、根据点坐标定义轨迹,将轨迹画出来(当然也可以不画)。

        let curve = new THREE.CatmullRomCurve3([

          new THREE.Vector3(-650, 0, 400),

          new THREE.Vector3(-650, 0, 0),

          new THREE.Vector3(0, 0, 0),

          new THREE.Vector3(650, 0, 0),

          new THREE.Vector3(650, 0, -400)

        ]);

        this.curve = curve;

        let points = curve.getPoints(1000);

        let geometry = new THREE.Geometry();

        // 把从曲线轨迹上获得的顶点坐标赋值给几何体

        geometry.vertices = points;

        let material = new THREE.LineBasicMaterial({

          color: 0x4488ff

        });

        let line = new THREE.Line(geometry, material);

        this.scene.add(line);

2、导入模型添加动画,具体“入门六”中已经提到,所以不多做介绍。

        let loader = new GLTFLoader();

        loader.load('./objs/factory2/RobotExpressive.glb', (gltf) => {

          let model = gltf.scene;

          model.scale.set(20, 20, 20);

          model.rotation.y = Math.PI;

          this.mixer = new THREE.AnimationMixer(model);

          let action = this.mixer.clipAction(gltf.animations[10]);

          action.play();

          this.robotModel = model;

          this.scene.add(model);

          // this.LOG.info(['gltf.animations -- ', gltf.animations]);

          this.render();

        });

3、animate回调里根据以上核心知识点来控制模型旋转和移动。

      animate() {

        if (this.controls) {

          this.controls.update();

        }

        this.render();

        requestAnimationFrame(this.animate);

        if (this.mixer) {

          let dt = this.clock.getDelta();

          this.mixer.update(dt);

        }

        //添加控制开关,GO是去,BACK是回来,这样可以来回移动

        if (this.robotDirection === 'GO') {

          if (this.progress > 1.0) {

            this.robotDirection = 'BACK'

          } else {

            this.progress += 0.0009;

          }

        } else {

          if (this.progress < 0) {

            this.robotDirection = 'GO'

          } else {

            this.progress -= 0.0009;

          }

        }

        if (this.curve && this.robotModel) {

          let point = this.curve.getPoint(this.progress);

          //模型的偏移量

          let offsetAngle = Math.PI;

          //创建一个4维矩阵

          let mtx = new THREE.Matrix4();

          mtx.lookAt(this.robotModel.position.clone(), point, this.robotModel.up);

          mtx.multiply(new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(0, offsetAngle, 0)));

          //计算出需要进行旋转的四元数值

          let toRot = new THREE.Quaternion().setFromRotationMatrix(mtx);

          //根据以上值调整角度

          this.robotModel.quaternion.slerp(toRot, 0.2);

          this.robotModel.position.set(point.x, point.y, point.z);

        }

      },