用于针孔相机模型的OpenGL顶点着色器

时间:2018-10-22 11:21:00

标签: opengl geometry glsl

我正在尝试实现一个简单的OpenGL渲染器,该渲染器可模拟针孔相机模型(例如here的定义)。当前,我使用顶点着色器将3D顶点映射到剪辑空间,其中着色器中的K包含[焦距x,焦距y,主点x,主点y],而zrange是顶点的深度范围。 / p>

#version 330 core

layout (location = 0) in vec3 vin;
layout (location = 1) in vec3 cin;
layout (location = 2) in vec3 nin;

out vec3 shader_pos;
out vec3 shader_color;
out vec3 shader_normal;

uniform vec4 K;
uniform vec2 zrange;
uniform vec2 imsize;

void main() {
  vec3 uvd;
  uvd.x = (K[0] * vin.x + K[2] * vin.z) / vin.z;
  uvd.y = (K[1] * vin.y + K[3] * vin.z) / vin.z;
  uvd.x = 2 * uvd.x / (imsize[0]) - 1;
  uvd.y = 2 * uvd.y / (imsize[1]) - 1;
  uvd.z = 2 * (vin.z - zrange[0]) / (zrange[1] - zrange[0]) - 1;
  shader_pos = uvd;
  shader_color = cin;
  shader_normal = nin;
  gl_Position = vec4(uvd.xyz, 1.0);
}

我使用简单的光线跟踪器验证了渲染,但是我的OpenGL实现似乎存在偏移。深度值是不同的,但不是由仿射偏移引起的,因为它是由错误的重新映射引起的(请参见四面体上的倾斜表面,忽略边缘上的错误)。 Error

1 个答案:

答案 0 :(得分:3)

  

我正在尝试实现一个模拟针孔相机模型的简单OpenGL渲染器。

标准透视投影矩阵已经实现了针孔相机模型。您在这里所做的只是每个顶点有更多计算,所有这些计算都可以在CPU上预先计算并放在一个矩阵中。

唯一的区别是z范围。但是“针孔相机”没有z范围,所有点都投影到图像平面。因此,您想要的是xy的针孔相机模型,以及z linear 映射。

但是,您的实现是错误的。 GPU将在窗口空间中线性插入z 。也就是说,它将计算每个片段相对于窗口三角形的 2D投影的重心坐标。但是,当使用透视投影时,并且当三角形不完全平行于图像平面时,这些重心坐标将不是相对于实际3D图元之前相应3D点所具有的重心坐标。投影。

这里的技巧是,由于在屏幕空间中,我们通常以x/zy/z作为顶点坐标,并且当我们在它们之间进行线性插值时,我们还必须插值1/z为深度。但是,实际上,我们不除以z,而是除以w(并让投影矩阵为我们设置w_clip = [+/-]z_eye)。除以w_clip之后,我们得到了z值的双曲线映射,但具有可以在窗口空间中线性内插的好特性。

这意味着通过使用线性z映射,您现在必须沿z维度 bend 弯曲基元,以获得正确的结果。查看以下自上而下的情况。从直线上方看,“线”代表平面三角形:

visualization of hyperbolic z versus linear z

在眼空间中,所有视线都将从原点穿过每个像素(例如,我们可以想象近平面上的2D像素栅格)。在NDC中,我们已将其转换为正交投影。仍然可以在近平面上想象像素,但是现在所有视线都是 parallel

在标准双曲映射中,平截头圆锥体中的点向末端压缩得很多。但是,火车铃仍然是平坦的。

如果改用线性映射,则三角形将不再是平坦的。例如看两个小火车之间的交点。为了获得正确的结果,它必须具有与双曲线情况相同的x(和y)坐标。

但是,您仅根据线性z值变换顶点,GPU仍将对结果进行线性插值,因此,在您的情况下,您将获得变换后的点之间的直线连接,两个三角形之间的交点会移动,除了实际的顶点本身,深度值都是错误的。

如果您要使用线性深度缓冲区,则必须校正片段着色器中每个片段的深度,以自行实现所需的非线性插值。这样做会破坏许多GPU进行的聪明的深度测试优化,尤其是早期的Z和分层Z,因此,尽管有可能,您会失去一些性能。

更好的解决方案是:只需使用标准的双曲深度值即可。读回深度值后,只需线性化它们即可。另外,不要在顶点着色器中进行z除法。这样不仅破坏了z,而且破坏了变量的经透视校正的插值,因此阴影也将是错误的。让GPU进行除法,只需将正确的值改组为gl_Position.w。 GPU不仅会在内部进行除法,而且透视校正后的插值还取决于w