Skip to content

三维可视化应用

概述

三维可视化处理立体数据,如 3D 模型、地球、建筑、游戏场景等。

Three.js 基础

1. 创建场景

javascript
import * as THREE from 'three'

class ThreeScene {
  constructor(container) {
    this.container = container
    this.scene = new THREE.Scene()
    this.camera = null
    this.renderer = null
    this.controls = null
    
    this.init()
  }

  init() {
    // 创建相机
    const width = this.container.clientWidth
    const height = this.container.clientHeight
    
    this.camera = new THREE.PerspectiveCamera(
      75,
      width / height,
      0.1,
      1000
    )
    this.camera.position.set(0, 0, 5)

    // 创建渲染器
    this.renderer = new THREE.WebGLRenderer({ antialias: true })
    this.renderer.setSize(width, height)
    this.renderer.setPixelRatio(window.devicePixelRatio)
    this.renderer.setClearColor(0x000000)
    this.container.appendChild(this.renderer.domElement)

    // 添加控制器
    this.controls = new OrbitControls(this.camera, this.renderer.domElement)
    this.controls.enableDamping = true
    this.controls.dampingFactor = 0.05

    // 添加光源
    const ambientLight = new THREE.AmbientLight(0x404040)
    this.scene.add(ambientLight)

    const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
    directionalLight.position.set(1, 1, 1)
    this.scene.add(directionalLight)

    // 窗口大小调整
    window.addEventListener('resize', () => this.onResize())

    // 开始渲染
    this.animate()
  }

  onResize() {
    const width = this.container.clientWidth
    const height = this.container.clientHeight

    this.camera.aspect = width / height
    this.camera.updateProjectionMatrix()
    this.renderer.setSize(width, height)
  }

  animate() {
    requestAnimationFrame(() => this.animate())
    
    this.controls.update()
    this.renderer.render(this.scene, this.camera)
  }
}

2. 创建几何体

javascript
// 立方体
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 })
const cube = new THREE.Mesh(geometry, material)
scene.add(cube)

// 球体
const sphereGeometry = new THREE.SphereGeometry(1, 32, 32)
const sphereMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 })
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial)
scene.add(sphere)

// 圆柱体
const cylinderGeometry = new THREE.CylinderGeometry(1, 1, 2, 32)
const cylinderMaterial = new THREE.MeshStandardMaterial({ color: 0x0000ff })
const cylinder = new THREE.Mesh(cylinderGeometry, cylinderMaterial)
scene.add(cylinder)

3. 加载模型

javascript
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'

const loader = new GLTFLoader()

loader.load(
  '/models/scene.gltf',
  (gltf) => {
    const model = gltf.scene
    model.scale.set(1, 1, 1)
    model.position.set(0, 0, 0)
    scene.add(model)
  },
  (progress) => {
    console.log('Loading:', (progress.loaded / progress.total * 100) + '%')
  },
  (error) => {
    console.error('Error:', error)
  }
)

地球可视化

1. 创建地球

javascript
class Earth {
  constructor(scene) {
    this.scene = scene
    this.earth = null
    this.clouds = null
    
    this.create()
  }

  async create() {
    // 地球
    const earthGeometry = new THREE.SphereGeometry(5, 64, 64)
    const earthTexture = await this.loadTexture('/textures/earth.jpg')
    
    const earthMaterial = new THREE.MeshPhongMaterial({
      map: earthTexture,
      bumpScale: 0.05,
      specular: new THREE.Color('grey'),
      shininess: 10
    })
    
    this.earth = new THREE.Mesh(earthGeometry, earthMaterial)
    this.scene.add(this.earth)

    // 云层
    const cloudsGeometry = new THREE.SphereGeometry(5.1, 64, 64)
    const cloudsTexture = await this.loadTexture('/textures/clouds.jpg')
    
    const cloudsMaterial = new THREE.MeshPhongMaterial({
      map: cloudsTexture,
      transparent: true,
      opacity: 0.4
    })
    
    this.clouds = new THREE.Mesh(cloudsGeometry, cloudsMaterial)
    this.scene.add(this.clouds)
  }

  loadTexture(url) {
    return new Promise((resolve, reject) => {
      const loader = new THREE.TextureLoader()
      loader.load(url, resolve, undefined, reject)
    })
  }

  rotate() {
    this.earth.rotation.y += 0.001
    this.clouds.rotation.y += 0.0015
  }
}

2. 添加标记点

javascript
function addMarker(scene, lat, lng, color = 0xff0000) {
  // 转换坐标
  const phi = (90 - lat) * (Math.PI / 180)
  const theta = (lng + 180) * (Math.PI / 180)

  const x = -5 * Math.sin(phi) * Math.cos(theta)
  const y = 5 * Math.cos(phi)
  const z = 5 * Math.sin(phi) * Math.sin(theta)

  // 创建标记点
  const geometry = new THREE.SphereGeometry(0.1, 16, 16)
  const material = new THREE.MeshBasicMaterial({ color })
  const marker = new THREE.Mesh(geometry, material)
  
  marker.position.set(x, y, z)
  scene.add(marker)

  return marker
}

建筑可视化

1. 创建建筑

javascript
class Building {
  constructor(scene, config) {
    this.scene = scene
    this.config = config
    this.floors = []
    
    this.create()
  }

  create() {
    const { width, depth, floors, floorHeight } = this.config

    for (let i = 0; i < floors; i++) {
      const floor = this.createFloor(i, width, depth, floorHeight)
      this.floors.push(floor)
      this.scene.add(floor)
    }
  }

  createFloor(index, width, depth, height) {
    const group = new THREE.Group()
    
    // 地板
    const floorGeometry = new THREE.BoxGeometry(width, 0.2, depth)
    const floorMaterial = new THREE.MeshStandardMaterial({ color: 0xcccccc })
    const floor = new THREE.Mesh(floorGeometry, floorMaterial)
    floor.position.y = index * height
    group.add(floor)

    // 墙壁
    const wallMaterial = new THREE.MeshStandardMaterial({ 
      color: 0xffffff,
      transparent: true,
      opacity: 0.5
    })

    // 前墙
    const frontWall = new THREE.Mesh(
      new THREE.BoxGeometry(width, height, 0.1),
      wallMaterial
    )
    frontWall.position.set(0, index * height + height / 2, depth / 2)
    group.add(frontWall)

    // 后墙
    const backWall = new THREE.Mesh(
      new THREE.BoxGeometry(width, height, 0.1),
      wallMaterial
    )
    backWall.position.set(0, index * height + height / 2, -depth / 2)
    group.add(backWall)

    // 左墙
    const leftWall = new THREE.Mesh(
      new THREE.BoxGeometry(0.1, height, depth),
      wallMaterial
    )
    leftWall.position.set(-width / 2, index * height + height / 2, 0)
    group.add(leftWall)

    // 右墙
    const rightWall = new THREE.Mesh(
      new THREE.BoxGeometry(0.1, height, depth),
      wallMaterial
    )
    rightWall.position.set(width / 2, index * height + height / 2, 0)
    group.add(rightWall)

    return group
  }
}

性能优化

1. LOD (Level of Detail)

javascript
const lod = new THREE.LOD()

// 高精度模型
const highDetail = new THREE.Mesh(geometry, material)
lod.addLevel(highDetail, 0)

// 中精度模型
const mediumDetail = new THREE.Mesh(geometryMedium, material)
lod.addLevel(mediumDetail, 50)

// 低精度模型
const lowDetail = new THREE.Mesh(geometryLow, material)
lod.addLevel(lowDetail, 100)

scene.add(lod)

2. 实例化

javascript
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 })

const mesh = new THREE.InstancedMesh(geometry, material, 1000)

const matrix = new THREE.Matrix4()
for (let i = 0; i < 1000; i++) {
  matrix.setPosition(
    Math.random() * 100 - 50,
    Math.random() * 100 - 50,
    Math.random() * 100 - 50
  )
  mesh.setMatrixAt(i, matrix)
}

scene.add(mesh)

最佳实践

1. 性能优化

  • 使用 LOD 降低远处模型精度
  • 实例化相同几何体
  • 合并几何体减少 draw call

2. 用户体验

  • 提供加载进度
  • 支持触摸操作
  • 添加交互提示

3. 视觉效果

  • 合理的光照设置
  • 后期处理效果
  • 环境贴图

相关资源

基于 VitePress 的本地知识库