屏幕坐标与OpenGL三维坐标的正确转换

最近接触一个关于OpenGL的项目,使用VC++,由于自己从来没有接触过OpenGL,也只能且学且做了。遇到一个关于坐标转换的问题,在网上搜索了许多资料,搜索引擎的前100个结果页面中的回答和代码都是千篇一律,基本都是错的或者不完整。哎,估计都是抄来抄去,自己想都不想就写篇文章发出去,或者在论坛上随便百度个答案回复一下。

原创,原创,多么渴望多一点原创的内容啊。闲话少说,希望搜索到我这篇文章的人,对你有用。

功能很简单:鼠标点击屏幕中的某点,然后获取该点屏幕坐标对应的OpenGL坐标。鼠标点的坐标很容易获得,直接从鼠标点击消息中获取就可以,但是OpenGL坐标却需要做一定计算转换。需要用到的核心OpenGL函数是gluUnProject()

对于这个问题,在网上流传着如下类似的代码:

GLint viewport[4];   
GLdouble modelview[16];    
GLdouble projection[16];   
GLfloat winX, winY, winZ;   
GLdouble posX, posY, posZ;   
glGetIntegerv(GL_VIEWPORT, viewport);   
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);    
glGetDoublev(GL_PROJECTION_MATRIX, projection);   
winX = (float)x;   
winY = viewport[3] - (float)y;   
glReadPixels((int)winX, (int)winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);   
gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);  

注:(x, y)是屏幕坐标,(winX, winY, winZ)是视景体坐标及深度坐标,(posX, posY, posZ)是OpenGL坐标。

上述代码几乎占了搜索引擎前99%的结果,基本没有人说出其中的问题,估计可能也是出自某个教科书。上面的代码其实只能针对一种特殊情况才有效,即glViewport(0, 0, screenWidth, screenHeight),screenWidth、screenHeight分别是客户区的宽和高,视口左下角坐标恰好是(0, 0),并且未经过任何模型变换

从屏幕坐标向OpenGL坐标转换需要经过两步:第一步是屏幕坐标向视景体坐标转换,第二步是视景体坐标向OpenGL坐标转换。上述代码中winX = (float)x; winY = viewport[3] - (float)y;反映第一步,gluUnProject()是第二步。一般说来,gluUnProject()的转换是不会出问题的。

如何进行正确的转换呢?

首先,在glGetIntegerv()之前必须添上模型变换的代码,即和绘图时使用的模型变换代码一样;其次,必须保证平移、缩放、旋转的顺序和绘图时的一致;另外,屏幕坐标向视景体坐标转换要正确。

还有非常重要的一点,就是在多视口情况下,活动视口必须最后绘制,使它作为当前的视口,这样才能保证glGetIntegerv()等取值函数能够得到正确的值。

好了,现在给出完整的代码,如下:

GLint viewport[4];   
GLdouble modelview[16];   
GLdouble projection[16];   
GLfloat winX, winY, winZ;   
GLdouble posX, posY, posZ;   
  
glPushMatrix();    
//缩放、平移、旋转等变换   
glScalef(......);    
glRotatef(......);   
glTranslatef(......);   
//变换要绘图函数里的顺序一样,否则坐标转换会产生错误   
glGetIntegerv(GL_VIEWPORT, viewport); // 得到的是最后一个设置视口的参数   
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);   
glGetDoublev(GL_PROJECTION_MATRIX, projection);   
glPopMatrix();   
  
winX = x;   
winY = viewport[3] - (float)y;   
glReadPixels((int)winX, (int)winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);   
gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);  

本文原创,希望看到我这篇文章的人,对你有用。

/usr/themes/NexTSun/static/images/yovisun-weixin-share.jpg
赞 (14) 分享
声明:原创文章,欢迎转载,请以 超链接 的形式注明 作者标题原始出处查看许可协议
标题屏幕坐标与OpenGL三维坐标的正确转换 | 作者:YoviSun
地址http://www.yovisun.com/archive/opengl-gluunproject-original.html
相关文章:
已有 42 条评论
  1. hf0291 hf0291

    谢谢 是可以直接转换 然后当作顶点的几何坐标来用 我是昨天才弄出来的 但是在坐标转换前 一定要记得加上模型转换的语句 要不然转换出来的坐标会有问题 我就是犯了这个错误

    1. Good,祝你好运~

    2. 77 77

      能否 叫我 QQ:1294653048,请教一些问题,谢谢了

      1. 有问题请给我留言或发邮件,我很少用QQ。。

  2. hf0291 hf0291

    能否加下我的QQ:417336321 到时候有些问题要和你交流下!

    1. QQ我很少用,直接在这里留言即可,我每天都会看。

  3. hf0291 hf0291

    好的 我想问下 就是OpenGL的纹理贴图的显示只能在OpenGL创建的窗口上还是可以像显示在MFC的控件上(例如 static text)?

  4. 似水流年 似水流年

    你好,我用glortho,发现不行,不知道为什么?

  5. 星语2015 星语2015

    能不能给出具体的代码呢
    我是初学者

  6. 不错!赞一个

  7. hello_world hello_world

    楼主想请教一个问题,三维坐标转换为二维坐标需呀注意哪些问题

    1. 最主要的是把几个坐标转换的矩阵弄清楚

添加新评论
选择表情
手机扫描二维码访问