three.js 物体、路径与移动,3D效果

在二维实现的基础上,替换路径数组,生成3维曲线,设置潜艇的3维坐标延路径移动

import * as THREE from 'three';
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { DragControls } from "three/examples/jsm/controls/DragControls";
import { Pieces } from "../@share/pieces";

/**
 * 3d 动画测试,路径与移动 - 空间中
 * https://threejs.org/docs/index.html#api/zh/extras/curves/CatmullRomCurve3 - 三维样条曲线
 * https://threejs.org/docs/index.html#api/zh/extras/core/Curve - 核心
 */
export class ThreePathAndMove3d {
    constructor(canvasId) {
        this.work(canvasId);
    }

    work(canvasId) {
        // 创建 3d 场景
        const scene = new THREE.Scene();
        scene.background = new THREE.Color(0x9e9e9e);

        // 渲染器
        const renderer = this.addRenderer();

        // 创建相机
        const camera = this.makeCamera();
        // .multiplyScalar() 矩阵的每个元素乘以参数。
        camera.position.set(-60, 20, 150).multiplyScalar(3);
        // 朝向
        camera.lookAt(0, 0, 0);
        // 控制相机
        const controls = new OrbitControls(camera, renderer.domElement);
        controls.update();

        // 初始化灯光
        // 方向光
        const light = new THREE.DirectionalLight(0xffffff, 1);
        light.position.set(0, 20, 0)
        scene.add(light);
        // 方向光
        const light2 = new THREE.DirectionalLight(0xffffff, 1);
        light2.position.set(1, 2, 4);
        scene.add(light2);

        // 添加物体
        const boat = this.makeObject(scene);

        // 绘制路径
        const len = 300;
        const curve = this.addPath(scene, len / 2);
        console.log(curve);

        const boatPosition = new THREE.Vector3();
        const boatTarget = new THREE.Vector3();

        function render(time) {
            time *= 0.0005;
            const boatTime = time * 0.05;
            curve.getPointAt(boatTime % 1, boatPosition);
            // 获取路径前一点坐标,用于头部向前
            curve.getPointAt((boatTime + 0.01) % 1, boatTarget);
            // 位移
            boat.position.set(boatPosition.x, boatPosition.y, boatPosition.z);
            boat.lookAt(boatTarget.x, boatTarget.y, boatTarget.z);

            // 加载渲染器
            renderer.render(scene, camera)
            // 开始动画
            requestAnimationFrame(render)
        }

        // 开始渲染
        requestAnimationFrame(render);
    }

    /**
     * 创建相机公用方法
     * */
    makeCamera(fov = 40) {
        const aspect = 2 // the canvas default
        const zNear = 0.1
        const zFar = 1000
        return new THREE.PerspectiveCamera(fov, aspect, zNear, zFar)
    }

    /**
     * 添加平面
     */
    addArea(scene, len) {
        const groundGeometry = new THREE.PlaneGeometry(len, len);
        const groundMaterial = new THREE.MeshPhongMaterial({ color: 0x23ADE5 });
        const groundMesh = new THREE.Mesh(groundGeometry, groundMaterial);
        groundMesh.rotation.x = Math.PI * -0.5;
        scene.add(groundMesh);
    }

    /**
     * 添加渲染器
     */
    addRenderer() {
        let renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 最后一步很重要,我们将renderer(渲染器)的dom元素(renderer.domElement)添加到我们的HTML文档中。这就是渲染器用来显示场景给我们看的<canvas>元素。
        document.body.appendChild(renderer.domElement);
        return renderer;
    }

    /**
     * 添加船只
     */
    makeObject(scene) {
        const boat = new THREE.Object3D();
        boat.position.y = -1;
        scene.add(boat);

        const bodyRadius = 2;
        const bodyLength = 10;
        // 舰体
        const bodyGeometry = new THREE.CapsuleGeometry(bodyRadius, bodyLength, 4, 32);
        const bodyMaterial = new THREE.MeshPhongMaterial({ color: 0x6688aa });
        const bodyMesh = new THREE.Mesh(bodyGeometry, bodyMaterial);
        bodyMesh.rotation.x = Math.PI * 0.5;
        boat.add(bodyMesh);

        const doorRadius = bodyRadius / 7 * 5;
        const doorHeight = 2;
        // 舰桥
        const doorGeometry = new THREE.CylinderGeometry(doorRadius, doorRadius, doorHeight, 36);
        const doorMesh = new THREE.Mesh(doorGeometry, bodyMaterial);
        doorMesh.position.set(0, 2, -2);
        boat.add(doorMesh);

        const glassRadius = doorRadius / 7;
        const glassHeight = bodyLength / 2;
        // 潜望镜
        const glassGeometry = new THREE.CylinderGeometry(glassRadius, glassRadius, glassHeight, 36);
        const glassMesh = new THREE.Mesh(glassGeometry, bodyMaterial);
        glassMesh.position.set(0, 2, -2.9);
        boat.add(glassMesh);

        const swingWidth = 5;
        const swingHeight = doorHeight / 20;
        // 舰桥翼
        const swingGeometry = new THREE.BoxGeometry(swingWidth, swingHeight, 1, 36);
        const swingMesh = new THREE.Mesh(swingGeometry, bodyMaterial);
        swingMesh.position.set(0, 2.5, -2);
        boat.add(swingMesh);

        const swingTailHeight = doorHeight / 8;
        const swingTailWidth = 4;
        // 尾翼
        const swingTail1Geometry = new THREE.BoxGeometry(swingTailWidth, swingTailHeight, 2, 36);
        const swingT1Mesh = new THREE.Mesh(swingTail1Geometry, bodyMaterial);
        swingT1Mesh.position.set(0, 0, -6);
        boat.add(swingT1Mesh);
        const swingTail2Geometry = new THREE.BoxGeometry(swingTailHeight, swingTailWidth, 2, 36);
        const swingT2Mesh = new THREE.Mesh(swingTail2Geometry, bodyMaterial);
        swingT2Mesh.position.set(0, 0, -6);
        boat.add(swingT2Mesh);

        return boat;
    }

    /**
     * 添加路径
     */
    addPath(scene, num) {
        let max = num;
        let min = -num;
        let pointArr = [];
        // 随机点
        for (let i = 0; i < 10; i++) {
            let point = Pieces.getRandomNumberByCount(3, max, 0, min);
            pointArr.push(new THREE.Vector3(point[0], point[1], point[2]));
        }
        /**
         * CatmullRomCurve3
         * 使用Catmull-Rom算法, 从一系列的点创建一条平滑的三维样条曲线。
         * CatmullRomCurve3( points : Array, closed : Boolean, curveType : String, tension : Float )
         * points – Vector3点数组
         * closed – 该曲线是否闭合,默认值为false。
         * curveType – 曲线的类型,默认值为centripetal。
         * tension – 曲线的张力,默认为0.5。
         * @type {CatmullRomCurve3}
         */
        const curve = new THREE.CatmullRomCurve3(pointArr, true);

        const points = curve.getPoints(50);
        const geometry = new THREE.BufferGeometry().setFromPoints(points);
        const material = new THREE.LineBasicMaterial({ color: 0xffffff });
        const splineObject = new THREE.Line(geometry, material);
        scene.add(splineObject);

        const boxHelper = new THREE.BoxHelper(splineObject);
        scene.add(boxHelper);

        return curve;
    }

}

 

posted @ 2022-06-08 16:28  名字不好起啊  阅读(983)  评论(0编辑  收藏  举报