在数字化浪潮下,3D 技术不再局限于游戏与影视 —— 如今,网页端 3D 产品展示、元宇宙场景、数据可视化、AR/VR 交互等应用随处可见,而 WebGL 与 Three.js 正是实现这些 "浏览器端 3D 魔法" 的核心技术。对于开发者而言,掌握 Web3D 技术不仅能拓展业务边界,更能在元宇宙、数字孪生等新兴领域抢占先机。本文将从基础概念到实战项目,带你系统入门 WebGL 与 Three.js,逐步构建属于自己的 Web3D 应用。
一、Web3D 技术基石:理清 WebGL 与 Three.js 的关系
在学习前,必须先明确一个核心问题:WebGL 和 Three.js 到底是什么?两者并非竞争关系,而是 "底层引擎" 与 "上层工具库" 的协同组合,共同降低 Web3D 开发门槛。
(一)WebGL:浏览器渲染 3D 的 "底层引擎"
WebGL(Web Graphics Library)是一种基于 OpenGL ES 2.0 的浏览器原生 API,它允许开发者通过 JavaScript 直接操作 GPU,在网页中渲染 2D/3D 图形,无需依赖插件(如 Flash)。其核心作用是 "将 3D 数据(顶点、纹理、光照)转换为屏幕像素",但直接使用 WebGL 存在显著痛点:
- 语法复杂:需手动编写着色器(Shader)代码(GLSL 语言),处理顶点坐标、纹理映射、光照计算等底层逻辑;
- 冗余代码多:创建一个简单的立方体,需编写数百行代码处理坐标系转换、缓冲区管理;
- 门槛高:需掌握计算机图形学基础(如矩阵变换、透视投影),新手难以快速上手。
例如,直接用 WebGL 绘制一个红色三角形,需编写顶点着色器(定义顶点位置)和片段着色器(定义颜色),再通过 JavaScript 初始化缓冲区、链接着色器程序 —— 整个过程涉及 10 + 步骤,对新手极不友好。
(二)Three.js:简化 Web3D 开发的 "上层工具库"
Three.js 是基于 WebGL 的开源 JavaScript 库,由 Ricardo Cabello(Mr.doob)开发,其核心价值是 "封装 WebGL 底层细节,提供直观的 3D 开发接口"。它就像给 WebGL 套了一层 "友好的外壳",让开发者无需关注着色器、缓冲区等底层逻辑,只需通过简单的 API 即可构建 3D 场景。
Three.js 的核心优势:
- 封装底层复杂度:内置场景、相机、渲染器等核心组件,一行代码即可完成 WebGL 初始化;
- 丰富的 3D 资源:支持几何体(立方体、球体等)、材质(金属、玻璃等)、光源(平行光、点光等)、控制器(旋转、缩放等交互);
- 跨平台兼容性:自动适配不同浏览器与设备,支持响应式 3D 场景;
- 生态完善:配套加载器(加载 GLB/FBX 模型)、动画库(Tween.js)、物理引擎(Cannon.js),满足复杂需求。
简单来说:WebGL 是 "发动机",Three.js 是 "方向盘与油门" —— 新手无需精通发动机原理,只需通过方向盘即可驾驶 3D 应用,这也是我们从 Three.js 入门 Web3D 的核心原因。
二、Web3D 基础:Three.js 核心组件与工作流程
Three.js 的 3D 场景由 "三大核心组件" 与 "辅助组件" 构成,所有 Web3D 应用的开发都围绕这些组件展开。理解它们的作用与关系,是入门的关键。
(一)三大核心组件:场景、相机、渲染器
这三个组件是构建任何 Three.js 应用的 "基石",缺一不可,其关系可类比 "摄影":
- 场景(Scene):相当于 "摄影棚",是所有 3D 物体(模型、光源、相机)的容器;
- 相机(Camera):相当于 "摄像机",决定从哪个角度观察场景;
- 渲染器(Renderer):相当于 "显示器",将相机捕捉到的场景渲染到网页的 DOM 元素(如)中。
1. 场景(Scene)
场景是 3D 物体的 "容器",通过new THREE.Scene()创建,后续所有物体(如立方体、光源)都需通过scene.add(object)添加到场景中。例如:
import * as THREE from 'three';
// 创建场景
const scene = new THREE.Scene();
// 给场景设置背景色(可选)
scene.background = new THREE.Color(0xf0f0f0); // 浅灰色背景
2. 相机(Camera)
Three.js 提供两种常用相机,需根据场景需求选择:
- 透视相机(PerspectiveCamera):模拟人眼视角,近大远小,适合 3D 游戏、产品展示等真实感场景;
- 正交相机(OrthographicCamera):物体大小与距离无关,适合 2.5D 游戏、工程图纸等场景。
透视相机参数解析(最常用):
// 参数:fov(视野角度)、aspect(宽高比)、near(近裁剪面)、far(远裁剪面)
const camera = new THREE.PerspectiveCamera(
75, // 视野角度:越大看到的范围越广
window.innerWidth / window.innerHeight, // 宽高比:通常为窗口宽高比
0.1, // 近裁剪面:距离相机小于0.1的物体不渲染
1000 // 远裁剪面:距离相机大于1000的物体不渲染
);
// 设置相机位置(默认在(0,0,0),需调整位置才能看到场景)
camera.position.z = 5; // 相机向后移动5个单位(Three.js默认坐标系:Z轴向前)
3. 渲染器(Renderer)
渲染器负责将场景与相机结合,渲染到网页的元素中。Three.js 会自动创建,也可指定已有:
// 创建渲染器,开启抗锯齿(使边缘更平滑)
const renderer = new THREE.WebGLRenderer({ antialias: true });
// 设置渲染器尺寸(通常为窗口大小)
renderer.setSize(window.innerWidth, window.innerHeight);
// 将渲染器的DOM元素(<canvas>)添加到页面中
document.body.appendChild(renderer.domElement);
(二)辅助组件:几何体、材质、网格、光源
三大核心组件搭建了 "空舞台",还需添加 "演员"(3D 物体)与 "灯光"(光源)才能构成完整场景:
1. 几何体(Geometry):3D 物体的 "形状"
Three.js 内置多种基础几何体,如立方体(BoxGeometry)、球体(SphereGeometry)、平面(PlaneGeometry)等,也支持加载外部模型(如 GLB/FBX)。例如,创建一个边长为 1 的立方体:
// 参数:宽、高、深
const geometry = new THREE.BoxGeometry(1, 1, 1);
2. 材质(Material):3D 物体的 "外观"
材质决定了物体的颜色、光泽度、透明度等视觉属性,Three.js 提供多种材质类型,适应不同需求:
- MeshBasicMaterial:基础材质,不受光照影响,适合简单物体;
- MeshLambertMaterial:漫反射材质,受光照影响,无高光;
- MeshPhongMaterial:高光材质,有镜面反射效果,适合金属、塑料;
- MeshStandardMaterial:PBR 物理材质,更真实的光照表现,适合产品展示。
例如,创建一个红色、有光泽的材质:
const material = new THREE.MeshPhongMaterial({
color: 0xff0000, // 红色
shininess: 100, // 光泽度
specular: 0x111111 // 高光颜色
});
3. 网格(Mesh):几何体与材质的 "组合"
网格是几何体与材质的结合体,只有创建网格后,才能将 3D 物体添加到场景中:
// 将几何体与材质组合成网格
const cube = new THREE.Mesh(geometry, material);
// 添加到场景
scene.add(cube);
4. 光源(Light):3D 场景的 "照明"
没有光源的场景一片漆黑,Three.js 提供多种光源类型,模拟不同光照效果:
- AmbientLight:环境光,照亮整个场景,无方向性;
- DirectionalLight:平行光,模拟太阳光,有方向性;
- PointLight:点光源,向四周发光,模拟灯泡;
- SpotLight:聚光灯,锥形光束,模拟手电筒。
例如,添加一个白色平行光:
// 创建平行光
const light = new THREE.DirectionalLight(0xffffff, 1);
// 设置光源位置
light.position.set(5, 5, 5);
// 添加到场景
scene.add(light);
(三)完整工作流程:从创建到渲染
结合以上组件,一个基础的 Three.js 应用工作流程如下:
// 1. 导入Three.js
import * as THREE from 'three';
// 2. 创建场景、相机、渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 3. 创建几何体、材质、网格
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshPhongMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 4. 添加光源
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 5);
scene.add(directionalLight);
// 5. 设置相机位置
camera.position.z = 5;
// 6. 创建动画循环
function animate() {
// 请求下一帧动画
requestAnimationFrame(animate);
// 旋转立方体
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
// 渲染场景
renderer.render(scene, camera);
}
// 7. 启动动画
animate();
运行这段代码,你将在浏览器中看到一个旋转的红色立方体 —— 这就是你的第一个 Three.js 3D 应用!
三、实战进阶:构建交互式 3D 场景
掌握基础组件后,我们可以通过添加交互控制器、加载外部模型、应用纹理等方式,构建更复杂的 3D 应用。
(一)添加交互:OrbitControls
OrbitControls 是 Three.js 的官方插件,允许用户通过鼠标拖拽旋转场景、滚轮缩放、Shift+拖拽平移,极大提升交互体验:
// 导入OrbitControls
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
// 创建控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 开启阻尼效果(更平滑)
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// 在动画循环中更新控制器
function animate() {
requestAnimationFrame(animate);
controls.update(); // 必须更新控制器
renderer.render(scene, camera);
}
(二)加载外部模型:GLTFLoader
实际项目中,我们通常使用专业建模软件(如 Blender、Maya)创建 3D 模型,再通过 Three.js 的加载器导入网页。GLTF/GLB 是目前最推荐的模型格式,体积小、加载快:
// 导入GLTFLoader
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
// 创建加载器
const loader = new GLTFLoader();
// 加载模型
loader.load(
// 模型URL
'models/cube.gltf',
// 加载成功回调
(gltf) => {
const model = gltf.scene;
scene.add(model);
},
// 加载进度回调
(xhr) => {
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
},
// 加载错误回调
(error) => {
console.error('An error occurred', error);
}
);
(三)应用纹理:TextureLoader
纹理是贴在 3D 物体表面的图片,能大幅提升视觉真实感。Three.js 的 TextureLoader 可轻松加载图片并应用到材质上:
// 导入TextureLoader
import { TextureLoader } from 'three';
// 创建加载器
const textureLoader = new TextureLoader();
// 加载纹理
const texture = textureLoader.load('textures/wood.jpg');
// 应用到材质
const material = new THREE.MeshStandardMaterial({
map: texture // 基础颜色贴图
});
(四)响应式设计:窗口大小调整
为确保 3D 场景在不同设备上正常显示,需监听窗口大小变化并更新相机与渲染器:
window.addEventListener('resize', () => {
// 更新相机宽高比
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
// 更新渲染器尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
});
四、项目实战:构建 3D 产品展示页
结合以上知识点,我们可以构建一个完整的 3D 产品展示页,实现以下功能:
- 加载并展示 3D 产品模型;
- 支持旋转、缩放等交互;
- 添加环境光与定向光,提升视觉效果;
- 实现响应式设计,适配不同设备。
以下是完整的实现代码:
// 导入所需模块
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
// 创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xf5f5f5);
// 创建相机
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);
renderer.setPixelRatio(window.devicePixelRatio); // 高分辨率屏幕适配
renderer.shadowMap.enabled = true; // 开启阴影
document.getElementById('container').appendChild(renderer.domElement);
// 添加光源
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 5);
directionalLight.castShadow = true; // 光源投射阴影
scene.add(directionalLight);
// 添加地面
const groundGeometry = new THREE.PlaneGeometry(20, 20);
const groundMaterial = new THREE.MeshStandardMaterial({
color: 0xffffff,
roughness: 0.8
});
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2; // 水平放置
ground.position.y = -2;
ground.receiveShadow = true; // 接收阴影
scene.add(ground);
// 添加控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minDistance = 3; // 最小缩放距离
controls.maxDistance = 10; // 最大缩放距离
// 加载3D模型
const loader = new GLTFLoader();
let model;
loader.load(
'models/product.gltf',
(gltf) => {
model = gltf.scene;
// 设置模型阴影
model.traverse((node) => {
if (node.isMesh) {
node.castShadow = true;
node.receiveShadow = true;
}
});
scene.add(model);
},
(xhr) => {
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
},
(error) => {
console.error('An error occurred', error);
}
);
// 窗口大小调整
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// 动画循环
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
五、性能优化:让 3D 应用更流畅
3D 渲染对性能要求较高,特别是在移动设备上。以下是一些关键优化技巧:
(一)减少几何体复杂度
- 使用低多边形(Low-poly)模型,减少顶点数量;
- 对远距离物体使用细节层次(LOD)技术,降低模型精度;
- 合并相同材质的网格,减少绘制调用(Draw Call)。
(二)优化纹理
- 使用压缩纹理格式(如 WebP、KTX2);
- 合理设置纹理尺寸(如 1024×1024、2048×2048),避免过大;
- 使用纹理图集(Texture Atlas),将多个小图合并为一张大图。
(三)光照与阴影优化
- 减少实时光源数量,优先使用烘焙光照贴图;
- 合理设置阴影分辨率和范围;
- 使用环境光遮蔽(AO)贴图增强细节,减少光照计算。
(四)其他优化技巧
- 使用 WebGL 检测,为低性能设备提供降级方案;
- 实现视锥体剔除(Frustum Culling),只渲染相机可见的物体;
- 使用 requestAnimationFrame 同步动画,避免不必要的重绘;
- 考虑使用 WebAssembly 加速复杂计算。
六、学习路径与资源推荐
掌握 Web3D 技术需要持续学习,以下是推荐的学习路径与资源:
(一)入门阶段
- 官方文档:Three.js Documentation(英文)、Three.js 中文文档;
- 视频教程:B 站 "Three.js 零基础入门" 系列;
- 示例学习:Three.js Examples(官方示例集,涵盖各类功能)。
(二)进阶阶段
- 书籍:《WebGL 编程指南》、《Three.js 开发指南》;
- 在线课程:Udemy "Three.js and WebGL Mastery";
- 社区:GitHub Three.js 仓库、Stack Overflow Three.js 标签。
(三)高级阶段
- 图形学基础:学习计算机图形学理论(如《Real-Time Rendering》);
- 着色器编程:学习 GLSL 语言,自定义材质效果;
- 前沿技术:关注 WebXR(VR/AR)、WebGPU 等新技术。
七、总结与展望
WebGL 与 Three.js 为网页带来了无限可能,从简单的 3D 展示到复杂的元宇宙应用,Web3D 技术正在重塑用户的在线体验。作为开发者,掌握这一技术不仅能提升自身竞争力,更能在数字化转型浪潮中把握先机。
未来,随着 5G 普及、硬件性能提升以及 WebGPU 等新技术的成熟,Web3D 应用将更加普及和强大。现在,就从创建你的第一个 Three.js 应用开始,开启 Web3D 开发之旅吧!
— 本文完 —