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 分钟前
利用资源提示关键词优化网页加载速度
前端
moyu8413 分钟前
跨域问题解析(下):Nginx代理、domain修改与postMessage解决方案
前端
moyu8427 分钟前
跨域问题解析(上):JSONP、CORS与Node代理解决方案
前端
moyu8431 分钟前
深入理解TCP的三次握手与四次挥手
前端
不一样的少年_1 小时前
头像组件崩溃、乱序、加载失败?一套队列机制+多级兜底全搞定
前端·vue.js
Code_XYZ1 小时前
uni-app x开发跨端应用,与web-view的双向通信解决方案
前端
wordbaby1 小时前
构建时规划,运行时执行:解构 React Router 的 prerender 与 loader
前端·react.js
用户5806139393001 小时前
【前端工程化】Eslint+Prettier vue项目实现文件保存时自动代码格式化
前端
麦当_1 小时前
基于 Shadcn 的可配置表单解决方案
前端·javascript·面试
MrSkye1 小时前
从零到一:我用AI对话写出了人生第一个弹幕游戏 | Prompt编程实战心得
前端·ai编程·trae