屏幕坐标与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
赞 (20) 分享
声明:原创文章,欢迎转载,请以 超链接 的形式注明 作者标题原始出处查看许可协议
标题屏幕坐标与OpenGL三维坐标的正确转换 | 作者:YoviSun
地址http://www.yovisun.com/archive/opengl-gluunproject-original.html
相关文章:
已有 42 条评论
  1. 沙发

  2. 内容分享的好专业的,只是还需要博主多多指教

    1. 有需要就给我留言吧

      1. beigua321 beigua321

        你写的版本跟网上流传的又什么区别吗
        从你给的代码看 只是多了个PushMatrix和PopMatrix而已

        1. 我想强调的是“必须保证平移、缩放、旋转的顺序和绘图时的一致”,就是PushMatrix后面的内容,另外还要注意多视口的情况

      2. beigua321 beigua321

        我遇到的问题正好就是大牛所说的问题,只要不旋转就都正常,我的代码如下:
        glLoadIdentity();
        glTranslatef(m_xPos, m_yPos, -5.0f);
        glScalef(m_xSize,m_ySize,m_zSize);
        glRotatef(m_xAngle, 1.0f,0.0f,0.0f);
        glRotatef(m_yAngle, 0.0f,1.0f,0.0f);
        glTranslatef(-(ptCenter.x - ptMin.x)/NormRef, -(ptCenter.y - ptMin.y)/NormRef, -(ptCenter.z - ptMin.z)/NormRef);

        glGetIntegerv(GL_VIEWPORT, &(ViewPort[0]));
        glGetDoublev(GL_MODELVIEW_MATRIX, &(ModelView[0]));
        glGetDoublev(GL_PROJECTION_MATRIX, &(Projection[0]));
        上面的代码是放在RenderScene里的,glUnProject是放在左键消息函数里的,能麻烦你帮指导一下有什么问题吗,或者留个联系方式,非常感谢

        1. 代码没错啊,再前后加上glPushMatrix()和glPopMatrix(),然后你的程序是多视口的不,如果是都视口的话,取glUnProject的时候,要取得的视口要最后画

        2. guagua guagua

          你的问题解决了没有?请问

  3. 相当专业啊,淫才啊,

    1. 你打错字了!

  4. 专业的拿出来分享 就是好啊

  5. 在CS中 OpenGL 模式更容易盲狙!

  6. 好内容啊,谢分享

  7. 好的东西就是要拿出来多分享啊

  8. 此代码似乎对有gluLookAt的情况也不行。

  9. 学习了 [龇牙]

  10. 好东西,学习了

  11. 宇宙人表示鸭力山大。。。。

  12. 匿名 匿名

    支持一下。

  13. 对这个不懂啊

  14. 黔灵人民医院 黔灵人民医院

    学习,支持,感谢博主分享!

  15. 匿名 匿名

    祝福朋友们身体健康,福运连连,欢笑天天,平安年年!

  16. 独立博客很不容易啊,坚持下去,加油。
    有空来我的小站回访一下哈。

  17. 欣赏了!努力加油!

  18. 祝福朋友们身体健康,福运连连,欢笑天天,平安年年!

  19. 匿名 匿名

    谢谢博主的分享,确实学习到不少。

  20. hf0291 hf0291

    我想问个问题,就是屏幕坐标转换成了OpenGL三维坐标后,我纹理坐标的顶点坐标能不能直接用这个转换出来的坐标glVertex3f(posX, posY, posZ)?? 如果不能这样用,那我纹理坐标的顶点坐标是怎么转换来的,求指导?

    1. 应该可以直接转换

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