{T}

场景系统

场景(Scene)是 Three.js 的核心概念之一,它是所有 3D 对象的容器。理解场景系统对于构建复杂的 3D 应用至关重要。

系统架构概述

plaintext
┌─────────────────────────────────────────────────────────────────┐
│                        场景系统架构                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   ┌─────────┐     ┌──────────┐     ┌───────────────┐          │
│   │  Scene  │────▶│ Object3D │────▶│ 子类对象       │          │
│   └─────────┘     └──────────┘     ├───────────────┤          │
│       │                              │ Mesh          │          │
│       │ 包含                         │ Group         │          │
│       ▼                              │ Light         │          │
│   ┌─────────┐                        │ Camera        │          │
│   │ 背景属性 │                        │ Line          │          │
│   │ 雾效果   │                        │ Points        │          │
│   │ 环境贴图 │                        │ Sprite        │          │
│   └─────────┘                        └───────────────┘          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Scene 基础

创建场景

javascript
// 创建空场景
const scene = new THREE.Scene()
 
// 场景创建后可立即添加对象
scene.add(new THREE.AmbientLight(0xffffff, 0.5))

Scene 属性配置

属性类型说明默认值
backgroundColor/Texture/CubeTexture场景背景null
environmentTexture环境贴图,用于反射null
fogFog/FogExp2雾效果null
childrenArray子对象数组[]
autoUpdateBoolean自动更新矩阵true
backgroundBlurrinessNumber背景模糊程度0
backgroundIntensityNumber背景强度1

背景设置

javascript
// 使用纯色背景
scene.background = new THREE.Color(0x333333)
 
// 使用渐变背景(通过着色器或 CSS)
scene.background = new THREE.Color(0x87ceeb) // 天蓝色
 
// 使用天空盒(立方体贴图)
scene.background = new THREE.CubeTextureLoader().load([
  'px.jpg', 'nx.jpg',  // 正X、负X
  'py.jpg', 'ny.jpg',  // 正Y、负Y
  'pz.jpg', 'nz.jpg'   // 正Z、负Z
])
 
// 使用等距矩形投影贴图(全景图)
scene.background = new THREE.TextureLoader().load('equirectangular.jpg')
 
// 设置背景模糊(需要 HDR 环境)
scene.backgroundBlurriness = 0.5
scene.backgroundIntensity = 1.0

环境贴图

javascript
// 设置环境贴图(用于 PBR 材质反射)
const envMap = new THREE.CubeTextureLoader().load([
  'px.jpg', 'nx.jpg',
  'py.jpg', 'ny.jpg',
  'pz.jpg', 'nz.jpg'
])
 
scene.environment = envMap
 
// 使用 PMREMGenerator 生成优化的环境贴图
const pmremGenerator = new THREE.PMREMGenerator(renderer)
const envMapTexture = pmremGenerator.fromScene(scene).texture
scene.environment = envMapTexture

雾效果

雾类型构造函数特点适用场景
Fognew THREE.Fog(color, near, far)线性雾,距离越远越浓室内场景
FogExp2new THREE.FogExp2(color, density)指数雾,更自然开放场景
javascript
// 线性雾:near 处开始,far 处完全透明
scene.fog = new THREE.Fog(0xcccccc, 10, 50)
// 参数说明:
// - color: 雾的颜色
// - near: 开始产生雾效果的近距离
// - far: 完全被雾遮蔽的远距离
 
// 指数雾:密度控制雾的浓度
scene.fog = new THREE.FogExp2(0xcccccc, 0.02)
// 参数说明:
// - color: 雾的颜色
// - density: 雾的密度,值越大雾越浓

场景图(Scene Graph)

树形结构

Three.js 使用树形结构组织场景中的对象,形成父子层级关系:

plaintext
Scene (根节点)
├── Group "buildings"                    [建筑组]
│   ├── Mesh "building_1"                [建筑1]
│   │   └── Mesh "door"                  [门]
│   └── Mesh "building_2"                [建筑2]
├── Group "lights"                       [灯光组]
│   ├── AmbientLight                     [环境光]
│   ├── DirectionalLight                 [方向光]
│   └── PointLight                       [点光源]
├── Mesh "ground"                        [地面]
└── Camera                               [相机]

Object3D 基类

所有场景中的对象都继承自 THREE.Object3D,它提供了基础的变换和层级管理功能。

Object3D 核心属性

属性类型说明
positionVector3本地位置坐标
rotationEuler旋转角度(弧度)
quaternionQuaternion四元数旋转
scaleVector3缩放比例
visibleBoolean是否可见
castShadowBoolean是否投射阴影
receiveShadowBoolean是否接收阴影
frustumCulledBoolean是否进行视锥体裁剪
matrixMatrix4本地变换矩阵
matrixWorldMatrix4世界变换矩阵
userDataObject自定义数据存储
javascript
const object = new THREE.Object3D()
 
// 位置操作
object.position.set(5, 3, 2)
object.position.x = 10
object.translateX(1)
object.translateY(2)
object.translateZ(3)
 
// 旋转操作
object.rotation.set(Math.PI / 4, 0, 0)  // 绕X轴旋转45度
object.rotation.x = Math.PI / 2
object.rotateX(Math.PI / 4)
object.rotateY(Math.PI / 6)
object.rotateZ(Math.PI / 3)
 
// 缩放操作
object.scale.set(2, 2, 2)
object.scale.x = 1.5
 
// 自定义数据
object.userData = {
  id: 'player_001',
  type: 'character',
  health: 100,
  level: 5
}

Object3D 核心方法

方法参数返回值说明
add(object)Object3D-添加子对象
remove(object)Object3D-移除子对象
traverse(callback)Function-遍历所有后代
traverseVisible(callback)Function-遍历可见后代
traverseAncestors(callback)Function-遍历所有祖先
getObjectById(id)NumberObject3D通过ID查找
getObjectByName(name)StringObject3D通过名称查找
getObjectByProperty(name, value)String, AnyObject3D通过属性查找
getWorldPosition(target)Vector3Vector3获取世界坐标
getWorldDirection(target)Vector3Vector3获取世界方向
getWorldQuaternion(target)QuaternionQuaternion获取世界四元数
getWorldScale(target)Vector3Vector3获取世界缩放
localToWorld(vector)Vector3Vector3本地转世界坐标
worldToLocal(vector)Vector3Vector3世界转本地坐标
lookAt(x, y, z)Number/Vector3-朝向指定位置
updateMatrix()--更新本地矩阵
updateMatrixWorld(force)Boolean-更新世界矩阵
dispose()--释放资源
javascript
// 添加和移除对象
object.add(child)
object.remove(child)
 
// 遍历所有后代
object.traverse((child) => {
  console.log(child.type, child.name)
})
 
// 查找对象
const found = object.getObjectByName('player')
const byId = object.getObjectById(1)
 
// 获取世界信息
const worldPos = new THREE.Vector3()
object.getWorldPosition(worldPos)
 
const worldDir = new THREE.Vector3()
object.getWorldDirection(worldDir)

Group(组)

Group 用于组织多个对象,方便统一管理和操作。

javascript
// 创建组
const group = new THREE.Group()
group.name = 'buildings'  // 命名便于查找
 
// 创建并添加对象到组
const cube1 = new THREE.Mesh(geometry1, material1)
cube1.name = 'cube_1'
const cube2 = new THREE.Mesh(geometry2, material2)
cube2.name = 'cube_2'
 
group.add(cube1)
group.add(cube2)
 
// 将组添加到场景
scene.add(group)
 
// 统一操作组(影响所有子对象)
group.position.set(0, 2, 0)      // 整体移动
group.rotation.y = Math.PI / 4   // 整体旋转
group.scale.set(2, 2, 2)         // 整体缩放
 
// 移动组(相对于当前位置)
group.translateX(5)
 
// 控制整体可见性
group.visible = false  // 隐藏所有子对象

层级关系示例

javascript
// 创建太阳系层级结构
const solarSystem = new THREE.Group()
solarSystem.name = 'solarSystem'
 
// 地球轨道组
const earthOrbit = new THREE.Group()
earthOrbit.name = 'earthOrbit'
 
// 月球轨道组(相对于地球)
const moonOrbit = new THREE.Group()
moonOrbit.name = 'moonOrbit'
 
// 创建天体
const sun = new THREE.Mesh(sunGeometry, sunMaterial)
sun.name = 'sun'
const earth = new THREE.Mesh(earthGeometry, earthMaterial)
earth.name = 'earth'
const moon = new THREE.Mesh(moonGeometry, moonMaterial)
moon.name = 'moon'
 
// 设置初始位置
earth.position.set(10, 0, 0)  // 地球距太阳10单位
moon.position.set(2, 0, 0)    // 月球距地球2单位
 
// 构建层级结构
moonOrbit.add(moon)
earthOrbit.add(earth)
earthOrbit.add(moonOrbit)
solarSystem.add(sun)
solarSystem.add(earthOrbit)
scene.add(solarSystem)
 
// 动画循环中更新轨道
function animate() {
  requestAnimationFrame(animate)
  
  earthOrbit.rotation.y += 0.01   // 地球公转
  moonOrbit.rotation.y += 0.05    // 月球公转
  earth.rotation.y += 0.02        // 地球自转
  
  renderer.render(scene, camera)
}

坐标系统

Three.js 使用右手坐标系:X轴指向右,Y轴指向上,Z轴指向观察者。

plaintext
        Y



        └─────── X


     Z

世界坐标

场景的全局坐标系统,所有对象的最终渲染位置由其世界坐标决定。

javascript
const object = new THREE.Object3D()
object.position.set(5, 3, 2)  // 设置本地坐标
 
// 获取世界坐标(考虑所有父级的变换)
const worldPosition = new THREE.Vector3()
object.getWorldPosition(worldPosition)
console.log('世界坐标:', worldPosition)

本地坐标

相对于父对象的坐标,子对象的变换会继承父对象的变换。

javascript
// 父对象在世界原点
const parent = new THREE.Object3D()
parent.position.set(10, 0, 0)  // 父对象在世界坐标 (10, 0, 0)
 
// 子对象相对于父对象
const child = new THREE.Object3D()
child.position.set(5, 0, 0)  // 本地坐标 (5, 0, 0)
 
parent.add(child)
scene.add(parent)
 
// child 的世界坐标 = 父坐标 + 本地坐标 = (15, 0, 0)
const worldPos = new THREE.Vector3()
child.getWorldPosition(worldPos)
console.log(worldPos)  // Vector3 {x: 15, y: 0, z: 0}

坐标转换

javascript
// 本地坐标转世界坐标
const worldPos = new THREE.Vector3(5, 0, 0)
object.localToWorld(worldPos)
 
// 世界坐标转本地坐标
const localPos = new THREE.Vector3(15, 0, 0)
object.worldToLocal(localPos)
 
// 获取世界方向(对象前方)
const worldDirection = new THREE.Vector3()
object.getWorldDirection(worldDirection)
 
// 更新矩阵后获取精确的世界信息
object.updateMatrixWorld(true)
const matrix = object.matrixWorld

场景操作

添加对象

javascript
// 添加单个对象
scene.add(mesh)
 
// 添加多个对象(一次性添加)
scene.add(mesh1, mesh2, mesh3)
 
// 使用 Group 批量管理
const group = new THREE.Group()
group.add(mesh1, mesh2, mesh3)
scene.add(group)
 
// 检查对象是否在场景中
console.log(scene.children.includes(mesh))

移除对象

javascript
// 移除单个对象
scene.remove(mesh)
 
// 移除并释放资源
function removeObject(object) {
  scene.remove(object)
  
  if (object.geometry) {
    object.geometry.dispose()
  }
  
  if (object.material) {
    if (Array.isArray(object.material)) {
      object.material.forEach(mat => {
        if (mat.map) mat.map.dispose()
        mat.dispose()
      })
    } else {
      if (object.material.map) object.material.map.dispose()
      object.material.dispose()
    }
  }
}
 
// 移除所有子对象
while (scene.children.length > 0) {
  const child = scene.children[0]
  scene.remove(child)
}
 
// 清空场景但保留相机和灯光
scene.children.forEach((child) => {
  if (child.type !== 'Camera' && child.type !== 'AmbientLight') {
    scene.remove(child)
  }
})

查找对象

javascript
// 通过 ID 查找(每个对象有唯一的 id)
const object = scene.getObjectById(1)
 
// 通过名称查找(需要预先设置 name 属性)
const player = scene.getObjectByName('player')
 
// 通过自定义属性查找
const enemy = scene.getObjectByProperty('type', 'enemy')
 
// 遍历查找特定类型的对象
scene.traverse((child) => {
  if (child.isMesh) {
    console.log('网格对象:', child.name)
  }
  if (child.isLight) {
    console.log('灯光对象:', child.type)
  }
})
 
// 使用 userData 查找
scene.traverse((child) => {
  if (child.userData.type === 'enemy') {
    console.log('敌人:', child.userData.health)
  }
})

遍历场景

javascript
// 遍历所有后代(包括自身)
scene.traverse((child) => {
  console.log(child.type, child.name)
  
  // 对特定类型操作
  if (child.isMesh) {
    child.material.wireframe = true
  }
})
 
// 只遍历可见对象
scene.traverseVisible((child) => {
  console.log('可见:', child.type)
})
 
// 遍历祖先(向上查找)
object.traverseAncestors((parent) => {
  console.log('父级:', parent.name)
})

场景优化

对象池

复用对象,减少创建和销毁开销,适用于频繁创建销毁的场景(如子弹、粒子)。

javascript
class ObjectPool {
  constructor(createFn, initialSize = 10, maxSize = 100) {
    this.createFn = createFn
    this.pool = []
    this.active = []
    this.maxSize = maxSize
    
    // 预创建对象
    for (let i = 0; i < initialSize; i++) {
      this.pool.push(this.createFn())
    }
  }
  
  // 获取对象
  get() {
    let object
    
    if (this.pool.length > 0) {
      object = this.pool.pop()
    } else if (this.active.length < this.maxSize) {
      object = this.createFn()
    } else {
      console.warn('对象池已满')
      return null
    }
    
    object.visible = true
    this.active.push(object)
    return object
  }
  
  // 释放对象
  release(object) {
    const index = this.active.indexOf(object)
    if (index > -1) {
      this.active.splice(index, 1)
      object.visible = false
      this.pool.push(object)
    }
  }
  
  // 释放所有对象
  releaseAll() {
    while (this.active.length > 0) {
      this.release(this.active[0])
    }
  }
}
 
// 使用对象池管理子弹
const bulletPool = new ObjectPool(() => {
  const geometry = new THREE.SphereGeometry(0.1)
  const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
  return new THREE.Mesh(geometry, material)
}, 20, 100)
 
// 发射子弹
const bullet = bulletPool.get()
if (bullet) {
  bullet.position.copy(player.position)
  scene.add(bullet)
}
 
// 子弹消失时释放
bulletPool.release(bullet)
scene.remove(bullet)

LOD(Level of Detail)

根据距离显示不同精度的模型,优化远距离对象的渲染性能。

javascript
const lod = new THREE.LOD()
 
// 创建不同精度的模型
const highDetail = new THREE.Mesh(
  new THREE.SphereGeometry(1, 32, 32),
  material
)
const mediumDetail = new THREE.Mesh(
  new THREE.SphereGeometry(1, 16, 16),
  material
)
const lowDetail = new THREE.Mesh(
  new THREE.SphereGeometry(1, 8, 8),
  material
)
 
// 设置不同距离的显示等级
lod.addLevel(highDetail, 0)     // 0-10 单位显示高精度
lod.addLevel(mediumDetail, 10)  // 10-50 单位显示中精度
lod.addLevel(lowDetail, 50)     // 50+ 单位显示低精度
 
// 更新 LOD(需要相机位置)
function animate() {
  lod.update(camera)
  renderer.render(scene, camera)
}
 
scene.add(lod)

实例化渲染

渲染大量相同几何体的对象时,大幅提升性能。

javascript
// 创建实例化网格
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 })
const count = 1000
 
const mesh = new THREE.InstancedMesh(geometry, material, count)
 
// 设置每个实例的变换矩阵
const dummy = new THREE.Object3D()
const colors = []
 
for (let i = 0; i < count; i++) {
  // 设置位置
  dummy.position.set(
    Math.random() * 100 - 50,
    Math.random() * 100 - 50,
    Math.random() * 100 - 50
  )
  
  // 设置旋转
  dummy.rotation.set(
    Math.random() * Math.PI,
    Math.random() * Math.PI,
    Math.random() * Math.PI
  )
  
  // 设置缩放
  dummy.scale.setScalar(Math.random() * 0.5 + 0.5)
  
  // 更新矩阵
  dummy.updateMatrix()
  mesh.setMatrixAt(i, dummy.matrix)
  
  // 设置颜色(需要开启 vertexColors)
  colors.push(Math.random(), Math.random(), Math.random())
}
 
// 应用颜色
mesh.instanceMatrix.needsUpdate = true
 
scene.add(mesh)

场景序列化

导出场景

javascript
import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js'
 
function exportScene(scene, filename = 'scene.gltf') {
  const exporter = new GLTFExporter()
  
  exporter.parse(
    scene,
    (gltf) => {
      const output = JSON.stringify(gltf, null, 2)
      
      // 下载文件
      const blob = new Blob([output], { type: 'application/json' })
      const url = URL.createObjectURL(blob)
      
      const link = document.createElement('a')
      link.href = url
      link.download = filename
      link.click()
      
      URL.revokeObjectURL(url)
    },
    (error) => {
      console.error('导出失败:', error)
    },
    { binary: false }  // false = GLTF, true = GLB
  )
}

导入场景

javascript
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
 
const loader = new GLTFLoader()
 
// 可选:使用 Draco 压缩
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('/draco/')
loader.setDRACOLoader(dracoLoader)
 
loader.load(
  'scene.gltf',
  (gltf) => {
    const model = gltf.scene
    
    // 处理模型
    model.traverse((child) => {
      if (child.isMesh) {
        child.castShadow = true
        child.receiveShadow = true
      }
    })
    
    scene.add(model)
  },
  (progress) => {
    console.log('加载进度:', (progress.loaded / progress.total * 100).toFixed(2) + '%')
  },
  (error) => {
    console.error('加载失败:', error)
  }
)

场景管理

多场景管理

javascript
class SceneManager {
  constructor() {
    this.scenes = new Map()
    this.currentScene = null
    this.clock = new THREE.Clock()
  }
  
  create(name, options = {}) {
    const scene = new THREE.Scene()
    
    // 配置场景
    if (options.background !== undefined) {
      scene.background = new THREE.Color(options.background)
    }
    
    if (options.fog) {
      if (options.fog.type === 'exp2') {
        scene.fog = new THREE.FogExp2(options.fog.color, options.fog.density)
      } else {
        scene.fog = new THREE.Fog(options.fog.color, options.fog.near, options.fog.far)
      }
    }
    
    if (options.environment) {
      scene.environment = options.environment
    }
    
    // 存储场景配置
    const sceneData = {
      scene,
      name,
      init: options.init || (() => {}),
      update: options.update || (() => {}),
      destroy: options.destroy || (() => {}),
      isInitialized: false
    }
    
    this.scenes.set(name, sceneData)
    return sceneData
  }
  
  switch(name) {
    const sceneData = this.scenes.get(name)
    if (!sceneData) {
      console.error(`场景 "${name}" 不存在`)
      return false
    }
    
    // 卸载当前场景
    if (this.currentScene && this.currentScene !== sceneData) {
      this.currentScene.destroy()
      this.currentScene.isInitialized = false
    }
    
    // 初始化新场景
    if (!sceneData.isInitialized) {
      sceneData.init(sceneData.scene)
      sceneData.isInitialized = true
    }
    
    this.currentScene = sceneData
    return true
  }
  
  update() {
    if (this.currentScene) {
      const delta = this.clock.getDelta()
      const elapsed = this.clock.getElapsedTime()
      this.currentScene.update(delta, elapsed)
    }
  }
  
  remove(name) {
    const sceneData = this.scenes.get(name)
    if (sceneData) {
      sceneData.destroy()
      this.scenes.delete(name)
    }
  }
}
 
// 使用场景管理器
const sceneManager = new SceneManager()
 
// 创建菜单场景
sceneManager.create('menu', {
  background: 0x333333,
  init: (scene) => {
    // 初始化菜单对象
    scene.add(createMenuUI())
  },
  update: (delta) => {
    // 菜单动画
  }
})
 
// 创建游戏场景
sceneManager.create('game', {
  background: 0x87ceeb,
  fog: { type: 'linear', color: 0xcccccc, near: 10, far: 100 },
  init: (scene) => {
    // 初始化游戏对象
    scene.add(createPlayer())
    scene.add(createEnemies())
  },
  update: (delta) => {
    // 游戏逻辑更新
  },
  destroy: () => {
    // 清理游戏资源
  }
})
 
// 切换场景
sceneManager.switch('game')

场景状态保存

javascript
class SceneState {
  constructor() {
    this.states = new Map()
  }
  
  save(name, scene) {
    const state = {
      timestamp: Date.now(),
      objects: []
    }
    
    scene.traverse((child) => {
      if (child.isMesh || child.isGroup) {
        state.objects.push({
          uuid: child.uuid,
          name: child.name,
          type: child.type,
          position: child.position.toArray(),
          rotation: [child.rotation.x, child.rotation.y, child.rotation.z],
          scale: child.scale.toArray(),
          visible: child.visible
        })
      }
    })
    
    this.states.set(name, state)
    return state
  }
  
  restore(name, scene) {
    const state = this.states.get(name)
    if (!state) {
      console.error(`状态 "${name}" 不存在`)
      return false
    }
    
    state.objects.forEach((objState) => {
      const object = scene.getObjectByProperty('uuid', objState.uuid)
      if (object) {
        object.position.fromArray(objState.position)
        object.rotation.set(objState.rotation[0], objState.rotation[1], objState.rotation[2])
        object.scale.fromArray(objState.scale)
        object.visible = objState.visible
      }
    })
    
    return true
  }
  
  delete(name) {
    this.states.delete(name)
  }
}
 
// 使用状态保存
const stateManager = new SceneState()
 
// 保存当前状态
stateManager.save('checkpoint_1', scene)
 
// 恢复状态
stateManager.restore('checkpoint_1', scene)

常见问题

Q1: 场景中的对象不显示?

诊断步骤

javascript
// 1. 检查对象是否已添加到场景
console.log('对象在场景中:', scene.children.includes(object))
 
// 2. 检查对象位置
console.log('对象位置:', object.position)
console.log('对象世界位置:', object.getWorldPosition(new THREE.Vector3()))
 
// 3. 检查对象可见性
console.log('对象可见:', object.visible)
 
// 4. 检查相机视锥体
const frustum = new THREE.Frustum()
const matrix = new THREE.Matrix4().multiplyMatrices(
  camera.projectionMatrix,
  camera.matrixWorldInverse
)
frustum.setFromProjectionMatrix(matrix)
console.log('对象在视锥体中:', frustum.intersectsObject(object))
 
// 5. 检查材质和几何体
console.log('材质:', object.material)
console.log('几何体:', object.geometry)

Q2: 如何正确清空场景?

javascript
function clearScene(scene) {
  // 遍历并释放所有资源
  scene.traverse((child) => {
    // 清理几何体
    if (child.geometry) {
      child.geometry.dispose()
    }
    
    // 清理材质
    if (child.material) {
      const materials = Array.isArray(child.material) ? child.material : [child.material]
      materials.forEach((mat) => {
        // 清理材质中的纹理
        if (mat.map) mat.map.dispose()
        if (mat.normalMap) mat.normalMap.dispose()
        if (mat.roughnessMap) mat.roughnessMap.dispose()
        if (mat.metalnessMap) mat.metalnessMap.dispose()
        if (mat.aoMap) mat.aoMap.dispose()
        if (mat.emissiveMap) mat.emissiveMap.dispose()
        mat.dispose()
      })
    }
  })
  
  // 移除所有子对象
  while (scene.children.length > 0) {
    scene.remove(scene.children[0])
  }
}

Q3: 如何优化大型场景?

优化策略

javascript
// 1. 使用 LOD 技术
const lod = new THREE.LOD()
lod.addLevel(highDetailMesh, 0)
lod.addLevel(lowDetailMesh, 50)
scene.add(lod)
 
// 2. 视锥体裁剪(Three.js 自动处理)
object.frustumCulled = true  // 默认开启
 
// 3. 实例化渲染大量相同对象
const instancedMesh = new THREE.InstancedMesh(geometry, material, count)
scene.add(instancedMesh)
 
// 4. 对象池复用
const pool = new ObjectPool(createFunction)
 
// 5. 分块加载(按需加载场景区域)
function loadChunk(x, z) {
  // 只加载玩家附近的区块
}
 
// 6. 降低绘制调用
// - 合并几何体
// - 使用实例化
// - 减少材质数量
 
// 7. 使用 Octree 或 BVH 加速碰撞检测
// 适用于大型场景的对象查询

Q4: 子对象的世界坐标计算错误?

javascript
// 问题:获取的世界坐标不正确
// 原因:矩阵未更新
 
// 解决方案:强制更新矩阵
object.updateMatrixWorld(true)
 
// 然后获取世界坐标
const worldPos = new THREE.Vector3()
object.getWorldPosition(worldPos)
 
// 或在渲染器中设置自动更新
renderer.autoUpdateObjects = true  // 默认为 true

最佳实践

1. 使用 Group 组织对象

javascript
// 按功能分组
const buildings = new THREE.Group()
buildings.name = 'buildings'
 
const vehicles = new THREE.Group()
vehicles.name = 'vehicles'
 
const characters = new THREE.Group()
characters.name = 'characters'
 
scene.add(buildings, vehicles, characters)
 
// 方便统一操作
buildings.visible = false  // 隐藏所有建筑
characters.traverse(child => {
  if (child.isMesh) {
    child.castShadow = true
  }
})

2. 命名对象便于调试

javascript
const cube = new THREE.Mesh(geometry, material)
cube.name = 'player-cube'
scene.add(cube)
 
// 调试时查找
const player = scene.getObjectByName('player-cube')
 
// 在控制台中可见
console.log(scene)

3. 使用 userData 存储自定义数据

javascript
const enemy = new THREE.Mesh(geometry, material)
enemy.userData = {
  id: 'enemy_001',
  type: 'enemy',
  health: 100,
  maxHealth: 100,
  level: 5,
  attack: 10,
  defense: 5,
  dropItems: ['sword', 'potion']
}
scene.add(enemy)
 
// 在游戏中使用
function damageEnemy(enemy, damage) {
  enemy.userData.health -= damage
  if (enemy.userData.health <= 0) {
    // 掉落物品
    dropItems(enemy.userData.dropItems)
    // 移除敌人
    scene.remove(enemy)
  }
}

4. 及时清理资源

javascript
// 创建清理函数
function disposeObject(object) {
  // 清理几何体
  if (object.geometry) {
    object.geometry.dispose()
  }
  
  // 清理材质
  if (object.material) {
    const materials = Array.isArray(object.material) ? object.material : [object.material]
    materials.forEach((mat) => {
      // 清理所有纹理
      for (const key in mat) {
        if (mat[key] && typeof mat[key].dispose === 'function') {
          mat[key].dispose()
        }
      }
      mat.dispose()
    })
  }
}
 
// 使用
disposeObject(oldObject)
scene.remove(oldObject)

相关链接

下一步