OpenGL中的相机镜头失真

时间:2017-06-11 23:35:52

标签: opengl shader lens distortion fisheye

我正在尝试为我的SLAM项目模拟镜头失真效果。 扫描的彩色3D点云已经在OpenGL中给出并加载。 我要做的是渲染给定姿势的2D场景,并在鱼眼摄像机的真实图像和渲染图像之间进行一些视觉测距。 由于相机具有严重的镜头失真,因此在渲染阶段也应该考虑它。

问题在于我不知道在哪里放置镜头失真。着色器?

我发现some open codes将失真放在几何着色器中。但是这个我认为失真模型与计算机视觉社区中的镜头失真模型不同。在CV社区中,镜头失真通常发生在投影平面上。

This one与我的工作非常相似,但他们没有使用失真模型。

任何人都有个好主意?

我刚发现another implementation。他们的代码在片段着色器和几何着色器中实现了失真。但片段着色器版本可以应用于我的情况。因此,我想以下内容将起作用:

# vertex shader
p'=T.model x T.view x p
p_f = FisheyeProjection(p') // custom fish eye projection

2 个答案:

答案 0 :(得分:3)

镜头失真通常会将直线变成曲线。使用OpenGL栅格化线条和三角形时,基元和#无论你如何变换顶点,边都会保持笔直。

如果你的模型具有足够精细的细分,那么将失真结合到顶点变换中是可行的。如果您只渲染点数,它也可以使用。

然而,当您的目标是普遍适用性时,您必须以某种方式处理直边原语。一种方法是使用几何着色器进一步细分传入的模型;或者你可以使用tesselation着色器。

另一种方法是渲染成立方体贴图,然后使用着色器创建相当于它的镜头。我实际上建议用它来生成鱼眼图像。

失真本身通常用3到5的多项式表示,映射从光学中心轴到失真角距离的无失真角距离。

答案 1 :(得分:1)

受VR社区的启发,我通过顶点位移实现了变形。 对于高分辨率,这在计算上更有效,但需要具有良好顶点密度的网格。您可能要在扭曲图像之前应用细分。

以下是实现OpenCV有理失真模型的代码(有关公式,请参见https://docs.opencv.org/4.0.1/d9/d0c/group__calib3d.html

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal_in;
layout (location = 2) in vec2 texture_coordinate_in;
uniform mat4 model_matrix;
uniform mat4 view_matrix;
uniform float dist_coeffs[8];
uniform mat4 projection_matrix;
uniform vec3 light_position;
out vec2 texture_coordinate;
out vec3 normal;
out vec3 light_direction;

// distort the real world vertices using the rational model
vec4 distort(vec4 view_pos)
{
  // normalize
  float z = view_pos[2];
  float z_inv = 1 / z;
  float x1 = view_pos[0] * z_inv;
  float y1 = view_pos[1] * z_inv;
  // precalculations
  float x1_2 = x1*x1;
  float y1_2 = y1*y1;
  float x1_y1 = x1*y1;
  float r2 = x1_2 + y1_2;
  float r4 = r2*r2;
  float r6 = r4*r2;
  // rational distortion factor
  float r_dist = (1 + dist_coeffs[0]*r2 +dist_coeffs[1]*r4 + dist_coeffs[4]*r6) 
    / (1 + dist_coeffs[5]*r2 + dist_coeffs[6]*r4 + dist_coeffs[7]*r6);
  // full (rational + tangential) distortion
  float x2 = x1*r_dist + 2*dist_coeffs[2]*x1_y1 + dist_coeffs[3]*(r2 + 2*x1_2);
  float y2 = y1*r_dist + 2*dist_coeffs[3]*x1_y1 + dist_coeffs[2]*(r2 + 2*y1_2);
  // denormalize for projection (which is a linear operation)
  return vec4(x2*z, y2*z, z, view_pos[3]);
}

void main()
{
  vec4 local_pos = vec4(position, 1.0);
  vec4 world_pos  =  model_matrix * local_pos;
  vec4 view_pos = view_matrix * world_pos;
  vec4 dist_pos = distort(view_pos);
  gl_Position = projection_matrix * dist_pos;
  // lighting on world coordinates not distorted ones
  normal = mat3(transpose(inverse(model_matrix))) * normal_in;
  light_direction = normalize(light_position - world_pos.xyz);
  texture_coordinate = texture_coordinate_in;
}

请注意,失真是在z归一化的坐标中计算的,但在失真的最后一行中已归一化为视图坐标。这样就可以使用类似于本文中的投影矩阵:http://ksimek.github.io/2013/06/03/calibrated_cameras_in_opengl/