3、Three.js基础入门:场景、相机、渲染器与第一个3D立方体
好,咱们正式开始动手了。
前面两章聊了聊3D交互的宏观概念和工具链,估计你已经手痒了。今天这一章,咱们直接进入Three.js的核心三件套:场景(Scene)、相机(Camera)、渲染器(Renderer)。说白了,这三个东西就是搭建任何3D世界的基石。你想想看,要拍一部电影,你得有舞台(场景)、有摄像机(相机)、还得有放映机(渲染器),对吧?Three.js里也是这个道理。
3.1 核心三件套:它们到底是干嘛的?
我刚开始学Three.js的时候,也对着这三个概念发过呆。其实没那么玄乎,咱们一个一个拆开看。
- 场景(Scene):就是一个容器。你所有的物体、灯光、辅助线,都得扔进这个容器里。它就像你的3D世界本身。
- 相机(Camera):决定了你从哪个角度去看这个世界。你换个角度,看到的东西就不一样。Three.js里最常用的是透视相机(PerspectiveCamera),模拟人眼效果,近大远小。
- 渲染器(Renderer):负责把场景和相机拍到的画面,真正画到你的浏览器屏幕上。没有它,你啥也看不见。
一句话总结:场景是舞台,相机是眼睛,渲染器是画笔。三者缺一不可。
3.2 创建你的第一个场景
好,咱们直接上代码。别怕,就几行。
// 1. 创建场景
const scene = new THREE.Scene();
// 2. 创建透视相机
// 参数:视野角度(FOV),宽高比,近裁剪面,远裁剪面
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// 3. 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 4. 把相机往后拉一点,不然我们和物体重叠在一起
camera.position.z = 5;
嗯,这里要注意几个细节。
透视相机的四个参数:
| 参数 | 说明 | 我的建议 |
|---|---|---|
| FOV (视野角度) | 视角范围,单位是度。值越大,看到的范围越广,但物体显得越小。 | 一般用45-75之间。75是比较舒服的默认值。 |
| Aspect (宽高比) | 渲染区域的宽度除以高度。通常用窗口的宽高比。 | 直接用 window.innerWidth / window.innerHeight 最省事。 |
| Near (近裁剪面) | 相机能看到的最远距离。 | 0.1 够用了。太小可能导致深度冲突。 |
| Far (远裁剪面) | 相机能看到的最远距离。 | 1000 对于入门场景足够了。太大可能影响性能。 |
避坑指南:我曾经把Near设成0.001,Far设成100000,结果渲染出来的物体出现了奇怪的闪烁。后来才知道,Near和Far的比值太大,会导致深度精度不够。记住,Near和Far的比值不要超过1000倍。
3.3 添加第一个物体:一个旋转的立方体
场景、相机、渲染器都有了,但舞台上空荡荡的。咱们加个立方体进去。
// 创建一个立方体几何体(长宽高各为1)
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 创建一个材质(这里用基础材质,颜色设为蓝色)
const material = new THREE.MeshBasicMaterial({ color: 0x00aaff });
// 把几何体和材质组合成一个网格对象
const cube = new THREE.Mesh(geometry, material);
// 把立方体添加到场景中
scene.add(cube);
// 动画循环:让立方体转起来
function animate() {
requestAnimationFrame(animate);
// 让立方体绕X轴和Y轴旋转
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
// 渲染场景
renderer.render(scene, camera);
}
animate();
你看,就这么简单。一个蓝色的立方体就在屏幕上旋转起来了。
我个人习惯把animate函数放在最后,这样逻辑清晰。为什么用requestAnimationFrame?因为它比setInterval聪明得多。它会等浏览器准备好再执行下一帧,不会浪费CPU。而且当页面切换到后台时,它会自动暂停,省电又省资源。
3.4 完整代码示例
把上面两段代码拼起来,就是咱们第一个完整的3D页面了。
<!DOCTYPE html>
<html>
<head>
<title>我的第一个Three.js立方体</title>
<style>
body { margin: 0; overflow: hidden; }
</style>
</head>
<body>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js"
}
}
</script>
<script type="module">
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.z = 5;
// 渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 立方体
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00aaff });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 动画循环
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
// 窗口大小变化时自适应
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>
重要提醒:记得在HTML里加上body { margin: 0; overflow: hidden; }。不然页面会有默认边距,渲染器填不满屏幕,看起来会有一条白边。我刚开始就因为这个排查了半天,以为是代码写错了。
3.5 让立方体动起来:理解动画循环
为什么立方体会旋转?因为我们在animate函数里,每一帧都改变了它的rotation属性。
这里有个关键点:requestAnimationFrame会以每秒约60次的频率调用animate函数。每次调用,我们都让cube.rotation.x和cube.rotation.y增加0.01。0.01弧度大约等于0.57度,所以每秒旋转约34度。看起来就是平滑的旋转了。
你想想看,如果改成+= 0.1,它会转得飞快;改成+= 0.001,就慢得像蜗牛。这就是动画的基本原理——通过逐帧改变属性来产生运动感。
3.6 本章小结
今天咱们干了三件事:
- 理解了场景、相机、渲染器这三个核心概念。
- 用代码创建了它们,并添加了一个立方体。
- 让立方体旋转了起来。
说实话,这第一步迈出去,后面就顺了。Three.js的很多高级功能,都是在这三个核心组件上叠加的。你掌握了这个基础,后面学灯光、材质、模型导入,都会轻松很多。
下一章,咱们会深入聊聊相机控制,让你能用鼠标拖拽旋转视角,那才是真正的3D交互体验。到时候你会发现,原来控制视角也这么有讲究。
课后小练习:试着把立方体的颜色改成红色(0xff0000),或者把BoxGeometry(1, 1, 1)改成BoxGeometry(2, 1, 1),看看会发生什么。动手试试,比光看强十倍。
好,今天就到这儿。咱们下章见。