9.3案例分析 Three.js 案例分析:场景漫游、模型交互与性能优化 9.3.1 场景漫游案例分析 场景漫游是 Three.js 中常见的应用场景,它允许用户在三维空间中自由探索。一个好的场景漫游体验需要流畅的移动、自然的视角控制以及清晰的场景感知。 案例目标: 创建一个简单的室内场景,并实现第一人称漫游。 技术栈: Three.js, OrbitControls 或 FirstPersonControls。 1. 场景搭建: 首先,我们需要搭建一个简单的室内场景,例如一个房间,可以使用 创建墙壁、地板和天花板。 2. 添加漫游控制: 这里我们可以选择使用 或 。 适用于围绕场景旋转视角,而 更适合第一人称漫游。
场景漫游是 Three.js 中常见的应用场景,它允许用户在三维空间中自由探索。一个好的场景漫游体验需要流畅的移动、自然的视角控制以及清晰的场景感知。
案例目标: 创建一个简单的室内场景,并实现第一人称漫游。
技术栈: Three.js, OrbitControls 或 FirstPersonControls。
1. 场景搭建:
首先,我们需要搭建一个简单的室内场景,例如一个房间,可以使用 BoxGeometry 创建墙壁、地板和天花板。
import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; // 初始化场景、相机和渲染器 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 roomWidth = 10; const roomHeight = 5; const roomDepth = 10; // 创建材质 const wallMaterial = new THREE.MeshBasicMaterial({ color: 0x808080, side: THREE.BackSide }); // BackSide 使材质从内侧可见 const floorMaterial = new THREE.MeshBasicMaterial({ color: 0xAAAAAA }); // 创建几何体 const wallGeometry = new THREE.BoxGeometry(roomWidth, roomHeight, 0.1); const floorGeometry = new THREE.BoxGeometry(roomWidth, 0.1, roomDepth); // 创建网格 const wall1 = new THREE.Mesh(wallGeometry, wallMaterial); wall1.position.set(0, roomHeight / 2, -roomDepth / 2); scene.add(wall1); const wall2 = new THREE.Mesh(wallGeometry, wallMaterial); wall2.position.set(0, roomHeight / 2, roomDepth / 2); scene.add(wall2); const wall3 = new THREE.Mesh(new THREE.BoxGeometry(0.1, roomHeight, roomDepth), wallMaterial); wall3.position.set(-roomWidth / 2, roomHeight / 2, 0); scene.add(wall3); const wall4 = new THREE.Mesh(new THREE.BoxGeometry(0.1, roomHeight, roomDepth), wallMaterial); wall4.position.set(roomWidth / 2, roomHeight / 2, 0); scene.add(wall4); const floor = new THREE.Mesh(floorGeometry, floorMaterial); floor.position.set(0, 0, 0); scene.add(floor); // 设置相机初始位置 camera.position.set(0, 1.6, 0); // 模拟人眼高度 camera.lookAt(0, 1.6, -1); // 添加环境光 const ambientLight = new THREE.AmbientLight(0x404040); // 柔和的环境光 scene.add(ambientLight); // 添加平行光 const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5); directionalLight.position.set(1, 1, 1); scene.add(directionalLight);
2. 添加漫游控制:
这里我们可以选择使用 OrbitControls 或 FirstPersonControls。 OrbitControls 适用于围绕场景旋转视角,而 FirstPersonControls 更适合第一人称漫游。
使用 OrbitControls:
const controls = new OrbitControls(camera, renderer.domElement); controls.enableDamping = true; // 启用阻尼,使旋转更平滑 controls.dampingFactor = 0.05; controls.screenSpacePanning = false; // 禁用屏幕空间平移 controls.minDistance = 1; // 最小缩放距离 controls.maxDistance = 500; // 最大缩放距离
使用 FirstPersonControls:
首先需要引入 FirstPersonControls.js。
<script src="js/FirstPersonControls.js"></script>
然后进行初始化:
import { FirstPersonControls } from 'three/examples/jsm/controls/FirstPersonControls'; const controls = new FirstPersonControls(camera, renderer.domElement); controls.lookSpeed = 0.1; // 视角旋转速度 controls.movementSpeed = 5; // 移动速度 controls.noFly = true; // 禁止飞行 controls.lookVertical = true; // 允许垂直方向观察 controls.constrainVertical = true; // 限制垂直视角 controls.verticalMin = 1.0; // 最小垂直角度 controls.verticalMax = 2.0; // 最大垂直角度
3. 渲染循环:
在渲染循环中,我们需要更新控制器。
function animate() { requestAnimationFrame(animate); controls.update(); // 更新控制器 renderer.render(scene, camera); } animate();
4. 优化建议:
碰撞检测: 为了防止穿墙,可以添加碰撞检测,例如使用 THREE.Raycaster。
加载模型: 可以使用 GLTFLoader 或 OBJLoader 加载更复杂的室内模型。
光照和阴影: 添加更真实的光照和阴影,例如使用 THREE.DirectionalLight 并启用阴影。
Graph TD 图:场景漫游流程
模型交互允许用户与场景中的模型进行互动,例如点击、拖拽、旋转等。
案例目标: 创建一个场景,包含一个可点击的立方体,点击后立方体颜色改变。
技术栈: Three.js, Raycaster。
1. 场景搭建:
import * as THREE from 'three'; // 初始化场景、相机和渲染器 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(); const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); const cube = new THREE.Mesh(geometry, material); scene.add(cube); camera.position.z = 5;
2. 添加 Raycaster:
Raycaster 用于检测鼠标点击与场景中物体的相交。
const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); function onMouseClick(event) { // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1) mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; // 通过相机和鼠标位置更新射线 raycaster.setFromCamera(mouse, camera); // 计算物体和射线的焦点 const intersects = raycaster.intersectObjects(scene.children); if (intersects.length > 0) { // 如果点击到物体,则改变颜色 intersects[0].object.material.color.set(Math.random() * 0xffffff); } } window.addEventListener('click', onMouseClick, false);
3. 渲染循环:
function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate();
4. 优化建议:
更复杂的交互: 可以实现拖拽、旋转、缩放等更复杂的交互效果。
选择不同物体: 可以使用 Raycaster 选择多个物体,并进行不同的操作。
使用纹理: 可以为模型添加纹理,使其更逼真。
Graph TD 图:模型交互流程
Three.js 应用的性能至关重要,尤其是在处理复杂场景和大量模型时。
案例目标: 优化包含大量立方体的场景的渲染性能。
技术栈: Three.js, InstancedMesh。
1. 创建大量立方体 (未优化):
import * as THREE from 'three'; // 初始化场景、相机和渲染器 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(); const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); const numCubes = 10000; for (let i = 0; i < numCubes; i++) { const cube = new THREE.Mesh(geometry, material); cube.position.x = Math.random() * 100 - 50; cube.position.y = Math.random() * 100 - 50; cube.position.z = Math.random() * 100 - 50; scene.add(cube); } camera.position.z = 100; function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate();
2. 使用 InstancedMesh 优化:
InstancedMesh 允许我们使用相同的几何体和材质渲染多个实例,从而减少渲染调用,提高性能。
import * as THREE from 'three'; // 初始化场景、相机和渲染器 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(); const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); const numCubes = 10000; // 创建 InstancedMesh const mesh = new THREE.InstancedMesh(geometry, material, numCubes); // 设置每个实例的位置 const matrix = new THREE.Matrix4(); for (let i = 0; i < numCubes; i++) { matrix.setPosition( Math.random() * 100 - 50, Math.random() * 100 - 50, Math.random() * 100 - 50 ); mesh.setMatrixAt(i, matrix); } scene.add(mesh); camera.position.z = 100; function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate();
3. 其他优化技巧:
减少多边形数量: 使用低多边形模型。
使用 LOD (Level of Detail): 根据距离调整模型的细节程度。
纹理优化: 使用压缩纹理格式 (例如 DXT, PVRTC, ETC),并优化纹理大小。
减少渲染调用: 合并几何体和材质。
使用 frustum culling: 仅渲染相机视野内的物体。
使用 post-processing 效果时要谨慎: Post-processing 效果会增加渲染负担。
Graph TD 图:性能优化流程
通过以上案例分析,我们学习了如何在 Three.js 中实现场景漫游、模型交互以及性能优化。掌握这些技巧,可以帮助你构建更复杂、更流畅的 Three.js 应用。记住,实践是最好的老师,不断尝试和探索,你将在 Three.js 的世界中取得更大的成就。