本文会对facebook的开源filter:vf_transform.c 做代码级分析,解释vr视频是如何做六面体转换的。
转换的关键其实就是输入vr视频到六面体的映射(也就是下图中蓝色图像映射到红色图像):
假设每个正方形的像素是512x512个,那么对于(x, y)这个像素值来说,想得到这个值,我只需要从原点,拉一条直线连接到(x, y)并沿着这条直线一直打到球面上,得到的像素值就是(x, y)这个点的值。描述这条直线靠极坐标就可以了
变换的过程其实就是将六面体每个面每个点都找到对应球面上的点。
从代码分析开始前,我还是先把之前文章里两张图贴过来,一个是全景vr视频,一个是转出的六面体视频:
六面体六个面的方向是:
右,左,上
下,前,后
代码中最关键的函数就是建立六面体到柱面的映射关系函数: generate_map。里面最关键的是transform_pos,输入六面体输出图像对应的坐标,根据计算结果可以计算出柱面对应坐标。其他h_subdivisons,increase_pixel_weight等等都是用于平滑作用,类似插值的道理,比较简单,就不赘述了。这里主要分析transform_pos这个函数。以常用的LAYOUT_CUBEMAP_32为例。
首先,根据输入的“输出像素坐标”,判断当前像素点处于输出图像的哪个面(face),上下左右前后。我们以“前”面为例。
输入为一个正面的像素点。我们把这个面放到一个三维坐标系中(采用左手坐标系),这个正方形的面距离原点的距离为0.5,x取值范围-0.5~0.5,y取值范围-0.5~0.5。为了让这个面的中心对准z轴,这里需要对输入的x,y坐标做一次转换:
qx = x - 0.5;
qy = y - 0.5;
这个转换对于不同面是不同的,这就是为什么代码中对不同face的vx和vy有不同的数组。如下图:
根据当前点位于当前面的位置,我们可以得到这个点在极坐标中的水平方向和垂直方向角度。
斜边长度:d = sqrtf(tx * tx + ty * ty + tz * tz);
水平方向角度(范围0~1):*outX = -atan2f (-tx / d, tz / d) / (M_PI * 2.0f) + 0.5f;
垂直方向角度(范围0~1):*outY = asinf (-ty / d) / M_PI + 0.5f;
归一化到(0~1)的目的,就是为了方便计算这个像素点在输入视频中的位置:
in_x = (int) (out_x * in_w);
in_y = (int) (out_y * in_h);
至此,一个立方体上的像素到球面上的点对应关系建立完毕。其他处理都比较直观,就不做赘述了。这个原理明白了,其他各种方式的变换也就可以自己实现了。之后有时间再写正四棱锥的映射。
最后留下一些提醒,为了在手机端播放,expand_coef是不可以为1的,必须略大于1,这是因为opengl ES在手机端实现vr一般都是2Dtexture,需要多一些内容做插值,如果仅仅把正方形内容准备齐全,在播放的时候会有缝。