如何使用摄像机参数在OpenGL中正确设置投影和模型视图矩阵

3

我有一组来自于一款范围扫描仪的点云数据(3D)。示例数据可以在这里找到:http://pastebin.com/RBfQLm56

此外,我还有以下扫描仪参数:

camera matrix
[3871.88184, 0, 950.736938;
  0, 3871.88184, 976.1383059999999;
  0, 0, 1]



distortion coeffs
[0.020208003; -1.41251862; -0.00355229038; -0.00438868301; 6.55825615]



camera to reference point (transform)

[0.0225656671, 0.0194614234, 0.9995559233, 1.2656986283;

  -0.9994773883, -0.0227084301, 0.0230060289, 0.5798922567;

  0.0231460759, -0.99955269, 0.0189388219, -0.2110195758;

  0, 0, 0, 1]

我正在尝试使用OpenGL正确渲染这些点,但渲染效果不正确。请问如何正确设置OpenGL的投影和模型视图矩阵?以下是我当前的操作 -

znear = 0.00001
zfar =  100
K = array([[3871.88184, 0, 950.736938],[0, 3871.88184, 976.1383059999999],[0, 0, 1]])
Rt =array([[0.0225656671, 0.0194614234, 0.9995559233, 1.2656986283],[-0.9994773883, -0.0227084301, 0.0230060289, 0.5798922567],[0.0231460759, -0.99955269, 0.0189388219, -0.2110195758]])
ren.set_projection(K,zfar,znear)
ren.set_projection_from_camera(Rt)

所使用的函数有:
def set_projection(self,K,zfar,znear):
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    f_x = K[0,0]
    f_y = K[1,1]
    c_x = K[0,2]
    c_y = K[1,2]
    fovY = 1/(float(f_x)/height * 2);
    aspectRatio = (float(width)/height) * (float(f_y)/f_x);
    near = zfar
    far = znear
    frustum_height = near * fovY;
    frustum_width = frustum_height * aspectRatio;
    offset_x = (width/2 - c_x)/width * frustum_width * 2;
    offset_y = (height/2 - c_y)/height * frustum_height * 2;
    glFrustum(-frustum_width - offset_x, frustum_width - offset_x, -frustum_height - offset_y, frustum_height - offset_y, near, far);


def set_modelview_from_camera(self,Rt):
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()
    Rx = array([[1,0,0],[0,0,-1],[0,1,0]])
    R = Rt[:,:3]
    U,S,V = linalg.svd(R)
    R = dot(U,V)
    R[0,:]=-R[0,:]
    t=Rt[:,3]
    M=eye(4)
    M[:3,:3]=dot(R,Rx)
    M[:3,3]=t
    M=M.T
    m=M.flatten()
    glLoadMatrixf(m)

然后我只需呈现点(粘贴代码片段):
def renderLIDAR(self,filename):
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    glPushMatrix();

    glEnable(GL_DEPTH_TEST)
    glClear(GL_DEPTH_BUFFER_BIT)
    glPointSize(1.0)
    f = open(filename,'r')
    f.readline() #Contains number of particles
    for line in f:
        line = line.split(' ')
        glBegin(GL_POINTS)
        glColor3f (0.0,1.0,0.0); 
        x = float(line[0])
        y = float(line[1])
        z = float(line[2])
        glVertex3f(x,y,z)
        #print x,y,z
        glEnd()

    glPopMatrix();
1个回答

3

你得到的矩阵,尤其是你问题中的最后一个矩阵,是OpenGL中投影和模型视图组合的结果,也称为模型视图投影,即

MVP = P · M

只要你不打算执行光照计算,你就可以在顶点着色器中只使用这个公式。

#version 330

uniform mat4 MVP;
in vec3 position;

void main()
{
    gl_Position = MVP * vec4(position, 1);
}

顺便提一下,OpenGL和你正在使用的库都是使用列主序(column major order)的,即内存中元素的顺序为

0 4 8 c
1 5 9 d
2 6 a e
3 7 b f

因此,源代码中编写的内容必须被视为“转置”(当然它并不是)。由于您编写的矩阵遵循相同的方案,因此您可以将它按原样放入uniform 中。仅剩下的问题是范围扫描仪使用的NDC空间的边界。但是,可以通过应用额外的矩阵来处理它。OpenGL使用范围[-1,1]^3,因此最糟糕的情况是,如果它在另一个流行的NDC范围[0,1]^3中,您将看到几何形状被挤压到窗口左上角,并且可能会倒转,如果Z轴朝另一个方向,则会倒转。试试吧,我会说它已经匹配了OpenGL。

无论如何,如果您想要与照明一起使用它,您必须将其分解为投影和模型视图部分。易言之,最好的起点是正交化左上角的3×3子矩阵,这产生了模型视图'M'的旋转部分。然后,您必须找到一个矩阵P,当与M左乘时,会产生原始矩阵。这是一个过度决定的线性方程组,因此高斯-约旦方案可以解决它。如果我没有完全错误,那么您已经获得了摄像机矩阵的分解M或P(我会选择M)。

一旦您获得了这个,您可能还想将平移部分(第4列)放入模型视图矩阵中。


非常感谢datenwolf。就矩阵而言,相机矩阵(第一个)是内部相机参数,第三个(相机到参考)是外部相机参数。我假设使用固定的OpenGL管道,设置矩阵的最简单方法是什么?我想一种选择是在着色器中将它们相乘,但如果有照明呢? - mrkulk
如果您想要工作照明,必须按照所述方式分解矩阵。您有关于这些矩阵的文档可以供我阅读吗?从技术上讲,为了使照明正常工作,只需要提取规范化的左上角3×3来变换法线。不幸的是,这仅适用于可编程管线,因为固定功能管线对矩阵做出了某些假设,因此对于固定功能管线,您必须将其分解。 规范化部分是输入模型视图的部分,而补充部分则是投影。 - datenwolf
还缺少正确设置近和远裁剪平面。 - datenwolf
分解后,似乎正常工作了。感谢您的帮助。 - mrkulk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接