OpenGL ES学习实战(360全景视频播放器)
全景视频原理
一、拍摄设备
全景视频在拍摄时是多个摄像机同时在一个点向四面八方拍摄。下面在网上找了一个拍摄设备的图片。
摄像机
我在面试的那家做VR视频的公司见到了他们的拍摄设备发面只有2个摄像头,类似下面的图片里的设备,但通过只有2个摄像头的设备来拼接成的全景视频在移动视角时会有强烈的拉伸感,我在观看一些小公司的App里的全景视频时会有这样的体验,具体视频质量的好坏这里就不深入讨论了大家可以自己在各平台对比体验一下就知道了。
拍摄设备
如果要拍摄的是VR视频每个方向上会有2个摄像头(区分左右眼),不过这种视频很少见。个人感觉很多公司都是通过全景视频做一下处理来生成VR视频的,所以没有很强的立体感,这也是个人的感觉,如果有对VR视频了解更多的大神可以在下面评论处说明以供大家共同学习。
二、视频拼接
后期视频的制作会将视频目标当作一个球来制作成视频,也就是说最后的视频是要渲染到一个球上面的。可以想像如果用平常的播放器来播放的效果图像的上面和下面是被拉伸的,下面是一个在网上下载的全景视频在平常的播放器播放的效果,可以看出下面的路面被严重拉伸,上面其实也被拉伸了因为是黑色的所以看着不太明显。
最后这个视频是要渲染到一个球上面的,而我们的视角在是在球的中心点,这样就可以向四面八方去观看了。下面是一个我做好的播放器里播放的效果,有对比才能看出图像被拉伸的情况。代码实现
一、生成顶点数据
从上面的介绍可知我们只要生成一个球体并将视频的每一帧渲染到球上面就可以了。怎么生成球体可以看我另一篇博客OpenGL ES学习笔记之四(创建球体),这里只简单介绍一下。生成球的顶点信息:
/**
绘制一个球的顶点
@param num 传入要生成的顶点的一层的个数(最后生成的顶点个数为 num * num)
@return 返回生成后的顶点
*/- (Vertex *)getBallDevidNum:(GLint) num{
if (num % 2 == 1) {
return 0;
}
GLfloat delta = 2 * M_PI / num; // 分割的份数
GLfloat ballRaduis = 0.3; // 球的半径
GLfloat pointZ;
GLfloat pointX;
GLfloat pointY;
GLfloat textureY;
GLfloat textureX;
GLfloat textureYdelta = 1.0 / (num / 2);
GLfloat textureXdelta = 1.0 / num;
GLint layerNum = num / 2.0 + 1; // 层数
GLint perLayerNum = num + 1; // 要让点再加到起点所以num + 1
Vertex * cirleVertex = malloc(sizeof(Vertex) * perLayerNum * layerNum);
memset(cirleVertex, 0x00, sizeof(Vertex) * perLayerNum * layerNum);
// 层数
for (int i = 0; i < layerNum; i++) {
// 每层的高度(即pointY),为负数让其从下向上创建
pointY = -ballRaduis * cos(delta * i);
// 每层的半径
GLfloat layerRaduis = ballRaduis * sin(delta * i);
// 每层圆的点,
for (int j = 0; j < perLayerNum; j++) {
// 计算
pointX = layerRaduis * cos(delta * j);
pointZ = layerRaduis * sin(delta * j);
textureX = textureXdelta * j;
// 解决图片上下颠倒的问题
textureY = 1 - textureYdelta * i;
cirleVertex[i * perLayerNum + j] = (Vertex){pointX, pointY, pointZ, textureX, textureY};
}