本文目录
前言
在
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 应用场景
- 模型位置与方向: 在
Three.js
中,模型的位置和方向经常通过三维向量来表示。例如,可以通过设置模型的.position
属性(一个THREE.Vector3
实例)来改变模型在场景中的位置。 - 位移与旋转: 三维向量还可以用来表示模型的位移和旋转。例如,通过向量的加法运算可以实现模型的移动,而通过向量的叉积和归一化等操作可以实现模型的旋转。
- 物理模拟: 在基于物理的模拟中,如粒子系统、碰撞检测等,三维向量也发挥着重要作用。它们可以用来表示速度、加速度、力等物理量。
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>
在学习的路上,如果你觉得本文对你有所帮助的话,那就请关注点赞评论三连吧,谢谢,你的肯定是我写博的另一个支持。