之前有一段时间趁热研究了一下VR播放技术。主要研究的全景播放技术实现和VR播放技术实现,两者播放实现有一定参考性。下面简单的进行总结一下。

全景播放

全景播放目前大致分两种方式:球面投影,立方体投影时。目前网络上视频源还是以球面投影为主。本文也主要讲的是球面投影全景。

球面投影的原理如下图:

对球体等分经纬度,然后均匀展开为一副平面图像,这个过程对应全景视频编码的过程。全景视频编码后是一个矩形的视频流。而全景播放则需要相反的操作,即讲矩形视频流按等分经纬度投射的球体上。

这个用OpenGL ES可以方便的实现。先生成球体的三维顶点数组,视频数据作为纹理数据,等分为顶点对应二维纹理坐标数组。然后将纹理贴图渲染于球面上。

全景播放过程就是要从球体中心点为观察点,沿着一定的视角进行观看。手机上可以根据陀螺仪数据调整角度。

最终效果

VR播放

VR播放这里指头戴设备播放3D视频。这里视频一般为上下格式,上面和下面各为一个全景视频源。播放原理和全景播放类似,只不过这里会分别渲染两幅视频到一半的屏幕,然后通过头戴设备观察。为了获得逼真的沉浸感,需要加个透镜来扩大视角,这是VR需要头戴设备的主要原因。

透镜的原理如下:

通过透镜我们以较小的屏幕区域,获得非常大的视角,这样观察到VR效果将会更加有身临其境的感觉。

畸变

当加入透镜后,会引入一个额外的问题:畸变。如下图

左侧为观察到的图像,右侧的实际头像,当我们通过透镜观察时,我们看到的实际头像会变形,凸透镜会有个网形畸变(如左图)。

为了使我们看到的图像为正常,我们可以对原始图像进行一个矫正,即管形畸变:

透镜的畸变效果如下:

图像各个点的畸变和这个点到中心点的距离相关,越是靠近边缘部分畸变程度愈大。

一般透镜可以参考一些光学折射研究,可以用以下公式来表示点的偏移:

这里k值为正值是为网形畸变,负值则为管形畸变。

反畸变性能考虑

反畸变一般可为下面几种方式。

像素级反畸变

对每个像素进行偏移。先将图像以不畸变的方式渲染到一个临时区域,然后临时区域作为纹理,通过片段着色器位移纹理坐标渲染到手机屏幕上。

网格反畸变

对图像进行网格划分,对网格顶点进行偏移

顶点偏移反畸变

上面两种方式都需要先渲染一般正常图像,然后在对看到的实际效果进行反畸变矫正,这样会造成渲染两遍。

顶点偏移直接对需要渲染球体顶点进行偏移,只需要渲染一次。性能效果最好。这里需要动态的根据观察角度,对球体顶点进行偏移。步骤如下:

1. 转换为相机坐标
1
vec4 pos = modelViewMatrix * Position;

将球体顶点坐标转换为相对相机(观察者视角)的相对坐标。以便计算顶点距离视角中心的距离

2. 计算半径平方并normalize
1
float r2 = dot(pos.xy, pos.xy) / (300.0*300.0*2.0);

由于相对坐标pos已经观察者看到的坐标了,直接计算出水平半径平方。这里z坐标不需计算,但可以作为裁剪参数,z为负时表示位于观察者背后,是看不到的,可以直接裁剪掉。

3. 计算出偏移后的坐标
1
pos.xy *= 1.0 + p[0]*r2 + p[1]*r2*r2 + p[2]*r2*r2*r2;

根据透镜偏移公式计算出偏移后的位置。

4. 根据投影矩阵计算裁剪坐标
1
gl_Position = projectionMatrix * pos;

和暴风魔镜App的对比

这里用了顶点偏移反畸变方式实现的效果与暴风魔镜官方App进行对比。播放视频为3dvr.mp4,该视频分辨率为2560*1440,目前从Profile工具测试,暴风魔镜官方App播放时的帧率为35左右,而此Demo播放帧率为58左右,几乎可以满帧率运行。

项目地址:https://github.com/shenqiliang/QVR

参考文章