基于Three.js的3D城市可视化实践:从基础搭建到动态效果实现

基于Three.js的3D城市可视化实践:从基础搭建到动态效果实现

在数字孪生与智慧城市领域,3D可视化技术正成为关键基础设施。本文将以Three.js框架为核心,通过完整代码示例展示如何构建具备动态效果的3D城市场景,重点解析场景初始化、摄像机控制、动态物体创建等核心模块的实现方法。

一、基础场景搭建

1.1 HTML结构配置

  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <title>3D城市可视化系统</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <style>
  8. body {
  9. margin: 0;
  10. overflow: hidden;
  11. background-color: #e0e0e0;
  12. }
  13. #container {
  14. position: absolute;
  15. width: 100%;
  16. height: 100%;
  17. }
  18. </style>
  19. </head>
  20. <body>
  21. <div id="container"></div>
  22. <!-- 依赖库与脚本将通过模块化方式引入 -->
  23. </body>
  24. </html>

1.2 核心模块初始化

  1. import * as THREE from 'three';
  2. import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
  3. // 初始化核心对象
  4. let scene, camera, renderer;
  5. let controls;
  6. function initScene() {
  7. // 创建场景
  8. scene = new THREE.Scene();
  9. scene.background = new THREE.Color(0x87CEEB); // 天空蓝背景
  10. scene.fog = new THREE.Fog(0x87CEEB, 10, 50); // 添加雾效
  11. // 创建透视摄像机
  12. camera = new THREE.PerspectiveCamera(
  13. 60,
  14. window.innerWidth / window.innerHeight,
  15. 0.1,
  16. 1000
  17. );
  18. camera.position.set(5, 10, 15);
  19. camera.lookAt(0, 0, 0);
  20. // 初始化WebGL渲染器
  21. renderer = new THREE.WebGLRenderer({
  22. antialias: true,
  23. powerPreference: "high-performance"
  24. });
  25. renderer.setSize(window.innerWidth, window.innerHeight);
  26. renderer.shadowMap.enabled = true;
  27. document.getElementById('container').appendChild(renderer.domElement);
  28. // 添加轨道控制器
  29. controls = new OrbitControls(camera, renderer.domElement);
  30. controls.enableDamping = true;
  31. controls.dampingFactor = 0.05;
  32. }

二、动态物体系统实现

2.1 车辆生成系统

  1. function createVehicle(position, color = 0x00FF00) {
  2. // 创建卡通材质车辆
  3. const material = new THREE.MeshToonMaterial({
  4. color: color,
  5. side: THREE.DoubleSide
  6. });
  7. // 简化车辆几何体
  8. const geometry = new THREE.BoxGeometry(1, 0.15, 0.3);
  9. const vehicle = new THREE.Mesh(geometry, material);
  10. // 设置初始位置
  11. vehicle.position.copy(position);
  12. vehicle.castShadow = true;
  13. // 添加到场景
  14. scene.add(vehicle);
  15. // 创建动画路径
  16. const path = new THREE.CurvePath();
  17. path.add(new THREE.EllipseCurve(
  18. 0, 0,
  19. 10, 3,
  20. 0, Math.PI * 2
  21. ));
  22. return { mesh: vehicle, path };
  23. }
  24. // 批量生成车辆
  25. const vehicles = [];
  26. for(let i = 0; i < 15; i++) {
  27. const angle = Math.random() * Math.PI * 2;
  28. const radius = 5 + Math.random() * 5;
  29. vehicles.push(createVehicle(
  30. new THREE.Vector3(
  31. Math.cos(angle) * radius,
  32. 0.1,
  33. Math.sin(angle) * radius
  34. ),
  35. 0x00FF00 + Math.floor(Math.random() * 0x100000)
  36. ));
  37. }

2.2 建筑物生成算法

  1. function generateBuilding(baseX, baseZ, width = 2, depth = 2) {
  2. const height = 3 + Math.random() * 7;
  3. const color = 0x666666 + Math.floor(Math.random() * 0x333333);
  4. // 创建建筑主体
  5. const mainGeometry = new THREE.BoxGeometry(width, height, depth);
  6. const mainMaterial = new THREE.MeshStandardMaterial({
  7. color: color,
  8. roughness: 0.7
  9. });
  10. const mainBuilding = new THREE.Mesh(mainGeometry, mainMaterial);
  11. // 随机屋顶类型
  12. let roof;
  13. if(Math.random() > 0.5) {
  14. // 锥形屋顶
  15. const roofGeo = new THREE.ConeGeometry(width*1.2, height*0.3, 4);
  16. roof = new THREE.Mesh(roofGeo, new THREE.MeshStandardMaterial({ color: 0x333333 }));
  17. roof.position.y = height;
  18. roof.rotation.y = Math.PI/4;
  19. } else {
  20. // 平顶
  21. const roofGeo = new THREE.BoxGeometry(width*1.1, height*0.2, depth*1.1);
  22. roof = new THREE.Mesh(roofGeo, new THREE.MeshStandardMaterial({ color: 0x444444 }));
  23. roof.position.y = height;
  24. }
  25. const group = new THREE.Group();
  26. group.add(mainBuilding);
  27. group.add(roof);
  28. group.position.set(baseX, 0, baseZ);
  29. // 添加窗户
  30. const windowMaterial = new THREE.MeshBasicMaterial({
  31. color: 0xFFFF00,
  32. transparent: true,
  33. opacity: 0.8
  34. });
  35. const windowSize = 0.5;
  36. const windowGap = 0.3;
  37. const startY = 1;
  38. for(let y = startY; y < height; y += windowSize + windowGap) {
  39. for(let x = -width/2 + windowGap; x < width/2; x += windowSize + windowGap) {
  40. for(let z = -depth/2 + windowGap; z < depth/2; z += windowSize + windowGap) {
  41. const windowGeo = new THREE.BoxGeometry(windowSize*0.9, windowSize*0.9, 0.01);
  42. const windowMesh = new THREE.Mesh(windowGeo, windowMaterial);
  43. windowMesh.position.set(x, y, z);
  44. mainBuilding.add(windowMesh);
  45. }
  46. }
  47. }
  48. return group;
  49. }
  50. // 生成城市建筑群
  51. const cityGroup = new THREE.Group();
  52. for(let i = 0; i < 50; i++) {
  53. const x = (Math.random() - 0.5) * 30;
  54. const z = (Math.random() - 0.5) * 30;
  55. cityGroup.add(generateBuilding(x, z));
  56. }
  57. scene.add(cityGroup);

三、高级渲染技术

3.1 自定义着色器应用

  1. const customShaderMaterial = new THREE.ShaderMaterial({
  2. uniforms: {
  3. time: { value: 0 },
  4. color: { value: new THREE.Color(0xFFA500) }
  5. },
  6. vertexShader: `
  7. varying vec2 vUv;
  8. void main() {
  9. vUv = uv;
  10. gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  11. }
  12. `,
  13. fragmentShader: `
  14. uniform float time;
  15. uniform vec3 color;
  16. varying vec2 vUv;
  17. void main() {
  18. float wave = sin(vUv.x * 10.0 + time * 2.0) * 0.1;
  19. float intensity = smoothstep(0.4, 0.6, vUv.y + wave);
  20. gl_FragColor = vec4(color * intensity, 1.0);
  21. }
  22. `
  23. });
  24. // 创建着色器平面
  25. const shaderPlane = new THREE.Mesh(
  26. new THREE.PlaneGeometry(20, 20),
  27. customShaderMaterial
  28. );
  29. shaderPlane.rotation.x = -Math.PI/2;
  30. shaderPlane.position.y = -0.1;
  31. scene.add(shaderPlane);

3.2 性能优化策略

  1. 实例化渲染:对重复物体使用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);

  1. 2. **LOD细节层次**:根据距离切换模型精度
  2. ```javascript
  3. const lod = new THREE.LOD();
  4. // 高精度模型
  5. const highDetailGeo = new THREE.BoxGeometry(1, 1, 1, 32, 32, 32);
  6. const highDetailMat = new THREE.MeshStandardMaterial({ color: 0xFF0000 });
  7. const highDetail = new THREE.Mesh(highDetailGeo, highDetailMat);
  8. // 低精度模型
  9. const lowDetailGeo = new THREE.BoxGeometry(1, 1, 1, 8, 8, 8);
  10. const lowDetailMat = new THREE.MeshStandardMaterial({ color: 0x00FF00 });
  11. const lowDetail = new THREE.Mesh(lowDetailGeo, lowDetailMat);
  12. lod.addLevel(highDetail, 0);
  13. lod.addLevel(lowDetail, 20);
  14. scene.add(lod);

四、完整动画循环

  1. function animate() {
  2. requestAnimationFrame(animate);
  3. // 更新控制器
  4. controls.update();
  5. // 更新自定义着色器时间
  6. customShaderMaterial.uniforms.time.value += 0.01;
  7. // 更新车辆动画
  8. vehicles.forEach((vehicle, index) => {
  9. const progress = (Date.now() * 0.0005 + index * 0.1) % 1;
  10. const angle = progress * Math.PI * 2;
  11. vehicle.mesh.position.x = Math.cos(angle) * 10;
  12. vehicle.mesh.position.z = Math.sin(angle) * 3;
  13. });
  14. // 旋转建筑群(演示用)
  15. cityGroup.rotation.y += 0.001;
  16. renderer.render(scene, camera);
  17. }
  18. // 初始化并启动
  19. initScene();
  20. animate();

五、关键技术点总结

  1. 场景管理:合理组织场景图结构,使用分组(Group)管理相关物体
  2. 性能优化:采用实例化渲染、LOD技术提升大规模场景性能
  3. 动画系统:结合requestAnimationFrame实现流畅动画
  4. 着色器编程:通过GLSL实现自定义渲染效果
  5. 交互控制:使用OrbitControls提供用户交互能力

本文展示的完整实现可在现代浏览器中直接运行,开发者可根据实际需求调整场景规模、物体细节和动画参数。该方案适用于智慧城市可视化、游戏开发、数字孪生等多个领域,为WebGL开发者提供了可复用的技术框架。