Appearance
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)
}| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
position | Cartesian3 | — | 视频相机站址(世界坐标),必填,缺省抛错 |
target | Cartesian3 | — | 视频相机瞄准点,必填且须与 position 不同 |
fov | number | 60 | 纵向视场角(度),须在 (0, 180) |
near | number | 0.1 | 近裁剪面(米),须大于 0 |
far | number | 2000 | 远裁剪面(米),须大于 near;同时作为分析式阴影立方图半径 |
aspectRatio | number | 16/9 | 相机宽高比(宽/高),须大于 0。应与视频画面宽高比一致以免拉伸 |
alpha | number | 1 | 视频混合透明度(0~1),须在 [0, 1] |
videoElement | HTMLVideoElement | — | 复用已有视频元素;与 videoUrl 二选一(同时提供抛错) |
videoUrl | string | — | 视频地址,内部创建 video(autoplay/muted/loop/crossOrigin);与 videoElement 二选一(都不提供抛错) |
debugFrustum | boolean | true | 是否绘制取景视锥线框(调试视频相机取景范围) |
frustumColor | Color | YELLOW.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 // 等价于 offon 重复调用为 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 → in、gl_FragColor → out_FragColor、texture2D → texture;深度重建 builtins(czm_inverseProjection/czm_unpackDepth/czm_reverseLogDepth/czm_depthRange/czm_inverseView/czm_inverseViewRotation)与遮挡判定(czm_shadowDepthCompare/czm_private_shadowVisibility)均为引擎公共 GLSL,与 ViewShedEffect 同一套; - 私有 API 使用:
scene.context、scene._frameState、shadowMap._shadowMapTexture/_lightPositionEC/_distance/_pointBias/_textureSize经Reflect.get读取,已在 1.133 引擎核验存在(对齐 ViewShedEffect 既有先例,需浏览器实测编译); - 资源所有权:本效果创建的 stage、视锥线框、视频纹理与 tick 监听由
off/destroy全部移除,并还原scene.shadowMap。
清理责任
ts
effect.destroy(viewer) // 或 effect.off(viewer),等价且幂等重复调用 destroy/off 安全。
Cesium 版本限制
- 依赖
ShadowMap(分析式)、PostProcessStage、Camera、scene.context与_frameState等私有/半私有能力; - 视频纹理刷新依赖 1.133 的
Texture.copyFrom(options)新签名; - 着色器为 GLSL 3.00,依赖 1.133 后处理编译管线;
- 已验证目标版本:Cesium 1.133.1。