Three.js学习
简介
Three.js 是一个基于 WebGL 的 JavaScript 3D 库,它为开发者提供了一系列易于使用的 API,让开发者能够在网页上轻松创建和展示复杂的 3D 场景、动画和交互效果,而无需深入了解 WebGL 的底层细节。下面将从其特点、应用场景和基本原理等方面详细介绍 Three.js。
特点
- 简单易用:Three.js 对 WebGL 复杂的底层操作进行了封装,提供了直观且易于理解的 API,降低了开发 3D 网页应用的门槛。例如,创建一个简单的 3D 立方体,只需要几行代码:
import * as THREE from 'three';
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建立方体
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 渲染循环
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
- 功能强大:Three.js 支持多种几何体(如立方体、球体、平面等)、材质(如基础材质、标准材质、纹理材质等)、光照(如环境光、平行光、点光源等)、动画(如关键帧动画、Tween 动画等)以及模型加载(如 GLTF、OBJ 等格式)。
- 跨平台兼容:由于基于 WebGL 技术,Three.js 可以在支持 WebGL 的现代浏览器(如 Chrome、Firefox、Safari 等)中运行,无需额外的插件。
应用场景
- 游戏开发:可以利用 Three.js 创建各种类型的网页游戏,如角色扮演游戏、射击游戏、益智游戏等。通过创建 3D 场景、角色和道具,以及实现动画和交互效果,为玩家带来沉浸式的游戏体验。
- 数据可视化:将数据以 3D 形式展示,使数据更加直观和易于理解。例如,展示地理信息、科学数据、商业数据等。
- 虚拟现实(VR)和增强现实(AR):结合 VR 和 AR 设备,Three.js 可以创建沉浸式的虚拟和增强现实体验。例如,在网页上实现 3D 虚拟展厅、AR 购物等应用。
- 建筑和室内设计:创建 3D 建筑模型和室内场景,让客户能够提前预览设计效果,进行交互式的漫游和体验。
基本原理
Three.js 的核心是场景(Scene
)、相机(Camera
)和渲染器(Renderer
)。
- 场景(
Scene
):是所有 3D 对象的容器,就像一个舞台,所有的物体、灯光等都将放置在这个舞台上。 - 相机(
Camera
):决定了我们从哪个角度观察场景。Three.js 提供了多种类型的相机,如透视相机(PerspectiveCamera
)和正交相机(OrthographicCamera
)。透视相机模拟了人眼的视觉效果,有近大远小的特点;正交相机则没有透视效果,物体的大小不会随距离变化。通过new THREE.PerspectiveCamera()
创建透视相机。 - 渲染器(
Renderer
):负责将场景和相机的内容渲染到网页上的画布中。Three.js 提供了多种渲染器,如 WebGL 渲染器(WebGLRenderer
)和 CSS3D 渲染器(CSS3DRenderer
)。通过new THREE.WebGLRenderer()
创建 WebGL 渲染器。
通过组合场景、相机和渲染器,以及添加各种几何体、材质、光照和动画,就可以创建出丰富多彩的 3D 网页应用。
1. 基础设置与初始化
1.1 场景(THREE.Scene
)
场景是 Three.js 中所有对象的容器,就像一个舞台,所有的物体、灯光等都将放置在这个舞台上。
import * as THREE from 'three';
const scene = new THREE.Scene();
这样创建一个场景并导出供其他模块使用。
1.2 相机(THREE.PerspectiveCamera
)
相机决定了我们从哪个角度观察场景。PerspectiveCamera
是透视相机,它模拟了人眼的视觉效果,有近大远小的特点。
// 主相机
const camera = new THREE.PerspectiveCamera(
45, // 视角大小
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近裁剪面
1000 // 远裁剪面
);
camera.position.set(0, 0, 5); // 设置相机位置
封装创建相机的函数:
const defaultOptions = {
fov: 45,
aspect: window.innerWidth / window.innerHeight,
near: 0.1,
far: 1000,
position: [0, 0, 5]
};
const createCamera = (options = {}) => {
const cameraOpts = Object.assign(defaultOptions, options);
const camera = new THREE.PerspectiveCamera(...Object.values(cameraOpts));
camera.position.set(...cameraOpts.position);
return camera;
};
export default createCamera;
1.3 渲染器(THREE.WebGLRenderer
)
渲染器负责将场景和相机的内容渲染到网页上的画布中。
class Renderer extends THREE.WebGLRenderer {
constructor(scene, camera) {
super({ antialias: true }); // 开启抗锯齿
this.scene = scene;
this.camera = camera;
this.init();
}
init() {
this.setSize(window.innerWidth, window.innerHeight); // 设置渲染器大小
document.body.appendChild(this.domElement); // 将渲染器的DOM元素添加到页面中
this.render(this.scene, this.camera); // 渲染场景
}
// 创建动画函数
animation(cb) {
if (typeof cb!== 'function') {
console.error('animation callback必须是一个函数');
return;
}
this.setAnimationLoop(cb); // 设置动画循环
}
}
export default Renderer;
2. 几何体与网格
2.1 几何体(THREE.BoxGeometry
、THREE.PlaneGeometry
等)
几何体定义了物体的形状,例如立方体、平面、球体等。
// 创建立方体几何体
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
// 创建平面几何体
const planeGeometry = new THREE.PlaneGeometry(30, 30);
2.2 材质(THREE.MeshBasicMaterial
、THREE.MeshStandardMaterial
等)
材质决定了物体的外观,如颜色、光泽等。
// 基础网格材质,不受光照影响
const basicMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
// 标准网格材质,受光照影响
const standardMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff });
不同材质的使用:
let material;
switch (val) {
case 'basic': // 基础网格材质(不受光照影响)
material = new THREE.MeshBasicMaterial({
color: materialParam.color,
wireframe: materialParam.wireframe
});
break;
case 'normal': // 法向可视化材质
material = new THREE.MeshNormalMaterial();
break;
case 'lambert': // Lambert漫反射材质(适合哑光表面)
material = new THREE.MeshLambertMaterial({
color: materialParam.color
});
break;
case 'phong': // Phong高光材质(适合光滑表面)
material = new THREE.MeshPhongMaterial({
color: materialParam.color,
shininess: materialParam.shininess
});
break;
default:
console.warn('未知材质类型');
break;
}
2.3 网格(THREE.Mesh
)
网格是几何体和材质的组合,将形状和外观结合在一起形成一个可渲染的物体。
const mesh = new THREE.Mesh(cubeGeometry, basicMaterial);
scene.add(mesh); // 将网格添加到场景中
3. 光照
3.1 环境光(THREE.AmbientLight
)
环境光会均匀照亮场景中的所有物体,没有特定的方向。
const ambientLight = new THREE.AmbientLight(0xffffff, 0.2); // 颜色为白色,强度为0.2
scene.add(ambientLight);
3.2 平行光(THREE.DirectionalLight
)
平行光从一个特定的方向照射物体,就像太阳光一样。
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(3, 3, 3); // 设置光源位置
scene.add(directionalLight);
如何设置平行光的阴影:
directionalLight.castShadow = true; // 开启平行光投射阴影
plane.receiveShadow = true; // 平面接收阴影
renderer.shadowMap.enabled = true; // 开启渲染器的阴影映射
4. 辅助工具
4.1 坐标轴辅助工具(THREE.AxesHelper
)
坐标轴辅助工具用于可视化场景中的坐标轴,方便我们确定方向。
const axesHelper = new THREE.AxesHelper(10); // 长度为10
scene.add(axesHelper);
4.2 网格辅助工具(THREE.GridHelper
)
网格辅助工具用于可视化场景中的网格,帮助我们确定物体的位置。
const gridHelper = new THREE.GridHelper(20, 20); // 大小为20,分割为20份
scene.add(gridHelper);
4.3 轨道控制器(OrbitControls
)
轨道控制器允许用户通过鼠标交互来控制相机的视角,实现旋转、缩放和平移。
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const controls = new OrbitControls(camera, renderer.domElement);
封装辅助工具类:
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
class Helper {
constructor(options = {}, camera, renderer, scene) {
const defaultOpts = {
OrbitControls: true,
axesHelper: { size: 10 },
gridHelper: { size: 20, division: 20 }
};
this.options = Object.assign(options, defaultOpts);
this.camera = camera;
this.renderer = renderer;
this.scene = scene;
this.init();
}
init() {
const { OrbitControls, axesHelper, gridHelper } = this.options;
OrbitControls && this.initOribit();
axesHelper && this.initAxesHelper(axesHelper);
gridHelper && this.initGrid(gridHelper);
}
initOribit() {
this.oribitControl = new OrbitControls(this.camera, this.renderer.domElement);
}
initAxesHelper(option) {
const { size } = option;
const axesHelper = new THREE.AxesHelper(size);
this.scene.add(axesHelper);
}
initGrid(option) {
const { size, division } = option;
const grid = new THREE.GridHelper(size, division);
this.scene.add(grid);
}
}
export default Helper;
5. 纹理与材质
5.1 纹理加载(THREE.TextureLoader
)
纹理加载器用于加载图像纹理,将其应用到物体的材质上。
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('/src/assets/texture/地球.jpeg');
const material = new THREE.MeshStandardMaterial({ map: texture });
纹理的使用:
const texture = new THREE.TextureLoader().load('/src/assets/texture/地球.jpeg');
const textureAlpha = new THREE.TextureLoader().load('/src/assets/texture/world_normal.jpg');
shpere.material = new THREE.MeshStandardMaterial({
map: texture,
normalMap: textureAlpha
});
6. 模型加载
6.1 GLTF 模型加载(GLTFLoader
)
GLTFLoader 用于加载 GLTF 或 GLB 格式的 3D 模型。
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
const gltfLoader = new GLTFLoader();
const darcoLoader = new DRACOLoader();
darcoLoader.setDecoderPath('/node_modules/three/examples/jsm/libs/draco/');
gltfLoader.setDRACOLoader(darcoLoader);
gltfLoader.load('/src/assets/models/gltf/ferrari.glb', (gltf) => {
const model = gltf.scene;
model.traverse(mesh => {
if (mesh.isMesh) {
mesh.castShadow = true;
}
});
scene.add(model);
});
6.2 OBJ 模型加载(OBJLoader
和MTLLoader
)
OBJLoader 用于加载 OBJ 格式的 3D 模型,MTLLoader 用于加载对应的材质文件。
import { MTLLoader } from 'three/addons/loaders/MTLLoader';
import { OBJLoader } from 'three/addons/loaders/OBJLoader';
const mtlLoader = new MTLLoader();
const basePath = '/src/assets/models/obj/';
mtlLoader.setPath(basePath);
mtlLoader.load('ambulance.mtl', (material) => {
material.preload();
const objLoader = new OBJLoader();
objLoader.setMaterials(material);
objLoader.load(`${basePath}ambulance.obj`, (object) => {
object.traverse(mesh => {
if (mesh.isMesh) {
mesh.castShadow = true;
}
});
scene.add(object);
});
});
7. 动画
7.1 关键帧动画(THREE.KeyframeTrack
、THREE.AnimationClip
、THREE.AnimationMixer
)
关键帧动画允许我们通过定义关键帧来创建物体的动画效果。
// 1. 创建关键帧数据
const times = [0, 4, 8];
const positions = [0, 0, 0, 3, 4, 5, 0, 0, 0];
const kf = new THREE.KeyframeTrack('box.position', times, positions);
// 2. 创建剪辑对象clip
const clip = new THREE.AnimationClip('moving_clip', 8, [kf]);
// 3. 创建一个播放器mixer
const mixer = new THREE.AnimationMixer(cube);
const action = mixer.clipAction(clip);
action.play();
action.loop = THREE.LoopRepeat;
const clock = new THREE.Clock();
renderer.animation(() => {
if (mixer) {
mixer.update(clock.getDelta());
}
renderer.render(scene, camera);
});