Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2036528
  • 博文数量: 610
  • 博客积分: 11499
  • 博客等级: 上将
  • 技术积分: 5511
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-12 19:27
文章分类

全部博文(610)

文章存档

2016年(5)

2015年(18)

2014年(12)

2013年(16)

2012年(297)

2011年(45)

2010年(37)

2009年(79)

2008年(101)

分类:

2012-07-12 09:28:08

 很多人用OpenGL绘图会遇到一个问题即屏幕坐标向OpenGL坐标转换,在网上流传着如下类似的代码:

 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, posZOpenGL坐标。

    上述代码并不保险,只针对一种特殊情况才好使,即glViewport(0, 0, screenWidth, screenHeight)screenWidthscreenHeight分别是客户区的宽和高,视口左下角坐标恰好是(00),并且未经过任何模型变换。

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

    如何进行正确的转换呢?首先,在glGetIntegerv之前添上模型变换的代码,和绘图时使用的模型变换代码一样,另外必须保证平移,缩放,旋转的顺序和绘图时的一样。其次,屏幕坐标向视景体坐标转换有两种方式(注意!在多视口情况下,活动视应当最后绘制,它将作为当前的视口,保证glGetIntegerv等取值函数能得到正确的值)。①winx = x – viewport[0]; winy = screenHeight – viewport[1] - y; viewport[0] = viewport[1] = 0;winx = x; winy = screenHeight – y;第一种比较直观,前两句是将屏幕坐标转换为视景体内的坐标,后两句将视景体的左下角点坐标改为(00),因为在设置裁剪视口时,使用glViewport设置视口的左下角点坐标不一定是(00),而在视景体内的点其视景体坐标与左下角点是相对的,即把视景体坐标看作是坐标系原点。第二种方式比较简略,但是同样的道理,只不过是glUnproject函数对winxwiny又做了一次转换。

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

 GLint    viewport[4];
 GLdouble modelview[16];
 GLdouble projection[16];
 GLfloat  winX, winY, winZ;
 GLdouble posX, posY, posZ;

 glPushMatrix();
 
 //
变换要绘图函数里的顺序一样,否则坐标转换会产生错误
 glScalef(m_srtMatrix[0], m_srtMatrix[1], m_srtMatrix[2]); //
缩放、平移、旋转变换
 glRotatef(m_srtMatrix[3], 1.0f, 0.0f, 0.0f);
 glRotatef(m_srtMatrix[4], 0.0f, 1.0f, 0.0f);
 glRotatef(m_srtMatrix[5], 0.0f, 0.0f, 1.0f);
 glTranslatef(m_srtMatrix[6], m_srtMatrix[7], m_srtMatrix[8]);


 glGetIntegerv(GL_VIEWPORT, viewport); //
得到的是最后一个设置视口的参数
 glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
 glGetDoublev(GL_PROJECTION_MATRIX, projection);

 glPopMatrix();

 winX = x;
 winY = screenHeight - y;
 glReadPixels((int)winX, (int)winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
 gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);






view plaincopy to clipboardprint?

  1. /*  

  2. Transform a point(column vector) by a 4x4 matrix. Then, out = m * in  

  3. Input: m ----- the 4x4 matrix, in ---- the 4x1 vector  

  4. Output: out ---- the resulting 4x1 vector  

  5. */  

  6. static void transform_point(GLdouble out[4], const GLdouble m[16], const GLdouble in[4])   

  7. {   

  8. #define M(row,col) m[col*4+row]   

  9.     out[0] =    

  10.         M(0, 0) * in[0] + M(0, 1) * in[1] + M(0, 2) * in[2] + M(0, 3) * in[3];   

  11.     out[1] =   

  12.         M(1, 0) * in[0] + M(1, 1) * in[1] + M(1, 2) * in[2] + M(1, 3) * in[3];   

  13.     out[2] =   

  14.         M(2, 0) * in[0] + M(2, 1) * in[1] + M(2, 2) * in[2] + M(2, 3) * in[3];   

  15.     out[3] =   

  16.         M(3, 0) * in[0] + M(3, 1) * in[1] + M(3, 2) * in[2] + M(3, 3) * in[3];   

  17. #undef M   

  18. }   

  19. // gluProject source code (说明见OpenGL API文档)   

  20. GLint gluProject(GLdouble objx, GLdouble objy, GLdouble objz, const GLdouble  modelMatrix[16], const GLdouble projMatrix[16], const GLint viewport[4], GLdouble *winx, GLdouble *winy, GLdouble *winz)   

  21. {   

  22.     // matrice transformation   

  23.     GLdouble in[4], out[4];   

  24.     //initialize matrice and column vector as a transformer   

  25.     in[0] = objx;   

  26.     in[1] = objy;   

  27.     in[2] = objz;   

  28.     in[3] = 1.0;   

  29.     transform_point(out, modelMatrix, in);  //乘以模型视图矩阵   

  30.     transform_point(in, projMatrix, out);   //乘以投影矩阵   

  31.     //齐次向量的第四项不能为0   

  32.     if(in[3] == 0.0)   

  33.         return GL_FALSE;   

  34.     //向量齐次化标准化   

  35.     in[0] /= in[3];   

  36.     in[1] /= in[3];   

  37.     in[2] /= in[3];   

  38.     //视口向量的作用   

  39.     *winx = viewport[0] + (1 + in[0]) * viewport[2] / 2;   

  40.     *winy = viewport[1] + (1 + in[1]) * viewport[3] / 2;   

  41.     *winz = (1 + in[2]) / 2;   

  42.     return GL_TRUE;   

  43. }  

阅读(521) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~