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 自定义相机脚本。你若觉得某些部分需要补充,或有新的功能需求,欢迎随时和我说。

相关推荐
卸任2 小时前
Vibe coding工具使用心得
前端·ai编程
brien93 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance3 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M3 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写3 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李3 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易3 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
清晨Coding3 小时前
删了 Cookie 还能自动登录?Google系网站登录态自动恢复的原理分析
javascript
风骏时光牛马3 小时前
前沿视界
前端
kyriewen4 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员