Cesium源碼剖析---視頻投影


  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交流可以掃碼加群,期待你的加入!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM