Skip to content

VideoFusionEffect 视频融合投影

以「站址 + 瞄准点」定义一个虚拟视频相机,把视频画面透视投影到场景任意表面(地形 / 3D Tiles / 实体),并做深度遮挡判定:被视频相机视角内前方几何遮挡的像素不贴视频,模拟真实监控相机与三维场景的融合。用于监控画面融合、数字孪生实景叠加、视频指挥一张图等场景。


能力定位

VideoFusionEffect 适合:

  • 视频实景融合:把监控/直播视频透视贴合到倾斜摄影或建筑表面;
  • 数字孪生叠加:将实时视频流按真实相机位姿投影到三维模型,获得「视频即场景」的观感;
  • 遮挡仿真:前方建筑挡住目标时不误贴视频,保持真实遮挡关系。

VideoMaterialProperty(把视频贴到多边形表面)和 VideoWallEffect(区域视频墙)不同:后两者只做表面纹理贴合,没有真实相机位姿与遮挡关系;VideoFusionEffect 建立的是针孔相机投影模型 —— 视频画面按站址/方位/俯仰/视场角投射进场景,几何遮挡实时生效。

ViewShedEffect 同族:复用其分析式 ShadowMap 遮挡机制(fromLightSource:false,不参与场景光照),仅用于判断「像素是否在视频相机可见范围内」。

构造函数

ts
constructor(options: VideoFusionEffectOptions)
ts
interface VideoFusionEffectOptions {
  position: Cartesian3          // 视频相机站址(世界坐标,必填)
  target: Cartesian3            // 视频相机瞄准点(世界坐标,必填且与 position 不同)
  fov?: number                  // 纵向视场角(度),默认 60
  near?: number                 // 近裁剪面(米),默认 0.1
  far?: number                  // 远裁剪面(米),默认 2000;同时作为遮挡深度立方图半径
  aspectRatio?: number          // 相机宽高比(宽/高),默认 16/9;应与视频画面宽高比一致
  alpha?: number                // 视频混合透明度(0~1),默认 1
  videoElement?: HTMLVideoElement // 视频元素,与 videoUrl 二选一
  videoUrl?: string             // 视频地址,内部创建 video(autoplay/muted/loop);与 videoElement 二选一
  debugFrustum?: boolean        // 是否绘制取景视锥线框,默认 true
  frustumColor?: Color          // 视锥线框颜色,默认 YELLOW.withAlpha(0.5)
}
参数类型默认值描述
positionCartesian3视频相机站址(世界坐标),必填,缺省抛错
targetCartesian3视频相机瞄准点,必填且须与 position 不同
fovnumber60纵向视场角(度),须在 (0, 180)
nearnumber0.1近裁剪面(米),须大于 0
farnumber2000远裁剪面(米),须大于 near;同时作为分析式阴影立方图半径
aspectRationumber16/9相机宽高比(宽/高),须大于 0。应与视频画面宽高比一致以免拉伸
alphanumber1视频混合透明度(0~1),须在 [0, 1]
videoElementHTMLVideoElement复用已有视频元素;与 videoUrl 二选一(同时提供抛错)
videoUrlstring视频地址,内部创建 video(autoplay/muted/loop/crossOrigin);与 videoElement 二选一(都不提供抛错)
debugFrustumbooleantrue是否绘制取景视锥线框(调试视频相机取景范围)
frustumColorColorYELLOW.withAlpha(0.5)视锥线框颜色,对齐源案例

站址/瞄准点推导:SDK 直接用 position + target 表达相机姿态(heading/pitch 由两点方位推导)。源案例(VideoShed3d + CoordinateTransform.enu_to_ecef)以「站址 + 方位角 + 俯仰角 + 距离」表达,二者等价;换算数学保留在示例模板(stationToTarget)。

生命周期

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

ts
on(viewer: Viewer): void      // 创建虚拟视频相机 + 分析式 ShadowMap + 视频纹理 + 后处理 stage + 视锥线框
off(viewer: Viewer): void     // 移除 stage 与视锥线框、释放视频纹理与 tick 监听,并还原 on 之前的 scene.shadowMap
destroy(viewer: Viewer): void // 等价于 off

on 重复调用为 no-op;off 重复调用安全。与 ViewShedEffect 一样临时接管 scene.shadowMap,与其它可视域/阴影分析效果同时启用时以后一个 on 的效果为准。

运行时调整

ts
effect.setAlpha(0.6)          // 视频混合透明度(0~1,运行时 clamp)
effect.setFrustumVisible(false) // 开关取景视锥线框
effect.enabled                // boolean,当前是否启用

基础示例

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

const effect = new VideoFusionEffect({
  position: Cartesian3.fromDegrees(116.4480, 39.9030, 80), // 站址
  target: Cartesian3.fromDegrees(116.4540, 39.9064, 30),   // 瞄准点
  fov: 60,
  far: 1200,
  aspectRatio: 16 / 9,
  videoUrl: '/static/videos/camera.mp4', // 或传入已创建的 videoElement
})
effect.on(viewer)
// 运行时调整
effect.setAlpha(0.6)
effect.setFrustumVisible(false)
// 场景退出时
effect.destroy(viewer)

进阶示例

站址 + 方位角/俯仰角/距离 → 瞄准点(对齐源案例 enu_to_ecef),配合离线 Canvas 演示流:

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

// 站址 + 航向/仰角/距离 → 瞄准点(ENU 数学,等价于源案例 CoordinateTransform.enu_to_ecef)
function stationToTarget (lon, lat, height, azimuthDeg, elevationDeg, distance) {
  const origin = Cartesian3.fromDegrees(lon, lat, height)
  const enu = Transforms.eastNorthUpToFixedFrame(origin)
  const azimuth = CesiumMath.toRadians(azimuthDeg)
  const elevation = CesiumMath.toRadians(elevationDeg)
  const east = distance * Math.cos(elevation) * Math.sin(azimuth)
  const north = distance * Math.cos(elevation) * Math.cos(azimuth)
  const up = distance * Math.sin(elevation)
  return Matrix4.multiplyByPoint(enu, new Cartesian3(east, north, up), new Cartesian3())
}

const effect = new VideoFusionEffect({
  position: Cartesian3.fromDegrees(106.4480, 29.5030, 90),
  target: stationToTarget(106.4480, 29.5030, 90, 49, -6, 574),
  fov: 55,
  far: 920,
  videoElement: video, // Canvas captureStream 生成的离线监控画面
})
effect.on(viewer)

实现说明

  • 分析式阴影贴图:以视频相机为光源创建 new ShadowMap({ fromLightSource:false, isPointLight:true, pointLightRadius: far, cascadesEnabled:false, size, softShadows:true, normalOffset:false })fromLightSource:false 保证其不参与场景光照,仅作遮挡判定;每个 uniform 求值时调用 shadowMap.update(getFrameState(viewer)) 按帧刷新;
  • 屏幕空间后处理:PostProcessStage 片元着色器对每个像素做 depthTexture 解包 → czm_inverseProjection 反投影重建视空间 → czm_inverseView 转世界 → camera_view_matrix 变换进视频相机空间;落在视频相机视锥内(visible(ndc))再 czm_shadowVisibility 判定遮挡,可见处按视频帧混合;天空(无几何)直接跳过;
  • 视频纹理:创建视频尺寸纹理后逐帧 Texture.copyFrom({ source: video }) 刷新;视频元数据未就绪时先建 1×1 占位,tick 中就绪后自动升采样到视频尺寸(copyFrom 会校验源尺寸不超过纹理尺寸);
  • GLSL 3.00 迁移:源着色器(Cesium 1.72)按 1.133 语法迁移 —— varying → ingl_FragColor → out_FragColortexture2D → texture;深度重建 builtins(czm_inverseProjection/czm_unpackDepth/czm_reverseLogDepth/czm_depthRange/czm_inverseView/czm_inverseViewRotation)与遮挡判定(czm_shadowDepthCompare/czm_private_shadowVisibility)均为引擎公共 GLSL,与 ViewShedEffect 同一套;
  • 私有 API 使用scene.contextscene._frameStateshadowMap._shadowMapTexture/_lightPositionEC/_distance/_pointBias/_textureSizeReflect.get 读取,已在 1.133 引擎核验存在(对齐 ViewShedEffect 既有先例,需浏览器实测编译);
  • 资源所有权:本效果创建的 stage、视锥线框、视频纹理与 tick 监听由 off/destroy 全部移除,并还原 scene.shadowMap

清理责任

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

重复调用 destroy/off 安全。

Cesium 版本限制

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