Cesium中的视频投影是指将视频作为一种物体材质,实现在物体上播放视频的效果。这个功能在Cesium早期版本中就支持了,在Code Example中有一个示例。今天就来分析一下其内部实现原理。

1. 添加视频投影及效果

  示例中添加视频投影的代码分为两部分,第一步是添加div控件,控件负责视频播放、暂停等任务,代码如下:

<video id="trailer" muted autoplay loop crossorigin controls>
<source src="https://cesiumjs.org/videos/Sandcastle/big-buck-bunny_trailer.webm" type="video/webm">
<source src="https://cesiumjs.org/videos/Sandcastle/big-buck-bunny_trailer.mp4" type="video/mp4">
<source src="https://cesiumjs.org/videos/Sandcastle/big-buck-bunny_trailer.mov" type="video/quicktime">
Your browser does not support the <code>video</code> element.
</video>

第二步是添加一个球状物体,并为其指定材质,代码如下:

1 var videoElement = document.getElementById('trailer');//获得video对象
2 var sphere = viewer.entities.add({
3 position : Cesium.Cartesian3.fromDegrees(-79, 39, 1000),
4 ellipsoid : {
5 radii : new Cesium.Cartesian3(1000, 1000, 1000),
6 material : videoElement //指定材质
7 }
8 });

运行程序,得到的效果如下图所示:

2. 内部代码实现

  在没有查看Cesium实现视频投影原理之前,我们可以大胆猜测实现的基本思路:视频不就是连续的照片组合在一起播放吗?那通过不断更换照片就可以实现视频投影!好,那我们就按照这个思路去查看一下相关代码。在Material.js中查找到了createTexture2DUpdateFunction这个函数,其中和video材质相关的部分代码如下:

 1 var uniforms = material.uniforms;
2 var uniformValue = uniforms[uniformId];
3 var uniformChanged = oldUniformValue !== uniformValue;
4 oldUniformValue = uniformValue;
5 var texture = material._textures[uniformId];
6
7 var uniformDimensionsName;
8 var uniformDimensions;
9
10 if (uniformValue instanceof HTMLVideoElement) {
11 // HTMLVideoElement.readyState >=2 means we have enough data for the current frame.
12 // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState
13 if (uniformValue.readyState >= 2) {
14 if (uniformChanged && defined(texture)) {
15 if (texture !== context.defaultTexture) {
16 texture.destroy();
17 }
18 texture = undefined;
19 }
20
21 if (!defined(texture) || texture === context.defaultTexture) {
22 texture = new Texture({
23 context : context,
24 source : uniformValue
25 });
26 material._textures[uniformId] = texture;
27 return;
28 }
29
30 texture.copyFrom(uniformValue);
31 } else if (!defined(texture)) {
32 material._textures[uniformId] = context.defaultTexture;
33 }
34 return;
35 }

从上面的代码可以看出,texture的更新可以分为三个状态:

  视频未加载完成:此时的texture赋值为context.defaultTexture,用默认图片代替;

  视频刚加载完成:通过video构造新的texture对象,并通过texture的copyFrom函数进行更新;

  视频加载完成后:只需通过texture的copyFrom函数进行更新即可。

构造texture对象属于常规操作,所以实现视频投影中画面内容更新的重要函数就是copyFrom。截取函数中部分代码如下:

 1 if (arrayBufferView) {
2 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
3 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
4
5 if (flipY) {
6 arrayBufferView = PixelFormat.flipY(arrayBufferView, pixelFormat, pixelDatatype, width, height);
7 }
8 gl.texSubImage2D(target, 0, xOffset, yOffset, width, height, pixelFormat, pixelDatatype, arrayBufferView);
9 } else {
10 // Only valid for DOM-Element uploads
11 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha);
12 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
13
14 gl.texSubImage2D(target, 0, xOffset, yOffset, pixelFormat, pixelDatatype, source);
15 }

其中,红色部分的代码是针对dom元素的,用到的关键函数就是texSubImage2D。这个函数可以动态的更新GPU中绑定的图片内容,相当于每次都从video元素中取当前播放的图片作为材质的当前贴图,达到视频投影的效果。texSubImage2D函数的全部用法如下:

 1 // WebGL 1:
2 void gl.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, ArrayBufferView? pixels);
3 void gl.texSubImage2D(target, level, xoffset, yoffset, format, type, ImageData? pixels);
4 void gl.texSubImage2D(target, level, xoffset, yoffset, format, type, HTMLImageElement? pixels);
5 void gl.texSubImage2D(target, level, xoffset, yoffset, format, type, HTMLCanvasElement? pixels);
6 void gl.texSubImage2D(target, level, xoffset, yoffset, format, type, HTMLVideoElement? pixels);
7 void gl.texSubImage2D(target, level, xoffset, yoffset, format, type, ImageBitmap? pixels);
8
9 // WebGL 2:
10 void gl.texSubImage2D(target, level, xoffset, yoffset, format, type, GLintptr offset);
11 void gl.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, HTMLCanvasElement source);
12 void gl.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, HTMLImageElement source);
13 void gl.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, HTMLVideoElement source);
14 void gl.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, ImageBitmap source);
15 void gl.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, ImageData source);
16 void gl.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, ArrayBufferView srcData, srcOffset);

3. 视频投影的应用

  这个例子中是将视频投影到一个规则球体上,比较简单。根据这个原理,还可以引申出一些更加有实用价值的功能。比如将视频投影到gltf模型或者3dtiles模型上,将摄像头拍摄的视频投影到真实场景中,使监控更加直观、立体。下面是做的一个将视频投影到3dtiles的效果:

PS:Cesium交流可以扫码加群,期待你的加入!!!

Cesium源码剖析---视频投影的更多相关文章

  1. Cesium源码剖析---Clipping Plane

    之前就一直有写博客的想法,别人也建议写一写,但一直没有动手写,自己想了一下原因,就一个字:懒.懒.懒.为了改掉这个毛病,决定从今天开始写博客了,一方面对自己掌握的知识做一个梳理,另一方面和大家做一个交 ...

  2. Cesium源码剖析---Post Processing之物体描边(Silhouette)

    Cesium在1.46版本中新增了对整个场景的后期处理(Post Processing)功能,包括模型描边.黑白图.明亮度调整.夜视效果.环境光遮蔽等.对于这么炫酷的功能,我们绝不犹豫,先去翻一翻它的 ...

  3. Cesium源码剖析---Ambient Occlusion(环境光遮蔽)

    Ambient Occlusion简称AO,中文没有太确定的叫法,一般译作环境光遮蔽.百度百科上对AO的解释是这样的:AO是来描绘物体和物体相交或靠近的时候遮挡周围漫反射光线的效果,可以解决或改善漏光 ...

  4. ffmpeg/ffplay源码剖析笔记<转>

    转载:http://www.cnblogs.com/azraelly/ http://www.cnblogs.com/azraelly/archive/2013/01/18/2865858.html ...

  5. Qt信号槽源码剖析(一)

    大家好,我是IT文艺男,来自一线大厂的一线程序员 大家在使用Qt开发程序时,都知道怎么使用Qt的信号槽,但是Qt信号槽是怎么工作的? 大部分人仍然不知道:也就是说大家只知道怎么使用,却不知道基于什么原 ...

  6. Qt信号槽源码剖析(二)

    大家好,我是IT文艺男,来自一线大厂的一线程序员 上节视频给大家讲解了Qt信号槽的基本概念.元对象编译器.示例代码以及Qt宏:今天接着深入分析,进入Qt信号槽源码剖析系列的第二节视频. Qt信号槽的宏 ...

  7. (文字版)Qt信号槽源码剖析(三)

    大家好,我是IT文艺男,来自一线大厂的一线程序员 上节视频给大家讲解了Qt信号槽的Qt宏展开推导:今天接着深入分析,进入Qt信号槽源码剖析系列的第三节视频. Qt信号槽宏推导归纳 #define si ...

  8. jQuery之Deferred源码剖析

    一.前言 大约在夏季,我们谈过ES6的Promise(详见here),其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样, ...

  9. Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现

    声明:本文为原创博文,转载请注明出处. Nodejs编程是全异步的,这就意味着我们不必每次都阻塞等待该次操作的结果,而事件完成(就绪)时会主动回调通知我们.在网络编程中,一般都是基于Reactor线程 ...

随机推荐

  1. HTML标签一览

    html标签属性大全 嵌套的html窗口<iframe > <iframe src="https://www.baidu.com"></iframe& ...

  2. 基于nginx的rtmp直播服务器(nginx-rtmp-module实现)

    首先,在搭建服务之前先了解下目前主流的几个直播协议: 1.RTMP: 实时消息传输协议,Real Time Messaging Protocol,是 Adobe Systems 公司为 Flash 播 ...

  3. 系统分析师教程(张友生)高清pdf下载

    最近准备考系统分析师,故找了一本张又生编著的<系统分析师教程>的电子书,本来想买本书,可惜有点小贵,舍不得,故寻找电子版下载,花了不少时间才找到,现在分享给大家. http://item. ...

  4. 关于编写c++动态库常用的定义

    1. 关于 1.1 最近一段时间,写了不少动态库,慢慢的也积累了东西. 1.2 之前一直做Windows的动态库,没有做过Linux和OS X的动态库,太缺乏经验了: 代码缺乏 编译器支持的判断.缺乏 ...

  5. 【LeetCode】237. Delete Node in a Linked List 解题报告 (Java&Python&C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 设置当前节点的值为下一个 日期 [LeetCode] ...

  6. 【LeetCode】268. Missing Number 解题报告(Java & Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 求和 异或 日期 题目地址:https://leet ...

  7. 【LeetCode】559. Maximum Depth of N-ary Tree 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 DFS BFS 日期 题目地址:https://le ...

  8. 1632 B君的连通

    B国拥有n个城市,其交通系统呈树状结构,即任意两个城市存在且仅存在一条交通线将其连接.A国是B国的敌国企图秘密发射导弹打击B国的交通线,现假设每条交通线都有50%的概率被炸毁,B国希望知道在被炸毁之后 ...

  9. AlexNet: ImageNet Classification with Deep Convolutional Neural Networks

    目录 激活函数 防止过拟合 增加数据 Dropout 细节 代码 AlexNet 上图是论文的网络的结构图,包括5个卷积层和3个全连接层,作者还特别强调,depth的重要性,少一层结果就会变差,所以这 ...

  10. 编写Java程序,创建Dota游戏中的防御塔类,通过两个坐属性显示防御塔所在的位置

    返回本章节 返回作业目录 需求说明: 创建Dota游戏中的防御塔类 通过两个坐属性显示防御塔所在的位置 实现思路: 创建防御塔(TowerDefense)类 在该类中定义了两个属性,分别是int类型横 ...