1. Three.js初探:WebGL与Three.js的关系、Three.js核心概念、第一个3D场景
各位同学,欢迎来到《Three.js场景管理与优化实战》的第一课。
说实话,每次带新人入门Three.js,我都会先问一个问题:你为什么要学Three.js,而不是直接学WebGL?
这个问题背后,其实藏着整个课程最核心的认知。今天我们就从WebGL和Three.js的关系讲起,然后搭建你的第一个3D场景。
1.1 WebGL与Three.js:从底层到上层
WebGL是什么?说白了,它是浏览器里的一把“底层画笔”。它直接跟GPU打交道,能画出点、线、三角形。但问题是——它太底层了。
我刚开始接触WebGL时,想画一个旋转的立方体。你猜怎么着?我需要写几百行代码:创建缓冲区、编写着色器、处理矩阵运算、管理顶点数据……光是把一个三角形画出来,就够我折腾一整天。
Three.js是什么?它是基于WebGL封装的一个“高级画室”。你不需要关心GPU怎么工作,不需要写那些晦涩的着色器代码。你只需要告诉Three.js:“我要一个立方体,放在这里,转起来”,它就帮你搞定了。
核心关系一句话总结:
WebGL是底层API,Three.js是上层框架。Three.js帮你把WebGL的复杂性藏起来了。
举个例子。用原生WebGL画一个三角形,你需要:
- 创建WebGL上下文
- 编写顶点着色器和片元着色器(GLSL语言)
- 创建缓冲区,上传顶点数据
- 编写渲染循环
- 处理视口和投影矩阵
而用Three.js呢?
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// 创建渲染器
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: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
// 渲染循环
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
看到了吗?十几行代码,一个旋转的绿色立方体就出来了。这就是Three.js的价值——它把WebGL的复杂度降低了至少一个数量级。
我的建议:如果你只是想快速做出3D效果,直接学Three.js就够了。但如果你想深入底层,比如做自定义着色器、性能极致优化,那WebGL的知识还是得补上。我个人习惯是:先用Three.js快速出活,遇到性能瓶颈再回头研究WebGL。
1.2 Three.js核心三要素:场景、相机、渲染器
Three.js里有三个东西,你几乎每个项目都会用到。它们就是场景(Scene)、相机(Camera)、渲染器(Renderer)。
我经常跟学员打一个比方:
- 场景就像是一个舞台,所有演员(物体)、灯光、背景都在上面。
- 相机就像是一台摄像机,你通过它来“看”这个舞台。
- 渲染器就像是一个摄影师,它负责把相机看到的画面“拍下来”,然后显示到屏幕上。
咱们一个一个来看。
1.2.1 场景(Scene)
场景是所有3D对象的容器。你创建的每一个物体、每一盏灯光,都要添加到场景里才能被渲染。
const scene = new THREE.Scene();
// 可以设置背景颜色
scene.background = new THREE.Color(0x87ceeb); // 天蓝色
场景本身还可以做一些全局设置,比如雾效、背景图等。这些我们后面章节会详细讲。
1.2.2 相机(Camera)
相机决定了你从哪个角度、以什么方式观察场景。Three.js提供了几种相机,最常用的是透视相机(PerspectiveCamera)和正交相机(OrthographicCamera)。
透视相机模拟人眼效果——近大远小。这是3D场景中最常用的。
const camera = new THREE.PerspectiveCamera(
75, // 视野角度(FOV),类似人眼的视角范围
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近裁剪面
1000 // 远裁剪面
);
这里有几个参数要注意:
- FOV(视野角度):值越大,看到的范围越广,但物体显得越小。一般用45-75度。
- 宽高比:通常设为窗口的宽高比,否则画面会拉伸。
- 近/远裁剪面:只有在这个范围内的物体才会被渲染。太近或太远的物体会被“裁剪”掉。
避坑指南:我曾经遇到过一个项目,场景里明明有物体,但就是看不到。排查了半天,发现是相机的位置设错了——物体在相机后面。记住:相机默认看向Z轴负方向,所以物体要放在Z轴负方向,或者调整相机的位置和朝向。
1.2.3 渲染器(Renderer)
渲染器负责把场景和相机结合起来,生成最终的图像。最常用的是WebGL渲染器。
const renderer = new THREE.WebGLRenderer({
antialias: true // 开启抗锯齿,让边缘更平滑
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
渲染器的domElement是一个canvas元素,它会被添加到页面中。所有3D画面都绘制在这个canvas上。
1.3 第一个3D场景:从零到一
好了,理论讲完了。咱们来动手搭建第一个3D场景。我会带着你一步步走,确保你能跑起来。
第一步:引入Three.js
你可以用CDN引入,也可以用npm安装。这里我用CDN,方便你快速测试。
<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>
第二步:创建核心三要素
// 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.set(0, 2, 5); // 把相机往后拉,并抬高一点
camera.lookAt(0, 0, 0); // 让相机看向原点
// 3. 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
第三步:添加一个物体
我们来创建一个旋转的环面结(TorusKnot),它比立方体更有视觉冲击力。
// 创建几何体
const geometry = new THREE.TorusKnotGeometry(1, 0.3, 100, 16);
// 创建材质
const material = new THREE.MeshStandardMaterial({
color: 0xff6b6b,
roughness: 0.3,
metalness: 0.7
});
// 组合成网格对象
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// 添加灯光(MeshStandardMaterial需要灯光才能显示)
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5, 5, 5);
scene.add(light);
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
第四步:渲染循环
function animate() {
requestAnimationFrame(animate);
// 让物体旋转
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render(scene, camera);
}
animate();
第五步:处理窗口变化
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
完整代码整合:把上面几段代码按顺序拼在一起,你就得到了第一个3D场景。一个旋转的、金属质感的环面结,在深色背景中缓缓转动。
1.4 本章小结
今天我们做了三件事:
- 理清了WebGL和Three.js的关系——Three.js是WebGL的上层封装,让你用更少的代码做更多的事。
- 掌握了Three.js的三大核心概念——场景是舞台,相机是眼睛,渲染器是摄影师。
- 亲手搭建了第一个3D场景——从引入库到渲染循环,完整走了一遍流程。
嗯,这里我要多说一句。很多初学者觉得Three.js入门简单,就跳过基础直接做项目。结果遇到问题完全不知道怎么排查。我的建议是:把今天这三个核心概念吃透,尤其是相机和渲染器的参数。后面所有复杂的场景,都是在这三个东西的基础上搭建的。
下一章,我们会深入讲解几何体与材质,教你如何创建各种形状,并给它们穿上漂亮的“外衣”。到时候见!