Threejs的三维向量及控制物体前进

前言

Three.js中,三维向量(Vector3)是一个核心概念,它表示一个具有大小和方向的三维空间中的量。下面我将从几个方面对Three.js中的三维向量进行简单介绍:

最终效果

我们通过三维向量(Vector3)监听键盘事件去控制物体向前运动,最终得到效果:
请添加图片描述
那么这个有什么用呢,其实这个用途很大,我们可以结合之前学过的人物动画的控制去模拟人物前进逛展厅的效果。所以这是很重要的一步。

1、三维向量(Vector3)

1.1 基本概念

定义: 三维向量是一个由三个有序数值(x、y、z)组成的集合,这些数值分别代表向量在三维空间中的三个方向(即X轴、Y轴、Z轴)上的分量。
表示:Three.js中,三维向量通常通过THREE.Vector3类来表示,可以通过构造函数new THREE.Vector3(x, y, z)来创建一个新的三维向量实例,其中x、y、z是向量的三个分量,默认值为0。

1.2 常用属性与方法

1.2.1 属性

  • .x、.y、.z:分别表示向量在X轴、Y轴、Z轴上的分量。
  • .length():计算向量的欧几里得长度(即直线距离)。
  • .normalize():将向量的长度归一化为1,即转换为单位向量,保持方向不变但长度为1。

1.2.2 方法

  • .add(v: Vector3):将传入的向量v与当前向量相加,并返回当前向量。
  • .multiplyScalar(s: Float):将当前向量的每个分量与标量s相乘,并返回当前向量。
  • .multiplyVectors ( a : Vector3, b : Vector3 ) : this
    按照分量顺序,将该向量设置为和a * b相等。
  • .distanceTo ( v : Vector3 ) : Float:计算该向量到所传入的v间的距离。

1.3 应用场景

  1. 模型位置与方向:Three.js中,模型的位置和方向经常通过三维向量来表示。例如,可以通过设置模型的.position属性(一个THREE.Vector3实例)来改变模型在场景中的位置。
  2. 位移与旋转: 三维向量还可以用来表示模型的位移和旋转。例如,通过向量的加法运算可以实现模型的移动,而通过向量的叉积和归一化等操作可以实现模型的旋转。
  3. 物理模拟: 在基于物理的模拟中,如粒子系统、碰撞检测等,三维向量也发挥着重要作用。它们可以用来表示速度、加速度、力等物理量。

2、控制物体前进

2.1 前置代码准备

代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        html,
        body {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
        }
    </style>
</head>

<body>
    <script type="module">
        // 倒入轨道控制器
        import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
        import * as THREE from 'three';

        // 创建场景
        const scene = new THREE.Scene();
        // 创建相机
        const camera = new THREE.PerspectiveCamera( // 透视相机
            75, // 视角 角度数
            window.innerWidth / window.innerHeight, // 宽高比 占据屏幕
            0.1, // 近平面(相机最近能看到物体)
            1000, // 远平面(相机最远能看到物体)
        );
        camera.position.set(0, 10, 30);
        // 创建渲染器
        const renderer = new THREE.WebGLRenderer({
            antialias: true, // 抗锯齿
        });

        // 设置渲染器宽高
        renderer.setSize(window.innerWidth, window.innerHeight);
        // renderer(渲染器)的dom元素添加到我们的HTML文档中
        document.body.appendChild(renderer.domElement);
        
        // 地面
        const plane = new THREE.Mesh(
            new THREE.PlaneGeometry(120, 100),
            new THREE.MeshStandardMaterial({ 
                color: 0x817936,
            })
        );
        plane.rotation.x = -Math.PI / 2;
        // 添加到场景中
        scene.add(plane);
        
        // 添加灯光
        const ambientLight = new THREE.AmbientLight(0x404040, 100);
        scene.add(ambientLight);

        // 创建球体
        const sphereGeometry  = new THREE.SphereGeometry(2, 32, 32);
        const sphereMaterial = new THREE.MeshStandardMaterial({ 
                color: 0xfff000,
            })
        const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
        sphere.position.set(0,2,0);
        scene.add(sphere);
 

        // 事件
        renderer.domElement.addEventListener("keydown", (event) => {
           
        })
        renderer.domElement.addEventListener("keyup", (event) => {
            
        })
        const axesHelper = new THREE.AxesHelper( 25 );
        scene.add( axesHelper );
        // 控制器
        const control = new OrbitControls(camera, renderer.domElement);
        // 开启阻尼惯性,默认值为0.05
        control.enableDamping = true;
        // 渲染循环动画
        function animate() {
            // 在这里我们创建了一个使渲染器能够在每次屏幕刷新时对场景进行绘制的循环(在大多数屏幕上,刷新率一般是60次/秒)
            requestAnimationFrame(animate);
            // 更新控制器。如果没在动画里加上,那必须在摄像机的变换发生任何手动改变后调用
            control.update();
            renderer.render(scene, camera);
        };

        // 执行动画
        animate();
    </script>
</body>

</html>

2.2 效果

可以看到我们场景中有一个黄色的小球
在这里插入图片描述

2.3 控制小球前进

接着我们写入监听键盘按下松开的监听代码:

        // 键位
        const keyEvent = {
            W: false,
        };

        // 事件
        window.addEventListener("keydown", (event) => {
            const keyCode = event.key.toUpperCase();
            console.log(keyCode);
            if (keyEvent.hasOwnProperty(keyCode)) {
                keyEvent[keyCode] = true;
            }
        });
        window.addEventListener("keyup", (event) => {
            const keyCode = event.key.toUpperCase();
            if (keyEvent.hasOwnProperty(keyCode)) {
                keyEvent[keyCode] = false;
            }
        });

并且在渲染循环动画加入代码:

            if (keyEvent.W) {
                sphere.position.add(new THREE.Vector3(0,0,-0.033333));
            }

那么我们这里的0.03333是怎么得到的,我们知道一秒30帧,那就是1/30 = 0.03333。

2.4 效果

请添加图片描述


3、完整代码

最后给出完整代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        html,
        body {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
        }
    </style>
</head>

<body>
    <script type="module">
        // 倒入轨道控制器
        import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
        import * as THREE from 'three';

        // 创建场景
        const scene = new THREE.Scene();
        // 创建相机
        const camera = new THREE.PerspectiveCamera( // 透视相机
            75, // 视角 角度数
            window.innerWidth / window.innerHeight, // 宽高比 占据屏幕
            0.1, // 近平面(相机最近能看到物体)
            1000, // 远平面(相机最远能看到物体)
        );
        camera.position.set(0, 10, 30);
        // 创建渲染器
        const renderer = new THREE.WebGLRenderer({
            antialias: true, // 抗锯齿
        });

        // 设置渲染器宽高
        renderer.setSize(window.innerWidth, window.innerHeight);
        // renderer(渲染器)的dom元素添加到我们的HTML文档中
        document.body.appendChild(renderer.domElement);

        // 地面
        const plane = new THREE.Mesh(
            new THREE.PlaneGeometry(120, 100),
            new THREE.MeshStandardMaterial({
                color: 0x817936,
            })
        );
        plane.rotation.x = -Math.PI / 2;
        // 添加到场景中
        scene.add(plane);

        // 添加灯光
        const ambientLight = new THREE.AmbientLight(0x404040, 100);
        scene.add(ambientLight);

        // 创建球体
        const sphereGeometry = new THREE.SphereGeometry(2, 32, 32);
        const sphereMaterial = new THREE.MeshStandardMaterial({
            color: 0xfff000,
        })
        const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
        sphere.position.set(0, 2, 0);
        scene.add(sphere);
        const axesHelper = new THREE.AxesHelper(25);
        scene.add(axesHelper);
        // 键位
        const keyEvent = {
            W: false,
        };

        // 事件
        window.addEventListener("keydown", (event) => {
            const keyCode = event.key.toUpperCase();
            console.log(keyCode);
            if (keyEvent.hasOwnProperty(keyCode)) {
                keyEvent[keyCode] = true;
            }
        });
        window.addEventListener("keyup", (event) => {
            const keyCode = event.key.toUpperCase();
            if (keyEvent.hasOwnProperty(keyCode)) {
                keyEvent[keyCode] = false;
            }
        });
        const velocity = new THREE.Vector3(0,0,-0.033333);
        

        
        // 控制器
        const control = new OrbitControls(camera, renderer.domElement);
        // 开启阻尼惯性,默认值为0.05
        control.enableDamping = true;
        // 渲染循环动画
        function animate() {
            // 在这里我们创建了一个使渲染器能够在每次屏幕刷新时对场景进行绘制的循环(在大多数屏幕上,刷新率一般是60次/秒)
            requestAnimationFrame(animate);
            if (keyEvent.W) {
                sphere.position.add(velocity);
            }
            // 更新控制器。如果没在动画里加上,那必须在摄像机的变换发生任何手动改变后调用
            control.update();
            renderer.render(scene, camera);
        };

        // 执行动画
        animate();
    </script>
</body>

</html>

在学习的路上,如果你觉得本文对你有所帮助的话,那就请关注点赞评论三连吧,谢谢,你的肯定是我写博的另一个支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你华还是你华

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值