Skip to content

VideoMaterialProperty 视频贴面材质

将视频画面实时贴到多边形表面的材质,基于 Cesium 原生 'Image' 材质类型封装。视频帧由渲染层逐帧 copyFrom 驱动,无需注册自定义 GLSL;贴面方向(stRotation)由 polygon 图形属性承载,与材质解耦。


能力定位

VideoMaterialProperty 适合:

  • 视频融合展示:监控视频 / 实时流贴到地表多边形,模拟大屏视频融合;
  • 动态纹理面:把 canvas 流(captureStream)、摄像头或视频文件作为多边形表面;
  • 视频贴面旋转:配合 polygon.stRotation 旋转贴面方向,对齐实际地理朝向。

WaterMaterialProperty(GLSL 程序化水面)不同,本材质消费 Cesium 原生 'Image' fabric(HTMLVideoElement 分支),无需宿主注册任何自定义材质。

构造函数

ts
constructor(options: VideoMaterialPropertyOptions)
ts
interface VideoMaterialPropertyOptions {
  videoElement?: HTMLVideoElement;  // 已有视频元素(优先),与 videoUrl 二选一
  videoUrl?: string;                // 视频地址,内部创建 <video> 加载;与 videoElement 二选一
  repeat?: Cartesian2 | Property;   // 纹理平铺,默认 (1,1)
  color?: Color | Property;         // 叠加颜色,默认 (1,1,1,1) 原色
}
参数类型描述
videoElementHTMLVideoElement复用已创建的视频元素(videoUrlvideoElement 必须提供其一,且只能其一)
videoUrlstring视频地址,内部创建 <video> 并设置 autoplay / muted / loop / crossOrigin='anonymous'
repeatCartesian2 | Property纹理平铺次数,默认 (1, 1)
colorColor | Property与视频画面叠加的颜色,默认 (1, 1, 1, 1)

repeat / color 支持 CallbackProperty 动态驱动。

材质属性接口

VideoMaterialProperty 实现 Cesium MaterialProperty 契约,可直接赋给实体材质:

ts
getType(time): string           // 返回 'Image'(Cesium 原生类型,无需注册)
getValue(time, result?): object // 返回 { image, repeat, color } uniforms
isConstant: boolean             // repeat/color 均为常量时为 true(视频帧恒为动态)
definitionChanged: Event        // 属性赋值时触发
equals(other): boolean          // 比较视频源 + repeat/color 属性

视频帧动态性isConstant 只反映 repeat / color 是否常量。视频画面本身每帧变化, 由 Cesium 渲染层在 HTMLVideoElement.readyState >= 2 时逐帧 texture.copyFrom 驱动, 因此材质永远"动态",但不影响 isConstant 语义(纹理内容不参与常量判定)。

基础示例(videoUrl)

ts
import { VideoMaterialProperty } from '@nexa/gis-cesium'
import { Cartesian3, CallbackProperty, Math as CesiumMath } from 'cesium'

const material = new VideoMaterialProperty({
  videoUrl: '/static/videos/camera.mp4',
})

const entity = viewer.entities.add({
  polygon: {
    hierarchy: Cartesian3.fromDegreesArray([
      114.225, 30.605,
      114.248, 30.605,
      114.248, 30.618,
      114.225, 30.618,
    ]),
    perPositionHeight: true,
    material,
  },
})

// 贴面旋转:stRotation 是 polygon 属性(弧度),与材质解耦
entity.polygon.stRotation = new CallbackProperty(
  () => CesiumMath.toRadians(180),
  false
)

进阶示例(videoElement + canvas 实时流)

零资产演示:用 canvas.captureStream(30) 程序化生成动态"监控画面",贴到地表多边形。

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

// 1. 程序化画面(零外部资产)
const canvas = document.createElement('canvas')
canvas.width = 320
canvas.height = 180
const ctx = canvas.getContext('2d')

function drawFrame () {
  ctx.fillStyle = 'hsl(210, 80%, 45%)'
  ctx.fillRect(0, 0, 320, 180)
  ctx.fillStyle = 'rgba(255, 220, 120, 0.9)'
  ctx.beginPath()
  ctx.arc(120, 80, 14, 0, Math.PI * 2)
  ctx.fill()
}
drawFrame()
const timer = setInterval(drawFrame, 1000 / 30)

// 2. canvas 流 → video 元素 → 材质
const video = document.createElement('video')
video.muted = true
video.loop = true
video.srcObject = canvas.captureStream(30)
video.play()

const material = new VideoMaterialProperty({ videoElement: video })

// 3. 贴到地表多边形
viewer.entities.add({
  polygon: {
    hierarchy: Cartesian3.fromDegreesArray([
      114.225, 30.605,
      114.248, 30.605,
      114.248, 30.618,
      114.225, 30.618,
    ]),
    perPositionHeight: true,
    material,
  },
})

实现与性能说明

  • 原生 Image 材质getType() 返回 'Image',直接消费 Cesium 内置 'Image' fabric 的 HTMLVideoElement 分支,无需 registerAll 注册 GLSL;
  • 帧驱动:视频纹理由 Cesium 渲染层逐帧 Texture.copyFrom 更新,readyState >= 2 后才开始采样(不足时材质保持上一帧/空纹理);
  • videoUrl 内部创建:自动设置 autoplay / muted / loop / crossOrigin='anonymous', 避免跨域纹理污染(SecurityError);
  • stRotation 解耦:贴面旋转是 polygon 图形属性而非材质 uniform,材质保持纯净 (image / repeat / color 三件套),旋转由调用方经 entity.polygon.stRotation 设置;
  • 资源所有权:材质本身无生命周期;videoUrl 创建的视频元素与实体由创建方负责清理。

清理责任

  • 复用外部 videoElement 时,材质不暂停/移除该元素,所有权在调用方;
  • videoUrl 内部创建的视频元素随材质一起由调用方管理(暂停、srcObject = null);
  • 引用材质的实体由创建方移除:
ts
viewer.entities.remove(entity)

与原生 Cesium API 的组合方式

  • 可直接赋给 entity.polygon.material / entity.ellipsoid.material 等实体材质;
  • 等价于 entity.polygon.material = new Cesium.ImageMaterialProperty({ image: videoEl }), 但统一了 getValue / equals / definitionChanged 契约,可与其他 MaterialProperty 一样参与属性生命周期管理;
  • perPositionHeight 结合可贴合真实地形高程的视频面。

Cesium 版本限制

  • 需要 Cesium 'Image' fabric 的 HTMLVideoElement 分支(readyState >= 2 采样);
  • 已验证目标版本:Cesium 1.133.1。