最近接触一个关于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);
本文原创,希望看到我这篇文章的人,对你有用。
沙发
板凳
内容分享的好专业的,只是还需要博主多多指教
有需要就给我留言吧
你写的版本跟网上流传的又什么区别吗
从你给的代码看 只是多了个PushMatrix和PopMatrix而已
我想强调的是“必须保证平移、缩放、旋转的顺序和绘图时的一致”,就是PushMatrix后面的内容,另外还要注意多视口的情况
我遇到的问题正好就是大牛所说的问题,只要不旋转就都正常,我的代码如下:
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是放在左键消息函数里的,能麻烦你帮指导一下有什么问题吗,或者留个联系方式,非常感谢
代码没错啊,再前后加上glPushMatrix()和glPopMatrix(),然后你的程序是多视口的不,如果是都视口的话,取glUnProject的时候,要取得的视口要最后画
你的问题解决了没有?请问
相当专业啊,淫才啊,
你打错字了!
专业的拿出来分享 就是好啊
在CS中 OpenGL 模式更容易盲狙!
好内容啊,谢分享
好的东西就是要拿出来多分享啊
此代码似乎对有gluLookAt的情况也不行。
学习了
好东西,学习了
宇宙人表示鸭力山大。。。。
为何?
支持一下。
对这个不懂啊
学习,支持,感谢博主分享!
祝福朋友们身体健康,福运连连,欢笑天天,平安年年!
独立博客很不容易啊,坚持下去,加油。
有空来我的小站回访一下哈。
欣赏了!努力加油!
祝福朋友们身体健康,福运连连,欢笑天天,平安年年!
谢谢博主的分享,确实学习到不少。
我想问个问题,就是屏幕坐标转换成了OpenGL三维坐标后,我纹理坐标的顶点坐标能不能直接用这个转换出来的坐标glVertex3f(posX, posY, posZ)?? 如果不能这样用,那我纹理坐标的顶点坐标是怎么转换来的,求指导?
应该可以直接转换