如何使用相机参数正确设置OpenGL中的投影和模型视图矩阵
我有一组从范围扫描仪获取的三维点。你可以在这里找到示例数据: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 个回答
你提到的那些矩阵,特别是你问题中的最后一个,在OpenGL中被称为投影和模型视图的组合,也叫做模型视图投影,简单来说就是:
MVP = P · M
只要你不需要进行光照计算,你可以直接在顶点着色器中使用这个公式,也就是:
#version 330
uniform mat4 MVP;
in vec3 position;
void main()
{
gl_Position = MVP * vec4(position, 1);
}
顺便说一下,OpenGL和你可能使用的库都是采用列优先的顺序,也就是说,内存中元素的排列方式是:
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列)也放入模型视图矩阵中。