1. Three.js 入门:从零搭建你的第一个3D场景

大家好,我是老张。今天咱们聊聊 Three.js 入门。说实话,我第一次接触 Three.js 的时候,心里也犯嘀咕——这玩意儿到底能干啥?后来在几个项目里摸爬滚打,才慢慢摸清了门道。嗯,今天就把这些经验分享给你。

1.1 Three.js 是什么?

Three.js 是一个基于 WebGL 的 JavaScript 3D 库。说白了,它帮你把复杂的 WebGL 底层操作封装成了简单好用的 API。你不需要懂图形学里的矩阵变换、着色器编程,就能在浏览器里搞出酷炫的 3D 效果。

我个人习惯把它比作「3D 界的 jQuery」。WebGL 就像原生 JavaScript,功能强大但写起来费劲。Three.js 就像 jQuery,让你用几行代码搞定原本几十行的事。

核心特点:

  • 开源免费,社区活跃
  • 支持所有主流浏览器(包括移动端)
  • 内置了场景、相机、渲染器、灯光、材质等完整体系
  • 可以加载各种 3D 模型格式(glTF、OBJ、FBX 等)

我在项目中遇到过不少用 Three.js 做的案例:产品展示页、数据可视化大屏、甚至一个简单的室内漫游 demo。你想想看,一个网页就能跑 3D,多方便。

1.2 三大核心概念:场景、相机、渲染器

Three.js 的架构其实特别简单,就三个核心东西:场景(Scene)相机(Camera)渲染器(Renderer)。这三者缺一不可,就像拍电影需要舞台、摄像机和放映机一样。

1.2.1 场景(Scene)

场景就是你的 3D 世界。所有物体、灯光、辅助线都放在里面。你可以把它想象成一个空房间,你往里面放什么,它就有什么。

const scene = new THREE.Scene();

就这么一行,一个空场景就建好了。然后你可以往里面加东西:

// 加一个立方体
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

小技巧: 我习惯在场景里加一个网格辅助线(GridHelper),方便观察物体的位置和方向。尤其是调试的时候,这东西帮了大忙。

1.2.2 相机(Camera)

相机决定了你从哪个角度观察场景。Three.js 提供了好几种相机,最常用的是 透视相机(PerspectiveCamera)正交相机(OrthographicCamera)

透视相机模拟人眼效果——近大远小。正交相机则没有透视效果,适合做 2D 或等轴测图。

// 透视相机:视野75度,宽高比自适应,近裁面0.1,远裁面1000
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 2, 5);  // 把相机往后拉一点
camera.lookAt(0, 0, 0);        // 看向原点

这里要注意几个参数:

参数 说明 我的建议
FOV(视野) 视角宽度,单位度 一般 45-75 之间,太宽会变形
宽高比 画面宽度/高度 用 window.innerWidth / window.innerHeight
近裁面/远裁面 可见范围 近裁面别设太小,远裁面别设太大

避坑指南: 我曾经把近裁面设成了 0.001,远裁面设成了 100000。结果场景里有些物体莫名其妙消失了,查了半天才发现是精度问题。建议近裁面 0.1,远裁面 1000 就够用。

1.2.3 渲染器(Renderer)

渲染器负责把场景和相机的内容画到屏幕上。最常用的是 WebGLRenderer。

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

渲染器会创建一个 canvas 元素,你把它挂到页面上就行。然后每帧调用:

function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}
animate();

嗯,这里要注意:渲染器一定要在场景和相机都准备好之后才调用 render。不然你会看到一个黑屏,啥也没有。

1.3 搭建第一个 Three.js 场景

好了,理论说完了,咱们动手写一个完整的例子。这个场景里有一个旋转的立方体,背景是浅蓝色。

<!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';

    // 1. 创建场景
    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0x87CEEB); // 浅蓝色

    // 2. 创建相机
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.set(0, 2, 5);
    camera.lookAt(0, 0, 0);

    // 3. 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // 4. 添加一个立方体
    const geometry = new THREE.BoxGeometry(1, 1, 1);
    const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
    const cube = new THREE.Mesh(geometry, material);
    scene.add(cube);

    // 5. 添加网格辅助线(方便观察)
    const gridHelper = new THREE.GridHelper(10, 10);
    scene.add(gridHelper);

    // 6. 动画循环
    function animate() {
      requestAnimationFrame(animate);

      // 让立方体旋转
      cube.rotation.x += 0.01;
      cube.rotation.y += 0.01;

      renderer.render(scene, camera);
    }
    animate();

    // 7. 窗口大小自适应
    window.addEventListener('resize', () => {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    });
  </script>
</body>
</html>

个人经验: 我刚开始写 Three.js 时,经常忘记加 camera.updateProjectionMatrix()。窗口缩放后画面就变形了。记住:改了相机宽高比,一定要调用这个方法。

运行这段代码,你会看到一个浅蓝色背景的 3D 场景,中间有一个绿色立方体在慢慢旋转。底部还有网格线帮你定位。

你想想看,就这么几行代码,一个 3D 场景就跑起来了。是不是比想象中简单?

1.4 本章小结

今天我们干了三件事:

  1. 认识了 Three.js——一个让 WebGL 变得简单的 3D 库
  2. 理解了三大核心——场景(装东西)、相机(看东西)、渲染器(画东西)
  3. 亲手搭了一个场景——带旋转立方体的 3D 页面

下一章我们会深入聊聊场景里的物体和材质。到时候我会分享一些我在实际项目中踩过的坑,比如材质不显示、模型加载失败之类的。嗯,敬请期待。

公众号:蓝海资料掘金营,微信deep3321