基于Three.js的3D城市可视化实践:从基础搭建到动态效果实现
在数字孪生与智慧城市领域,3D可视化技术正成为关键基础设施。本文将以Three.js框架为核心,通过完整代码示例展示如何构建具备动态效果的3D城市场景,重点解析场景初始化、摄像机控制、动态物体创建等核心模块的实现方法。
一、基础场景搭建
1.1 HTML结构配置
<!DOCTYPE html><html lang="zh-CN"><head><title>3D城市可视化系统</title><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>body {margin: 0;overflow: hidden;background-color: #e0e0e0;}#container {position: absolute;width: 100%;height: 100%;}</style></head><body><div id="container"></div><!-- 依赖库与脚本将通过模块化方式引入 --></body></html>
1.2 核心模块初始化
import * as THREE from 'three';import { OrbitControls } from 'three/addons/controls/OrbitControls.js';// 初始化核心对象let scene, camera, renderer;let controls;function initScene() {// 创建场景scene = new THREE.Scene();scene.background = new THREE.Color(0x87CEEB); // 天空蓝背景scene.fog = new THREE.Fog(0x87CEEB, 10, 50); // 添加雾效// 创建透视摄像机camera = new THREE.PerspectiveCamera(60,window.innerWidth / window.innerHeight,0.1,1000);camera.position.set(5, 10, 15);camera.lookAt(0, 0, 0);// 初始化WebGL渲染器renderer = new THREE.WebGLRenderer({antialias: true,powerPreference: "high-performance"});renderer.setSize(window.innerWidth, window.innerHeight);renderer.shadowMap.enabled = true;document.getElementById('container').appendChild(renderer.domElement);// 添加轨道控制器controls = new OrbitControls(camera, renderer.domElement);controls.enableDamping = true;controls.dampingFactor = 0.05;}
二、动态物体系统实现
2.1 车辆生成系统
function createVehicle(position, color = 0x00FF00) {// 创建卡通材质车辆const material = new THREE.MeshToonMaterial({color: color,side: THREE.DoubleSide});// 简化车辆几何体const geometry = new THREE.BoxGeometry(1, 0.15, 0.3);const vehicle = new THREE.Mesh(geometry, material);// 设置初始位置vehicle.position.copy(position);vehicle.castShadow = true;// 添加到场景scene.add(vehicle);// 创建动画路径const path = new THREE.CurvePath();path.add(new THREE.EllipseCurve(0, 0,10, 3,0, Math.PI * 2));return { mesh: vehicle, path };}// 批量生成车辆const vehicles = [];for(let i = 0; i < 15; i++) {const angle = Math.random() * Math.PI * 2;const radius = 5 + Math.random() * 5;vehicles.push(createVehicle(new THREE.Vector3(Math.cos(angle) * radius,0.1,Math.sin(angle) * radius),0x00FF00 + Math.floor(Math.random() * 0x100000)));}
2.2 建筑物生成算法
function generateBuilding(baseX, baseZ, width = 2, depth = 2) {const height = 3 + Math.random() * 7;const color = 0x666666 + Math.floor(Math.random() * 0x333333);// 创建建筑主体const mainGeometry = new THREE.BoxGeometry(width, height, depth);const mainMaterial = new THREE.MeshStandardMaterial({color: color,roughness: 0.7});const mainBuilding = new THREE.Mesh(mainGeometry, mainMaterial);// 随机屋顶类型let roof;if(Math.random() > 0.5) {// 锥形屋顶const roofGeo = new THREE.ConeGeometry(width*1.2, height*0.3, 4);roof = new THREE.Mesh(roofGeo, new THREE.MeshStandardMaterial({ color: 0x333333 }));roof.position.y = height;roof.rotation.y = Math.PI/4;} else {// 平顶const roofGeo = new THREE.BoxGeometry(width*1.1, height*0.2, depth*1.1);roof = new THREE.Mesh(roofGeo, new THREE.MeshStandardMaterial({ color: 0x444444 }));roof.position.y = height;}const group = new THREE.Group();group.add(mainBuilding);group.add(roof);group.position.set(baseX, 0, baseZ);// 添加窗户const windowMaterial = new THREE.MeshBasicMaterial({color: 0xFFFF00,transparent: true,opacity: 0.8});const windowSize = 0.5;const windowGap = 0.3;const startY = 1;for(let y = startY; y < height; y += windowSize + windowGap) {for(let x = -width/2 + windowGap; x < width/2; x += windowSize + windowGap) {for(let z = -depth/2 + windowGap; z < depth/2; z += windowSize + windowGap) {const windowGeo = new THREE.BoxGeometry(windowSize*0.9, windowSize*0.9, 0.01);const windowMesh = new THREE.Mesh(windowGeo, windowMaterial);windowMesh.position.set(x, y, z);mainBuilding.add(windowMesh);}}}return group;}// 生成城市建筑群const cityGroup = new THREE.Group();for(let i = 0; i < 50; i++) {const x = (Math.random() - 0.5) * 30;const z = (Math.random() - 0.5) * 30;cityGroup.add(generateBuilding(x, z));}scene.add(cityGroup);
三、高级渲染技术
3.1 自定义着色器应用
const customShaderMaterial = new THREE.ShaderMaterial({uniforms: {time: { value: 0 },color: { value: new THREE.Color(0xFFA500) }},vertexShader: `varying vec2 vUv;void main() {vUv = uv;gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);}`,fragmentShader: `uniform float time;uniform vec3 color;varying vec2 vUv;void main() {float wave = sin(vUv.x * 10.0 + time * 2.0) * 0.1;float intensity = smoothstep(0.4, 0.6, vUv.y + wave);gl_FragColor = vec4(color * intensity, 1.0);}`});// 创建着色器平面const shaderPlane = new THREE.Mesh(new THREE.PlaneGeometry(20, 20),customShaderMaterial);shaderPlane.rotation.x = -Math.PI/2;shaderPlane.position.y = -0.1;scene.add(shaderPlane);
3.2 性能优化策略
- 实例化渲染:对重复物体使用
THREE.InstancedMesh
```javascript
const buildingGeometry = new THREE.BoxGeometry(1, 5, 1);
const buildingMaterial = new THREE.MeshStandardMaterial({ color: 0x999999 });
const instancedBuilding = new THREE.InstancedMesh(
buildingGeometry,
buildingMaterial,
1000
);
// 批量添加实例
for(let i = 0; i < 1000; i++) {
const matrix = new THREE.Matrix4();
matrix.makeTranslation(
(Math.random() - 0.5) 100,
0,
(Math.random() - 0.5) 100
);
instancedBuilding.setMatrixAt(i, matrix);
}
scene.add(instancedBuilding);
2. **LOD细节层次**:根据距离切换模型精度```javascriptconst lod = new THREE.LOD();// 高精度模型const highDetailGeo = new THREE.BoxGeometry(1, 1, 1, 32, 32, 32);const highDetailMat = new THREE.MeshStandardMaterial({ color: 0xFF0000 });const highDetail = new THREE.Mesh(highDetailGeo, highDetailMat);// 低精度模型const lowDetailGeo = new THREE.BoxGeometry(1, 1, 1, 8, 8, 8);const lowDetailMat = new THREE.MeshStandardMaterial({ color: 0x00FF00 });const lowDetail = new THREE.Mesh(lowDetailGeo, lowDetailMat);lod.addLevel(highDetail, 0);lod.addLevel(lowDetail, 20);scene.add(lod);
四、完整动画循环
function animate() {requestAnimationFrame(animate);// 更新控制器controls.update();// 更新自定义着色器时间customShaderMaterial.uniforms.time.value += 0.01;// 更新车辆动画vehicles.forEach((vehicle, index) => {const progress = (Date.now() * 0.0005 + index * 0.1) % 1;const angle = progress * Math.PI * 2;vehicle.mesh.position.x = Math.cos(angle) * 10;vehicle.mesh.position.z = Math.sin(angle) * 3;});// 旋转建筑群(演示用)cityGroup.rotation.y += 0.001;renderer.render(scene, camera);}// 初始化并启动initScene();animate();
五、关键技术点总结
- 场景管理:合理组织场景图结构,使用分组(Group)管理相关物体
- 性能优化:采用实例化渲染、LOD技术提升大规模场景性能
- 动画系统:结合requestAnimationFrame实现流畅动画
- 着色器编程:通过GLSL实现自定义渲染效果
- 交互控制:使用OrbitControls提供用户交互能力
本文展示的完整实现可在现代浏览器中直接运行,开发者可根据实际需求调整场景规模、物体细节和动画参数。该方案适用于智慧城市可视化、游戏开发、数字孪生等多个领域,为WebGL开发者提供了可复用的技术框架。