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.xcube.rotation.y增加0.01。0.01弧度大约等于0.57度,所以每秒旋转约34度。看起来就是平滑的旋转了。

你想想看,如果改成+= 0.1,它会转得飞快;改成+= 0.001,就慢得像蜗牛。这就是动画的基本原理——通过逐帧改变属性来产生运动感。

3.6 本章小结

今天咱们干了三件事:

  1. 理解了场景、相机、渲染器这三个核心概念。
  2. 用代码创建了它们,并添加了一个立方体。
  3. 让立方体旋转了起来。

说实话,这第一步迈出去,后面就顺了。Three.js的很多高级功能,都是在这三个核心组件上叠加的。你掌握了这个基础,后面学灯光、材质、模型导入,都会轻松很多。

下一章,咱们会深入聊聊相机控制,让你能用鼠标拖拽旋转视角,那才是真正的3D交互体验。到时候你会发现,原来控制视角也这么有讲究。

课后小练习:试着把立方体的颜色改成红色(0xff0000),或者把BoxGeometry(1, 1, 1)改成BoxGeometry(2, 1, 1),看看会发生什么。动手试试,比光看强十倍。

好,今天就到这儿。咱们下章见。