VR镜头抗畸变基本算法探讨
VR终端的体验有多种形式,除了现在比较成熟的VR一体机、VR眼镜以外,还有基本的Cardboard体验。
在Cardboard的体验形式下,如需要取得比较好的体验效果,如何比较好的处理透镜产生的图形畸变关系到最终的用户体验。
什么是畸变
在几何光学中,畸变是与直线投影的偏差,它是光学像差(即光经过透镜后产生的图像失真或模糊)的一种形式。而在VR眼镜中,我们所涉及的畸变都是径向畸变,即产生的图像变形都是径向对称的,这也是由摄像镜头的径向对称性所决定的。径向畸变通常有三种形式:
1、 桶形畸变
即图像放大率随着距光轴的距离而减小。在这样的畸变中,直线在中心向外凸出,像桶一样。
2、 枕形畸变
图像放大率随着距光轴的距离而增加。在这样的畸变中,直线穿过图像向内弯曲,正方形的四个角外发拉长,像枕头一样。
3、 胡子畸变
胡子畸变是上面两种类型的混合物,在这样的畸变中,它开始于靠近图像中心的桶形失真,并逐渐变成朝向图像周边的枕形失真,使得框架上半部分的水平线看起来像车把胡须。
在数学上,除了胡子畸变以外,桶形和枕形畸变是二次的,这意味着它们随着距中心的距离的平方而增加。在VR中,我们只需要关注桶形和枕形畸变。
VR眼镜中的畸变是如何产生的
在VR眼镜中,为了获取得沉浸式的体验,需要给用户提供比较大的视场角(FOV),作为廉价的VR方案,肯定不可能在用户面前放上一个超大的曲面显示屏。替代方案就是在用户眼前小的显示屏上放置一个放大透镜。
效果如下图所示:
这样有个靠近眼睛的透镜,可以让用户获得比较大的视场角,但这样做的代价就是光线经过透镜后会产生径向畸变,视角场越大,畸变就会越严重。
如下图所示,正常的图像经过放大透镜后会产生枕形畸变。
在正常情况下,透镜的曲率越大,产生的畸变就会越严重。
VR眼镜中的畸变纠正办法
那么如何让人眼通过VR眼镜的透镜看到正常的图像,其解决办法就是提前对显示屏上输出的图片进行畸变纠正,即反向畸变。
将输出到显示屏上的图像转换成对应的桶状畸变,这样经过透镜到达人眼之后,就会反向形成正常的图像了。
相关的畸变公式
相关的畸变公式有两个,其一是zhang(1999) 证明了具有两个系数的多项式径向畸变模型,模拟为如下公式:
但在实际使用的过程中,如果要进行互相换算,需要解4次方程,代价有些高,所以一般情况下,我们都会使用另一个简单一点的,即费兹本(2001)近似径向畸变模型。公式如下:
其中 ru 和 rd 分别是非畸变和畸变后的图像中心距离。α 是每一个镜片固有的常量系数。
具体算法的实现
由于费兹本公式中的r是围绕光轴所在图像中心点进行的计算,要使用这个公式,我们首先需要先进行坐标归一化,将图像中的坐标系向这个公式中的坐标系进行映射。
如下图所示,需要将左图的坐标映射为右图的座标。
(xn,yn) 代表归一化后的坐标系,w 和 h 代表宽和高。
在使用公式完成反向畸变计算后,再进行坐标反向映射。
搞清楚了上面这些东西,在Unity Shader中,使用一个简单的片元着色器就可以方便的搞定了,参考代码如下:
// 镜片的畸变常量系数
float alpha = 0.1f;
// 将坐标向费兹本公式坐标转换
float x = (2.0 * i.uv.x - 1.0) / 1.0f;
float y = (2.0 * i.uv.y - 1.0) / 1.0f;
// 将UV坐标转换为反向畸变后的坐标
float r = x * x + y * y;
float x3 = x / (1.0 - alpha * r);
float y3 = y / (1.0 - alpha * r);
float x2 = x / (1.0 - alpha * (x3 * x3 + y3 * y3));
float y2 = y / (1.0 - alpha * (x3 * x3 + y3 * y3));
// 将坐标进行反向转换
float i2 = (x2 + 1.0) * 1.0 / 2.0;
float j2 = (y2 + 1.0) * 1.0 / 2.0;
在上面的算法中可以暴露出镜片的畸变系统alpha。
再结合双目渲染的其他物理参数:比如瞳距、屏幕宽高、屏幕与镜片深度等,再进行相关的调整适配,就可以搞定大多数的双目渲染场景了。
- 点赞
- 收藏
- 关注作者
评论(0)