three.js基础入门(一)
入门(three.js使用、相机设置、材质定义、光照设置、自定义geometry等,大概分成三章)
进阶(shader学习、自定义材质、部分源码阅读,大概两三章)
如果还有想要了解three.js其他相关的内容,欢迎留言补充
一、Three.js 是什么?
Three.js 是一个基于 WebGL 的 JavaScript 3D 库。
WebGL 本身功能强大但复杂,即使一个简单的绘制三角形都需要很多代码;Three.js 把这个常用的三维渲染需要用到的能力进行了封装,简单调用组合就能快速实现 3D 场景。
为什么选择 Three.js?
- 门槛低:不懂 WebGL 也能上手
- 功能全:支持光照、阴影、动画等
- 社区活跃:教程和案例非常多
- 跨平台:只要是浏览器,就能运行
它被广泛用于产品展示、游戏、可视化等场景
二、Hello World 示例
下面我们写一个最简单的 three.js 程序,在网页上可以看到一个旋转的立方体。
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Three.js Hello World</title>
<style>body { margin: 0; }</style>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"></script>
<script>
// 创建一个场景(Scene)
const scene = new THREE.Scene();
// 创建一个相机(Camera)
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
//创建一个渲染器(Renderer)
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建一个立方体(Box)
const geometry = new THREE.BoxGeometry();
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();
</script>
</body>
</html>
三、材质类型
在现实中,砖和玻璃就算形状一样,视觉效果也完全不同。在 Three.js 中,这种视觉差异由“材质(Material)”控制。
常用材质介绍:
材质名 | 是否受光照影响 | 简介 |
---|---|---|
MeshBasicMaterial |
❌ 否 | 最简单,不受光影响,常用于调试 |
MeshStandardMaterial |
✅ 是 | 现代物理材质,效果真实,推荐使用 |
MeshPhongMaterial |
✅ 是 | 支持高光、光照,但不如 Standard 拟真 |
MeshLambertMaterial |
✅ 是 | 柔和漫反射,适合基础物体 |
MeshNormalMaterial |
❌ 否 | 表面法线可视化,常用于开发调试 |
如果场景中物体需要考虑光照影响推荐MeshStandardMaterial
,MeshBasicMaterial
材质更简单,但是因为不受光看起来很平
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>body { margin: 0; }</style>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"></script>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建两种材质的球体,左边为 MeshBasicMaterial(不受光照),右边为 MeshStandardMaterial(受光照)
const geometry = new THREE.SphereGeometry(1, 32, 32);
const basicMat = new THREE.MeshBasicMaterial({ color: 0x44aaff});
const standardMat = new THREE.MeshStandardMaterial({ color: 0x44aaff, metalness: 0.7, roughness: 0.3 });
const basicSphere = new THREE.Mesh(geometry, basicMat);
const standardSphere = new THREE.Mesh(geometry, standardMat);
basicSphere.position.x = -1.5;
standardSphere.position.x = 1.5;
scene.add(basicSphere);
scene.add(standardSphere);
// 平行光
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5, 5, 5);
scene.add(light);
// 环境光
const ambient = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambient);
function animate() {
requestAnimationFrame(animate);
basicSphere.rotation.y += 0.01;
standardSphere.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
</script>
</body>
</html>
效果是不是差异很大,需要注意的是使用
MeshStandardMaterial
一定要添加光照(重要的事情说三遍),上面的代码添加了平行光DirectionalLight
和环境光AmbientLight
,如果不添加光照的话就是漆黑一片
不加光照效果
示例代码:
const material = new THREE.MeshStandardMaterial({
color: 0xff0000,
roughness: 0.5,
metalness: 0.8
});
四、Geometry与 Mesh
几何体相当于一个 3D 模型的“骨架”,只负责定义形状,不负责外观材质。Three.js 提供了多个内置几何体类,常见包括:
常用几何体:
new THREE.BoxGeometry() // 立方体
new THREE.SphereGeometry() // 球体
new THREE.PlaneGeometry() // 平面
new THREE.ConeGeometry() // 圆锥
new THREE.TorusGeometry() // 圆环
几何体定义形状,但还不能被渲染。只有当几何体与材质(Material)结合,才能生成真正可见的 3D 物体,这个组合称为 Mesh
:
组合方式:几何体 + 材质 = 网格(Mesh)
const geometry = new THREE.SphereGeometry(1, 32, 32);
const material = new THREE.MeshStandardMaterial({ color: 0x00ffff });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
用 scene.add(mesh)
把它们添加到场景中
Geometry 就像“模具”,Material 就像“颜料”,Mesh 则是已经上色后的成品
BufferGeometry
Three.js 中的大多数几何体都继承自 BufferGeometry
。
// 自定义 BufferGeometry
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0,
0.0, 1.0, 0.0
]);
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
关于自定义geometry后续可以详细介绍,我们会自己定义一些顶点来绘制three.js默认不支持的图形
五、相机与光源
即使你在网页上放了 100 个模型,没有相机和光,啥也看不到,
相机类型:
-
PerspectiveCamera
(透视相机)——真实世界的视角,近大远小,是最常用的 3D 相机 -
OrthographicCamera
(正交相机)——常用于 2D,平行投影,没有透视效果
// 创建透视相机
const perspectiveCamera = new THREE.PerspectiveCamera(
75, window.innerWidth / window.innerHeight, 0.1, 1000
);
perspectiveCamera.position.set(0, 0, 5);
// 创建正交相机
const aspect = window.innerWidth / window.innerHeight;
const orthoCamera = new THREE.OrthographicCamera(
-aspect * 5, aspect * 5, 5, -5, 0.1, 1000
);
orthoCamera.position.set(0, 0, 5);
camera.position.z = 5;
光源种类:
光源类型 | 说明 |
---|---|
AmbientLight |
环境光,提供均匀照明 |
DirectionalLight |
平行光,如太阳光 |
PointLight |
点光源,如灯泡 |
SpotLight |
聚光灯,可以打出圆锥区域 |
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5, 5, 5);
scene.add(light);
示例代码: 这个示例中用了AmbientLight、DirectionalLight和PointLight,我还加了一个gui面板可以控制光线,可以粘贴代码跑一下,体验光照对场景的影响
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>body { margin: 0; }</style>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dat.gui@0.7.9/build/dat.gui.min.js"></script>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(4, 3, 6);
camera.lookAt(0, 1, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
const sphereGeo = new THREE.SphereGeometry(1, 64, 64);
const sphereMat = new THREE.MeshStandardMaterial({ color: 0x66ccff, metalness: 0.5, roughness: 0.4 });
const sphere = new THREE.Mesh(sphereGeo, sphereMat);
sphere.castShadow = true;
sphere.position.y = 1;
scene.add(sphere);
const planeGeo = new THREE.PlaneGeometry(10, 10);
const planeMat = new THREE.MeshStandardMaterial({ color: 0xeeeeee });
const plane = new THREE.Mesh(planeGeo, planeMat);
plane.rotation.x = -Math.PI / 2;
plane.receiveShadow = true;
scene.add(plane);
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 5, 5);
dirLight.castShadow = true;
scene.add(dirLight);
const pointLight = new THREE.PointLight(0xffaa00, 1, 15);
pointLight.position.set(-3, 2, 2);
pointLight.castShadow = true;
scene.add(pointLight);
// gui控制面板
const gui = new dat.GUI();
const ambientFolder = gui.addFolder('Ambient Light');
ambientFolder.add(ambientLight, 'visible').name('开启');
ambientFolder.add(ambientLight, 'intensity', 0, 1, 0.01).name('强度');
const dirFolder = gui.addFolder('Directional Light');
dirFolder.add(dirLight, 'visible').name('开启');
dirFolder.add(dirLight, 'intensity', 0, 2, 0.01).name('强度');
dirFolder.add(dirLight.position, 'x', -10, 10).name('位置X');
dirFolder.add(dirLight.position, 'y', -10, 10).name('位置Y');
dirFolder.add(dirLight.position, 'z', -10, 10).name('位置Z');
const pointFolder = gui.addFolder('Point Light');
pointFolder.add(pointLight, 'visible').name('开启');
pointFolder.add(pointLight, 'intensity', 0, 2, 0.01).name('强度');
pointFolder.add(pointLight.position, 'x', -10, 10).name('位置X');
pointFolder.add(pointLight.position, 'y', -10, 10).name('位置Y');
pointFolder.add(pointLight.position, 'z', -10, 10).name('位置Z');
ambientFolder.open();
dirFolder.open();
pointFolder.open();
function animate() {
requestAnimationFrame(animate);
sphere.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
</script>
</body>
</html>
下一章
- 加载 3D 模型(GLTF)
- 添加 OrbitControls 实现相机交互
- 学会使用动画系统
- 尝试后处理(模糊、发光、景深等)
- 欢迎留言补充