Github项目地址
为了方便没有准备好梯子的同学,我把项目在CSDN上打包下载,不过更新会慢一些
回到目录
恭喜Martin同学获得由CSDN颁发的“更新慢慢慢”荣誉称号
全景视频有很多种类,例如Sphere全景,Skybox(Cubemap),Cylinder等,我们以最为常见的Sphere全景视频为例进行说明。
原理
其实要说明这个问题,只需要几张图
- 360全景视频截图
仔细观察我们会发现,视频的底部基本都是一个区域,而视频的最左侧和最右侧是可以连接起来的 贴图
全景视频的原理,就是将一张平面图片贴到球体上,红色的 区域是用户的手机屏幕,可视区域就是图中的蓝色部分,使用透视投影(就是近大远小)将这一部分压缩到手机屏幕的这块区域,这样,之前平面图片的变形就被补偿了(当然,可视区域周围确实依然存在形变,尤其当视角较大的时候),同时当用户拿着手机旋转时,我们只需要将球以相反的方向旋转即可。地图
看着这张图,再看看地球仪,是不是和全景视频的情况几乎一致?唯一的区别是,看地球仪的时候我们站在球外,而全景视频往往站在球内比例问题
球体的经度是360度,纬度是180度,所以全景视频的比例通常为2:1,而且由于用户的可视区域往往较小,所以全景视频的分辨率通常较高(1080P是起码要求),对于播放器的要求也就较高。
之前在一个交流群里看到有人问怎么用程序判断一个视频是否是全景视频(要是每个视频都带Exif这种标记就好了),我能想到的判断方法是:- 分辨率较高
- 宽高比例十分接近2:1
- 视频第一行同色
- 视频最后一行同色
- 视频第一列和最后一列颜色十分接近
当然,看一眼应该就能看出来了(好像是废话)
实践
原理理解了,那要如何实现把视频贴到球上这个步骤呢?
这里再盗用邱佳迪大大的几张图:
球面被切分成若干个三角形(因为三角形是OpenGL的基本形状),并且这些三角形在球面上是等大的,但是在对应的平面上疏密不等(纬度越高越稀疏)
请注意,真正在实现球体绘制的时候采用的不是这种方案而是类似经线纬线的画法
OpenGL支持纹理贴图,其实和我们之前显示一张图片在平面上的原理完全一致,只不过这次平面变成了球面
用过Unity的应该都知道,U3D中的材质球就是这个样子:
将图片贴到球上以后,要如何显示出来呢?这就需要用到MVP矩阵了(Model-View-Projection 模型-视图-投影矩阵)
在OpenGL中,矩阵被定义为一个列主序的3x3或者4x4的数组,如下:
/*** Matrix math utilities. These methods operate on OpenGL ES format* matrices and vectors stored in float arrays.* <p>* Matrices are 4 x 4 column-vector matrices stored in column-major* order:* <pre>* m[offset + 0] m[offset + 4] m[offset + 8] m[offset + 12]* m[offset + 1] m[offset + 5] m[offset + 9] m[offset + 13]* m[offset + 2] m[offset + 6] m[offset + 10] m[offset + 14]* m[offset + 3] m[offset + 7] m[offset + 11] m[offset + 15]</pre>** Vectors are 4 x 1 column vectors stored in order:* <pre>* v[offset + 0]* v[offset + 1]* v[offset + 2]* v[offset + 3]</pre>*/
MVP矩阵的含义分别是
- Model:模型矩阵,用于定义单个物体的位移和朝向,只会改变单个物体的状态
- View: 视图矩阵,用来定义相机的位移和朝向,会改变整个场景
- Projection: 投影矩阵,定义场景到视口(viewport,可以看成屏幕)的投影方式,决定显示方式,在之前的显示图片和播放平面视频的例子里面,我们使用了正交投影来保证显示不会变形,同时适应视频尺寸和屏幕尺寸
其实这些只是人为定义,这些矩阵的形式是完全一样的,你完全可以用model矩阵来表示相机的位置,但是不会有人建议你这么做
那为什么这三种矩阵能实现不同效果呢?其实这和矩阵的个数有关:
- 每个物体有一个Model矩阵
- 每个相机(每只眼睛)有一个View矩阵
- 每个场景(Scene)有一个Projection矩阵
有一张挺好的图,可以加深理解:
当然,在全景视频播放中,并不需要这么复杂,而且改变modelMatrix 和反向改变viewMatrix 是等效的,在全景视频播放器中这三个矩阵起到的功能如下:
private float[] modelMatrix = new float[16]; //球体 拖动、传感器数据改变时改变
private float[] viewMatrix = new float[16]; //观看角度
private float[] projectionMatrix = new float[16];//投影变换,用于缩放
透视投影将球面投影到屏幕的原理可以参考下图:
白色平面就是我们的近平面,从近平面到无穷远的这一块球面区域会被压缩到屏幕上,就出现了我们看到的效果(正因为如此,视角大的时候就容易形变了)
关于如何拍摄全景视频以及其他全景视频类型可以参考这里
Github项目地址
回到目录