Three.js 自定义相机脚本:让镜头舞动起来

在 Three.js 的 3D 世界里,相机就像是我们的 "电子眼睛",决定着观众能看到怎样的奇幻景观。不过,默认的相机行为有时就像个 "愣头青",移动生硬、缺乏灵性。今天,咱们就化身 "相机驯兽师",编写自定义相机脚本,让它实现平滑过渡、限制运动范围,还能根据用户输入来一场华丽的动态表演!

一、相机基础知识:先摸清 "电子眼睛" 的脾气

在 Three.js 的舞台背后,相机分为正交相机(OrthographicCamera)和透视相机(PerspectiveCamera)。透视相机就像我们人类的眼睛,遵循近大远小的视觉规律,能营造出逼真的 3D 空间感;而正交相机则像工程图纸里的 "铁面无私者",无论远近,物体大小始终如一。

我们最常用的透视相机,它的创建就像组装一台精密的光学仪器:

dart 复制代码
// 创建一个透视相机,视角75度,宽高比,近裁切面,远裁切面
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// 将相机放置在初始位置
camera.position.set(0, 5, 10);

这里的视角、宽高比、近裁切面和远裁切面参数,就像是给相机设定的 "视力范围" 和 "观察规矩"。

二、实现平滑过渡:给相机装上 "丝滑轮滑"

默认的相机移动,就像突然急刹车又猛踩油门,让人头晕目眩。要实现平滑过渡,我们得给相机添加一个 "缓动系统",就像给它的 "脚步" 加上润滑油。

我们借助Tween.js库来实现这一效果。首先引入Tween.js,然后编写代码:

php 复制代码
// 引入Tween.js库
import * as TWEEN from '@tweenjs/tween.js';
// 定义目标位置
const targetPosition = new THREE.Vector3(5, 5, 15);
// 创建缓动动画
const tween = new TWEEN.Tween(camera.position)
   .to({ x: targetPosition.x, y: targetPosition.y, z: targetPosition.z }, 1000) // 1000毫秒内过渡到目标位置
   .easing(TWEEN.Easing.Quadratic.InOut) // 使用二次方缓动函数
   .start();
// 在动画循环中更新Tween
function animate() {
    requestAnimationFrame(animate);
    TWEEN.update();
    renderer.render(scene, camera);
}
animate();

这段代码就像是给相机制定了一个优雅的 "移动计划表",让它在 1 秒内,以二次方缓动的优雅姿态,从当前位置滑向目标位置,整个过程丝滑流畅。

三、限制运动范围:给相机划定 "活动禁区"

有时候,我们不希望相机 "乱跑",比如在一个虚拟房间内,不能让它穿墙而出。这时候,我们就得给相机划定 "活动禁区",用数学的方式(虽然不用公式,但原理类似)来限制它的位置。

ini 复制代码
// 定义相机运动范围
const minX = -10;
const maxX = 10;
const minY = 0;
const maxY = 20;
const minZ = 5;
const maxZ = 25;
// 在相机移动后检查并限制位置
function checkAndLimitCameraPosition() {
    if (camera.position.x < minX) {
        camera.position.x = minX;
    } else if (camera.position.x > maxX) {
        camera.position.x = maxX;
    }
    if (camera.position.y < minY) {
        camera.position.y = minY;
    } else if (camera.position.y > maxY) {
        camera.position.y = maxY;
    }
    if (camera.position.z < minZ) {
        camera.position.z = minZ;
    } else if (camera.position.z > maxZ) {
        camera.position.z = maxZ;
    }
}
// 在动画循环中调用检查函数
function animate() {
    requestAnimationFrame(animate);
    // 假设这里有其他相机移动逻辑
    checkAndLimitCameraPosition();
    renderer.render(scene, camera);
}
animate();

这段代码就像给相机戴上了 "电子围栏",一旦它想要越界,就会被强制拉回到规定的范围内,保证它始终在我们设定的 "舞台" 上活动。

四、基于用户输入的动态变化:让相机听懂 "指挥"

为了让相机更 "智能",能根据用户的操作做出反应,我们需要监听用户输入事件,比如键盘按键、鼠标移动等。

1. 监听键盘按键控制相机移动

ini 复制代码
// 记录键盘按键状态
const keys = {
    w: false,
    a: false,
    s: false,
    d: false
};
// 监听键盘按下事件
document.addEventListener('keydown', function (event) {
    if (event.key === 'w') {
        keys.w = true;
    } else if (event.key === 'a') {
        keys.a = true;
    } else if (event.key ==='s') {
        keys.s = true;
    } else if (event.key === 'd') {
        keys.d = true;
    }
});
// 监听键盘松开事件
document.addEventListener('keyup', function (event) {
    if (event.key === 'w') {
        keys.w = false;
    } else if (event.key === 'a') {
        keys.a = false;
    } else if (event.key ==='s') {
        keys.s = false;
    } else if (event.key === 'd') {
        keys.d = false;
    }
});
// 在动画循环中根据按键状态移动相机
function animate() {
    requestAnimationFrame(animate);
    const speed = 0.1;
    if (keys.w) {
        camera.position.z -= speed;
    }
    if (keys.s) {
        camera.position.z += speed;
    }
    if (keys.a) {
        camera.position.x -= speed;
    }
    if (keys.d) {
        camera.position.x += speed;
    }
    renderer.render(scene, camera);
}
animate();

这段代码赋予了相机 "听指令" 的能力,当我们按下键盘上的 W、A、S、D 键时,相机就会按照我们的指示移动,就像一个听话的 "小跟班"。

2. 监听鼠标移动控制相机旋转

ini 复制代码
let mouseX = 0;
let mouseY = 0;
const windowHalfX = window.innerWidth / 2;
const windowHalfY = window.innerHeight / 2;
// 监听鼠标移动事件
document.addEventListener('mousemove', function (event) {
    mouseX = (event.clientX - windowHalfX);
    mouseY = (event.clientY - windowHalfY);
});
// 在动画循环中根据鼠标位置旋转相机
function animate() {
    requestAnimationFrame(animate);
    const rotateSpeed = 0.001;
    camera.rotation.y += (mouseX * rotateSpeed);
    camera.rotation.x -= (mouseY * rotateSpeed);
    // 限制相机的俯仰角度,避免"倒立"
    camera.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, camera.rotation.x));
    renderer.render(scene, camera);
}
animate();

有了这段代码,我们的鼠标就成了控制相机的 "方向盘",轻轻移动鼠标,相机就会跟着旋转视角,让我们可以全方位欣赏 3D 世界的美景,同时限制俯仰角度,保证画面始终 "端正"。

五、总结与展望

通过编写自定义相机脚本,我们让 Three.js 中的相机从 "呆板" 变得 "灵动"。从平滑过渡的优雅舞步,到限制运动范围的 "守规矩",再到基于用户输入的 "智能互动",每一步都像是在给相机注入新的 "灵魂"。

未来,我们还可以进一步探索更多有趣的功能,比如结合物理引擎让相机的移动更符合现实规律,或者根据场景氛围自动调整相机的参数。快拿起代码,开启属于你的 Three.js 相机定制之旅吧,让 3D 世界的视角变得独一无二!

上述内容从多方面讲解了 Three.js 自定义相机脚本。你若觉得某些部分需要补充,或有新的功能需求,欢迎随时和我说。

相关推荐
10年前端老司机几秒前
React 受控组件和非受控组件区别和使用场景
前端·javascript·react.js
夏晚星几秒前
vue实现微信聊天emoji表情
前端·javascript
停止重构3 分钟前
【方案】前端UI布局的绝技,响应式布局,多端适配
前端·网页布局·响应式布局·grid布局·网页适配多端
極光未晚3 分钟前
TypeScript在前端项目中的那些事儿:不止于类型的守护者
前端·javascript·typescript
ze_juejin4 分钟前
Vue3 + Vite + Ant Design Vue + Axios + Pinia 脚手架搭建
前端·vue.js
Rrvive5 分钟前
原型与原型链到底是什么?
javascript
lichenyang4536 分钟前
React项目(移动app)
前端
用户61848240219517 分钟前
Vue-library-start,一个基于Vite的vue组件库开发模板
前端
美团技术团队19 分钟前
报名 | 美团技术沙龙第86期:多业务场景下,美团如何做性能优化
前端
Rrvive1 小时前
localhost 和 127.0.0.1 的核心区别
前端