opengl中的世界坐标系,我们都清楚是归一化到(-1,+1)之间的,而我们的窗口坐标又是以坐上角为原点的,有时为了实现页面与opengl中绘制对象的交互,两者坐标间的转化其实是很有必要的。此外,除了2D坐标外,3D坐标也可进行转换,此文只介绍了2D的使用,要是有机会之后更新3D的使用。
核心代码部分
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
glGetIntegerv(GL_VIEWPORT, viewport); // 得到的是最后一个设置视口的参数
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
在分析这段代码之前,我们要简单了解下,opengl坐标变换的本身原理。这里我们参考下这篇文章
如果我们需要绘制一个3D物体,输入顶点坐标进行一系列渲染之后,我们最终得到的其实是2D图像,不过我们看起来像3D,这期间进行的一系列处理,就是上面图片所描述的,本质其实是在做矩阵运算。
1、从不同的位置去观察它。(视图变换)
2、移动或者旋转它,当然了,如果它只是计算机里面的物体,我们还可以放大或缩小它。(模型变换)
3、如果把物体画下来,我们可以选择:是否需要一种“近大远小”的透视效果。另外,我们可能只希望看到物体的一部分,而不是全部(剪裁)。(投影变换)
4、我们可能希望把整个看到的图形画下来,但它只占据纸张的一部分,而不是全部。(视口变换)
既然我们可以从一个3D坐标转为2D坐标,那么上述过程的逆,不就得到了图形的3D坐标。
那我们接着回头看我们写到的代码,
glGetIntegerv(GL_VIEWPORT, viewport); // 得到的是最后一个设置视口的参数
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
这三句就是获取了,我们接下来要用到函数的矩阵,分别获取视口、模型、投影矩阵。
将得到的参数带入gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ)函数,便可以将窗口的坐标转换为世界坐标。
GLint gluUnProject(
GLdouble winX, GLdouble winY, GLdouble winZ, const GLdouble * model, const GLdouble * proj, const GLint * view, GLdouble* objX, GLdouble* objY, GLdouble* objZ )
;
gluUnProject
maps the specified window coordinates into object coordinates usingmodel
,proj
, andview
. The result is stored inobjX
,objY
, andobjZ
. A return value ofGLU_TRUE
indicates success; a return value ofGLU_FALSE
indicates failure.gluUnProject使用模型,项目和视图将指定的窗口坐标映射为对象坐标。 结果存储在objX,objY和objZ中。 返回值GLU_TRUE表示成功; 返回值GLU_FALSE表示失败。
当然,gluProject()函数则是将世界坐标转为窗口坐标。
GLint gluProject(
GLdouble objX, GLdouble objY, GLdouble objZ, const GLdouble * model, const GLdouble * proj, const GLint * view, GLdouble* winX, GLdouble* winY, GLdouble* winZ )
;
gluProject
transforms the specified object coordinates into window coordinates usingmodel
,proj
, andview
. The result is stored inwinX
,winY
, andwinZ
. A return value ofGLU_TRUE
indicates success, a return value ofGLU_FALSE
indicates failure.gluProject使用模型,项目和视图将指定的对象坐标转换为窗口坐标。 结果存储在winX,winY和winZ中。 返回值GLU_TRUE表示成功,返回值GLU_FALSE表示失败。
我利用这个函数实现了一个小例子,我们看一下结果。鼠标在上面滑动,可以显示其世界坐标(对象坐标)。这是在一个绘制三角形例子上改的。我们可以看到这个坐标计算的值还是蛮准确的。如果要看具体例子的实现,看这里。
如果我要获取3D坐标的话,还需要用到这个函数glReadPixels。我们绘图需要用到的数据存储在GPU中,我们要获得Z坐标,就是要从该x,y坐标处获得此处的深度值。
glReadPixels((int)winX, (int)winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
具体的介绍及使用看这里。
配合我上文介绍的函数就可以实现3D坐标的获取了。