Cesium贴地效果实战:用CLAMP_TO_GROUND和clampToGround搞定点、线、面、模型(Vue3版)

在三维地理信息系统中,让实体完美贴合起伏地形是提升场景真实感的关键技术。Cesium作为领先的WebGIS引擎,提供了 HeightReference.CLAMP_TO_GROUND clampToGround 两种核心贴地方案。本文将深入解析它们在点、线、面、模型等不同实体类型上的应用差异,结合Vue3框架给出可复用的解决方案,帮助开发者规避常见的闪烁、穿模等问题。

1. 贴地技术原理与基础配置

1.1 地形数据准备

实现贴地效果前,必须确保场景加载了高精度地形数据。推荐使用Cesium Ion提供的全球地形服务:

const viewer = new Cesium.Viewer("cesiumContainer", {
  terrainProvider: Cesium.createWorldTerrain({
    requestWaterMask: true,  // 请求水域效果
    requestVertexNormals: true  // 请求光照数据
  })
})

关键参数说明

  • requestWaterMask :启用动态水面效果
  • requestVertexNormals :使地形具备光照响应能力

1.2 深度检测启用

必须显式开启地形深度测试才能获得正确的遮挡关系:

viewer.scene.globe.depthTestAgainstTerrain = true

注意:该设置会影响性能,在低端设备上可能导致帧率下降。可通过 viewer.scene.globe.depthTestAgainstTerrain = false 临时关闭。

2. 点状实体贴地方案

2.1 基础点要素配置

使用 CLAMP_TO_GROUND 实现点要素贴地:

viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(116.39, 39.9),
  point: {
    pixelSize: 15,
    color: Cesium.Color.RED,
    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
  }
})

2.2 广告牌(Billboard)特殊处理

广告牌需要额外设置 disableDepthTestDistance 避免被地形裁剪:

viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(116.39, 39.9),
  billboard: {
    image: '/icons/pin.png',
    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
    disableDepthTestDistance: Number.POSITIVE_INFINITY
  }
})

性能优化技巧

  • 对静态广告牌设置 disableDepthTestDistance: 0 可提升渲染效率
  • 动态广告牌建议使用有限距离值,如 disableDepthTestDistance: 1000

3. 线状实体贴地方案

3.1 折线(Polyline)两种方案对比

方案 API 适用场景 性能影响
顶点采样 sampleTerrainMostDetailed 静态线路,高精度要求
实时贴地 clampToGround: true 动态线路,实时性要求高

顶点采样方案代码

const positions = Cesium.Cartesian3.fromDegreesArray([...])
Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, positions)
  .then(samples => {
    viewer.entities.add({
      polyline: {
        positions: samples,
        width: 5,
        material: Cesium.Color.BLUE
      }
    })
  })

3.2 动态线路优化技巧

对于需要频繁更新的动态线路,推荐组合使用:

const dynamicLine = viewer.entities.add({
  polyline: {
    positions: new Cesium.CallbackProperty(() => {
      return updateLinePositions()
    }, false),
    clampToGround: true,
    width: 3
  }
})

提示:设置 CallbackProperty 的第二个参数为false可避免不必要的实时计算

4. 面状实体与模型贴地

4.1 多边形(Polygon)处理方案

面状实体需要特殊处理边缘锯齿问题:

viewer.entities.add({
  polygon: {
    hierarchy: Cesium.Cartesian3.fromDegreesArray([...]),
    material: Cesium.Color.GREEN.withAlpha(0.6),
    classificationType: Cesium.ClassificationType.TERRAIN
  }
})

常见问题解决

  • 闪烁问题:检查 classificationType 是否设置为TERRAIN
  • 边缘锯齿:增加多边形边缘采样点密度

4.2 3D模型贴地实战

GLB模型需要同时设置高度参考和调整姿态:

const modelEntity = viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(116.39, 39.9),
  model: {
    uri: '/models/building.glb',
    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
    minimumPixelSize: 64,
    maximumScale: 200
  }
})

// 获取地形高度并调整模型姿态
Cesium.sampleTerrain(viewer.terrainProvider, 11, [position])
  .then(updatedPositions => {
    modelEntity.position = updatedPositions[0]
  })

模型穿模解决方案

  1. 在建模软件中为模型添加基础底板
  2. 使用 Cesium.HeightReference.RELATIVE_TO_GROUND 并设置适当偏移
  3. 对复杂模型进行LOD分级处理

5. Vue3组件化实践

5.1 可复用贴地组件设计

创建通用的CesiumEntity组件:

<template>
  <div ref="container"></div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const props = defineProps({
  type: String,
  positions: Array,
  options: Object
})

const container = ref(null)
let entity = null

onMounted(() => {
  const viewer = window.viewer // 假设viewer已全局注册
  entity = viewer.entities.add({
    [props.type]: {
      ...props.options,
      positions: Cesium.Cartesian3.fromDegreesArray(props.positions)
    }
  })
})
</script>

5.2 组合式API封装

创建useCesiumEntity组合函数:

import { onUnmounted } from 'vue'

export function useCesiumEntity(viewer, config) {
  const entity = viewer.entities.add(config)
  
  onUnmounted(() => {
    viewer.entities.remove(entity)
  })

  return {
    entity,
    update: (newConfig) => {
      Object.assign(entity, newConfig)
    }
  }
}

在组件中使用:

<script setup>
import { useCesiumEntity } from './composables/useCesiumEntity'

const { entity } = useCesiumEntity(viewer, {
  position: Cesium.Cartesian3.fromDegrees(116.39, 39.9),
  point: {
    color: Cesium.Color.RED,
    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
  }
})
</script>

6. 性能优化与调试技巧

6.1 内存管理最佳实践

  • 使用 entityCollection 管理同类实体
  • 对不再需要的实体调用 viewer.entities.remove(entity)
  • 批量操作时使用 viewer.entities.suspendEvents() resumeEvents()

6.2 调试工具推荐

  1. Cesium Inspector:通过 viewer.extend(Cesium.viewerCesiumInspectorMixin) 启用
  2. 性能监测:
viewer.scene.debugShowFramesPerSecond = true
  1. 地形线框模式:
viewer.scene.globe.showWireframe = true

6.3 移动端适配方案

  • 降低地形细节: viewer.terrainProvider.quality = 0.5
  • 简化实体数量:使用实例化渲染替代独立实体
  • 启用分级加载:
viewer.scene.globe.dynamicScreenSpaceError = true
viewer.scene.globe.dynamicScreenSpaceErrorDensity = 0.00278

更多推荐