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

  • 相关阅读:
    作业3
    学习进度
    作业2
    介绍自己,并介绍github注册过程和初步使用
    mysql显示中文乱码问题解决
    数学能力测试
    C++ Primer总结
    shell脚本
    完全二叉树
    test命令的测试功能
  • 原文地址:https://www.cnblogs.com/webgl-angela/p/9809307.html
Copyright © 2020-2023  润新知