Three.js 入门:在浏览器里构建你的第一个 3D 场景

Three.js 是一个基于 WebGL 的 JavaScript 3D 图形库。它把复杂的底层 GPU 编程封装成简洁的 API,让前端开发者也能轻松创建 3D 场景,而不需要掌握晦涩的图形学知识。


一、Three.js 能做什么?

  • 数字孪生:工厂、城市、机房的三维可视化
  • 产品展示:360° 旋转查看商品
  • 数据大屏:三维地图、粒子效果
  • Web 游戏:轻量级 3D 游戏
  • 建筑可视化:BIM 模型在线浏览
  • 科学仿真:卫星轨道、导航轨迹展示

二、核心概念:3D 场景的组成

在写代码之前,先理解 Three.js 的三个核心概念:

场景(Scene)

相当于摄影棚,所有东西都放在这里

场景是一个容器,3D 物体、光源等都需要被添加到场景中才能被渲染。

相机(Camera)

相当于摄像机,决定你从哪个角度看场景

最常用的是透视相机(PerspectiveCamera),模拟人眼的透视效果(近大远小)。参数包括视角(FOV)、宽高比、近裁剪面、远裁剪面。

渲染器(Renderer)

相当于后期制作,把场景和相机的信息输出成画面

WebGLRenderer 负责把 3D 场景渲染到 HTML 的 <canvas> 元素上,最终显示在页面中。


三、搭建第一个 3D 场景

我们来渲染一个旋转的绿色立方体,这是 Three.js 的 Hello World。

第一步:引入 Three.js

可以通过 CDN 引入:

复制代码
<!DOCTYPE html>
<html>
<head>
  <style>
    body { margin: 0; }
    canvas { display: block; }
  </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';
    // 后续代码写在这里
  </script>
</body>
</html>

第二步:创建场景、相机、渲染器

复制代码
// 1. 创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);  // 深蓝色背景

// 2. 创建透视相机
// 参数:视角(度), 宽高比, 近裁剪面, 远裁剪面
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;  // 相机往后退 5 个单位,才能看到原点处的物体

// 3. 创建渲染器,挂载到页面
const renderer = new THREE.WebGLRenderer({ antialias: true });  // antialias: 抗锯齿
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);  // 把 canvas 添加到页面

第三步:创建几何体

Three.js 中一个可见的物体由两部分组成:

  • Geometry(几何体):定义形状(顶点、面)
  • Material(材质):定义外观(颜色、贴图、光照反应)

两者结合,创建 Mesh(网格体) 并添加到场景:

复制代码
// 创建一个 1x1x1 的立方体
const geometry = new THREE.BoxGeometry(1, 1, 1);

// 创建材质:MeshBasicMaterial 不受光照影响,颜色始终可见
const material = new THREE.MeshBasicMaterial({ color: 0x00ff88 });

// 合并为网格体
const cube = new THREE.Mesh(geometry, material);

// 添加到场景
scene.add(cube);

第四步:动画循环

Three.js 的动画原理是利用浏览器的 requestAnimationFrame,每帧更新物体状态并重新渲染:

复制代码
function animate() {
  requestAnimationFrame(animate);  // 告诉浏览器下一帧继续调用 animate

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

  // 渲染这一帧
  renderer.render(scene, camera);
}

animate();  // 启动动画循环

到这里,你应该能看到一个旋转的绿色立方体了。


四、让场景更真实:加入光照

MeshBasicMaterial 不受光照影响,永远是纯色。换成 MeshStandardMaterial,加上灯光,立方体就有了立体感:

复制代码
// 换成标准材质(受光照影响)
const material = new THREE.MeshStandardMaterial({ color: 0x00ff88 });

// 添加环境光(均匀照亮整个场景,防止背光面全黑)
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);  // 颜色, 强度
scene.add(ambientLight);

// 添加方向光(模拟太阳光,有方向,会产生阴影)
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 5);  // 光源位置
scene.add(directionalLight);

五、常用几何体速览

Three.js 内置了很多几何体,直接拿来用:

复制代码
// 球体:半径, 水平分段数, 垂直分段数(数值越大越圆滑)
const sphere = new THREE.SphereGeometry(1, 32, 32);

// 圆柱体:顶部半径, 底部半径, 高度, 分段数
const cylinder = new THREE.CylinderGeometry(0.5, 0.5, 2, 32);

// 平面:宽, 高
const plane = new THREE.PlaneGeometry(10, 10);

// 圆环体(甜甜圈形):主半径, 管道半径
const torus = new THREE.TorusGeometry(1, 0.3, 16, 100);

六、鼠标控制旋转:OrbitControls

静态场景加上 OrbitControls,用户可以用鼠标旋转、缩放、平移视角:

复制代码
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;  // 惯性阻尼,松开鼠标后慢慢停下

// 在动画循环里更新控制器
function animate() {
  requestAnimationFrame(animate);
  controls.update();  // 必须在每帧调用
  renderer.render(scene, camera);
}

七、完整代码汇总

复制代码
<!DOCTYPE html>
<html>
<head>
  <style>body { margin: 0; } canvas { display: block; }</style>
</head>
<body>
<script type="importmap">
  { "imports": { 
    "three": "https://unpkg.com/three@0.160.0/build/three.module.js",
    "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
  }}
</script>
<script type="module">
  import * as THREE from 'three';
  import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

  const scene = new THREE.Scene();
  scene.background = new THREE.Color(0x1a1a2e);

  const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  camera.position.z = 5;

  const renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  // 立方体
  const cube = new THREE.Mesh(
    new THREE.BoxGeometry(1, 1, 1),
    new THREE.MeshStandardMaterial({ color: 0x00ff88 })
  );
  scene.add(cube);

  // 灯光
  scene.add(new THREE.AmbientLight(0xffffff, 0.5));
  const light = new THREE.DirectionalLight(0xffffff, 1);
  light.position.set(5, 5, 5);
  scene.add(light);

  // 鼠标控制
  const controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;

  // 动画循环
  function animate() {
    requestAnimationFrame(animate);
    cube.rotation.x += 0.005;
    cube.rotation.y += 0.005;
    controls.update();
    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>

总结

概念 作用 类比
Scene 场景容器 摄影棚
Camera 观察视角 摄像机
Renderer 渲染输出 后期制作
Geometry 几何形状 骨架
Material 外观材质 皮肤/涂料
Mesh 可见物体 骨架+皮肤的组合
Light 光源 灯光
相关推荐
超人气王1 小时前
JavaScript新手基础入门——this指针指向,一文带你搞清楚
前端·javascript
z落落1 小时前
C# 数组属性和方法(Clear / Copy / IndexOf / LastIndexOf)
开发语言·javascript·c#
嘟嘟07171 小时前
Python切片技巧×DeepSeek API:手把手教你打造智能商品文案生成器
前端·javascript
月月大王的3D日记1 小时前
Three.js Day 4:材质初探(上)——Basic、Normal、Matcap、Depth 一口气认识四种
javascript
来自上海的这位朋友1 小时前
浏览器里的实时对局同步:WildHunt 的 WebSocket、输入序号与服务端快照
前端·javascript·后端
遇事不決洛必達1 小时前
【爬虫随笔】常见js混淆原理和特征
javascript·爬虫·逆向·js加密
吃炸鸡的前端2 小时前
react-hook-from从入门到精通
前端·javascript·react.js
zzqssliu2 小时前
跨境独立站多端适配开发:多语言+多货币+跨平台同步技术实战
前端·javascript·php
Chengbei112 小时前
AI赋能Chrome MCP × JS逆向Skill自动化JS逆向助力挖洞与绕过实战(小白也能学会)
javascript·人工智能·chrome·网络安全·自动化·系统安全·安全架构