Skip to content

ViewShedEffect 可视域分析

以观察点为虚拟相机建立分析式阴影贴图,对整帧做「深度重建 → 变换进虚拟相机空间 → 阴影图遮挡判定」的后处理染色:可视区染 visibleAreaColor(默认绿)、不可视区染 invisibleAreaColor(默认红)。遮挡随地形 / 3D Tiles 起伏实时变化,用于哨位通视、雷达遮蔽、反恐布控等「从某点能看到哪些区域」的分析场景。


能力定位

ViewShedEffect 适合:

  • 通视分析:从观测点判定可见/不可见区域;
  • 雷达/哨位遮挡:评估地形或建筑对视线的遮蔽;
  • 态势叠加:与 3D Tiles 倾斜摄影结合,在真实城市模型上做可视域推演。

RadarSweepEffect(屏幕空间后处理)同属 PostProcessStage 一族,可叠加共存;但可视域需要真实的遮挡判定,因此额外引入了分析式阴影贴图,作用于整帧而非局部扫描线。

构造函数

ts
constructor(options: ViewShedEffectOptions)
ts
interface ViewShedEffectOptions {
  viewPosition: Cartesian3     // 观测点位置(必填,世界坐标)
  viewPositionEnd: Cartesian3  // 目标点位置(必填,与观测点不同)
  viewDistance?: number        // 可视距离(米),默认 1000
  viewHeading?: number         // 航向角(度),默认 0
  viewPitch?: number           // 俯仰角(度),默认 0
  horizontalViewAngle?: number // 水平视场角(度),默认 90
  verticalViewAngle?: number   // 垂直视场角(度),默认 60
  visibleAreaColor?: Color     // 可视区域颜色,默认 Color.LIME
  invisibleAreaColor?: Color   // 不可视区域颜色,默认 Color.RED
}
参数类型默认值描述
viewPositionCartesian3观测点位置(世界坐标),必填,缺省抛错
viewPositionEndCartesian3目标点位置(世界坐标),必填且须与观测点不同
viewDistancenumber1000可视距离(米),须大于 0
viewHeadingnumber0航向角(度)。默认 0,对齐源案例 ViewShedStage;由两点方位推导的写法见下方进阶示例
viewPitchnumber0俯仰角(度),默认 0,同上
horizontalViewAnglenumber90水平视场角(度),须在 (0, 360)
verticalViewAnglenumber60垂直视场角(度),须在 (0, 180)
visibleAreaColorColorColor.LIME可视区域染色
invisibleAreaColorColorColor.RED不可视区域染色

viewHeading/viewPitch 默认 0(对齐源案例构造参数默认值)。源案例的交互层(ViewShedDraw + CameraLine)在两点拾取后通过 getHeading/getPitch 推导航向与俯仰,这一推导不属于核心类职责,应在调用方完成(见进阶示例)。

生命周期

实现 Lifecycle<Viewer> 契约,on/off/destroy 均幂等:

ts
on(viewer: Viewer): void      // 创建虚拟相机 + 分析式 ShadowMap + 后处理 stage + 视锥边线/草图实体
off(viewer: Viewer): void     // 移除 stage 与实体,并还原 on 之前的 scene.shadowMap
destroy(viewer: Viewer): void // 等价于 off

相比源案例 clear() 未还原 scene.shadowMap(会污染场景阴影),off/destroy无条件还原 on 之前的阴影贴图配置。

基础示例

ts
import { ViewShedEffect } from '@nexa/gis-cesium'
import { Cartesian3 } from 'cesium'

const effect = new ViewShedEffect({
  viewPosition: Cartesian3.fromDegrees(117.079958, 31.657521, 60),
  viewPositionEnd: Cartesian3.fromDegrees(117.084658, 31.654321, 8),
  viewDistance: 500,
  horizontalViewAngle: 90,
  verticalViewAngle: 60,
})
effect.on(viewer)

进阶示例

两点交互 + 方位推导(对齐源案例 ViewShedDraw/CameraLine/Utils):先拾取观测点,再拾取目标点,用 getHeading/getPitch 推导航向/俯仰,用两点距离作为可视距离。

ts
import { ViewShedEffect } from '@nexa/gis-cesium'
import { Cartesian3, Math as CesiumMath, Matrix4, Transforms } from 'cesium'

// 方位推导(对齐源案例 Utils/getHeading、getPitch)
function getHeading (from: Cartesian3, to: Cartesian3): number {
  const final = new Cartesian3()
  const m = Transforms.eastNorthUpToFixedFrame(from)
  Matrix4.inverse(m, m)
  Matrix4.multiplyByPoint(m, to, final)
  Cartesian3.normalize(final, final)
  return CesiumMath.toDegrees(Math.atan2(final.x, final.y))
}
function getPitch (from: Cartesian3, to: Cartesian3): number {
  const final = new Cartesian3()
  const m = Transforms.eastNorthUpToFixedFrame(from)
  Matrix4.inverse(m, m)
  Matrix4.multiplyByPoint(m, to, final)
  Cartesian3.normalize(final, final)
  return CesiumMath.toDegrees(Math.asin(final.z))
}

// 交互:首点 = 观测点,次点 = 目标点
let first: Cartesian3 | undefined
const handler = new ScreenSpaceEventHandler(viewer.scene.canvas)
handler.setInputAction((e) => {
  const position = viewer.scene.pickPosition(e.position)
  if (!position) return
  if (!first) {
    first = position
  } else {
    const effect = new ViewShedEffect({
      viewPosition: first,
      viewPositionEnd: position,
      viewDistance: Cartesian3.distance(first, position),
      viewHeading: getHeading(first, position),
      viewPitch: getPitch(first, position),
    })
    effect.on(viewer)
    first = undefined
  }
}, ScreenSpaceEventType.LEFT_CLICK)

实现说明

  • 分析式阴影贴图new ShadowMap({ fromLightSource: false, isPointLight: true, pointLightRadius, cascadesEnabled: false, size, softShadows, normalOffset: false }) 以虚拟相机为光源,注入 viewer.scene.shadowMapfromLightSource:false 保证其不参与场景光照,仅作遮挡判定用(对齐源案例 createShadowMap);
  • 屏幕空间后处理:PostProcessStage 片元着色器对每个像素做 depthTexture 解包 → czm_inverseProjection 反投影重建视空间坐标 → czm_inverseView 转世界坐标 → camera_view_matrix 变换进虚拟相机空间,落在视锥内再 czm_shadowVisibility 判定遮挡,可视/不可视分别 mix 染色;
  • GLSL 3.00 迁移:源着色器(Cesium 1.72)按 1.133 语法迁移 —— varying → ingl_FragColor → out_FragColortexture2D → texture;自定义 czm_shadowVisibility(samplerCube, zx_shadowParameters) 与引擎内置同名函数(czm_shadowParameters 结构体)构成重载,编译期可区分;
  • 深度重建 builtinsczm_inverseProjection / czm_unpackDepth / czm_reverseLogDepth / czm_depthRange / czm_inverseView / czm_inverseViewRotation 均在 1.133 引擎后处理管线中提供;
  • 私有 API 使用scene.context@private getter)、scene._frameStateshadowMap._shadowMapTexture/_shadowMapMatrix/_lightPositionEC/_distance/_pointBias/_textureSize 均经 Reflect.get 读取,已在 1.133 引擎逐一核验存在(见 Gate 1 风险 R3,需浏览器实测编译);
  • 资源所有权:本效果创建的 stage、视锥边线/草图实体由 off/destroy 移除,且还原 scene.shadowMap

清理责任

ts
effect.destroy(viewer) // 或 effect.off(viewer),等价且幂等

重复调用 destroy/off 安全。

Cesium 版本限制

  • 依赖 ShadowMap(分析式)、PostProcessStageCamerascene.context_frameState 等私有/半私有能力;
  • 着色器为 GLSL 3.00,依赖 1.133 后处理编译管线;
  • 已验证目标版本:Cesium 1.133.1。