OpenGL三维坐标与二维坐标:gluProject 和 gluUnproject 的详解

简介

三维空间中,经常需要将3D空间中的点转换到2D(屏幕坐标),或者将2D点转换到3D空间中。当你使用OpenGL的时候,简单使用gluProject()和gluUnproject()函数就可以实现这个功能了。但这两个神奇的函数是怎样实现的,一直困扰着我,经过一番仔细研究,将自己的思路写在这里:


gluPorject()

先通过看代码,来一步一步分析它的数学原理吧!(其实代码是次要的,数学原理在这里才是关键所在!)这里的代码据说是赖在mesa OpenGL中的!

PS:这里为了更好理解,我修改了一下里面的代码,但没有兼顾效率的!

  1.   
  1.   
  2. static void transform_point(GLdouble out[4] , const GLdouble m[16], const GLdouble in[4])  
  3. {  
  4. #define M(row,col) m[col*4+row]  
  5.     out[0] = M(0, 0) * in[0] + M(0, 1) * in[1] + M(0, 2) * in[2] + M(0, 3) * in[3];  
  6.     out[1] = M(1, 0) * in[0] + M(1, 1) * in[1] + M(1, 2) * in[2] + M(1, 3) * in[3];  
  7.     out[2] = M(2, 0) * in[0] + M(2, 1) * in[1] + M(2, 2) * in[2] + M(2, 3) * in[3];  
  8.     out[3] = M(3, 0) * in[0] + M(3, 1) * in[1] + M(3, 2) * in[2] + M(3, 3) * in[3];  
  9. #undef M  
  10. }  
  11.   
  12. GLint gluProject(GLdouble objx, GLdouble objy, GLdouble objz  
  13.                 , const GLdouble model[16], const GLdouble proj[16], const GLint viewport[4]  
  14.                 , GLdouble * winx, GLdouble * winy, GLdouble * winz)  
  15. {  
  16.       
  17.     GLdouble objCoor[4];  
  18.     GLdouble objProj[4], objModel[4];  
  19.   
  20.       
  21.     // 4x4 matrix must be multi to a 4 dimension vector( it a 1 x 4 matrix)  
  22.     // so we need to put the original vertex to a 4D vector  
  23.     objCoor[0] = objx;  
  24.     objCoor[1] = objy;  
  25.     objCoor[2] = objz;  
  26.     objCoor[3] = 1.0;  
  27.   
  28.     // 由于原来的向量位于标准基向量(1, 0, 0), (0, 1, 0), (0, 0, 1)中,所以需要先转换到当前的模型矩阵中  
  29.     transform_point(objModel, model, objCoor);  
  30.   
  31.     // 然后将模型矩阵中的顶点转换到投影矩阵所在坐标系的矩阵中  
  32.     transform_point(objProj, proj, objModel);  
  33.   
  34.     // scale matrix  
  35.       
  36.   
  37.       
  38.     if (objProj[3] == 0.0)  
  39.         return GL_FALSE;  
  40.   
  41.     objProj[0] /= objProj[3];  
  42.     objProj[1] /= objProj[3];  
  43.     objProj[2] /= objProj[3];  
  44.   
  45.       
  46.     // 由于投影矩阵投影在[-1, 1]之间,所以需要将转换后的投影坐标放置到[0, 1]之间  
  47.     // 最后再在一个offset 矩形中转换为屏幕坐标就可以了(viewport[4]可以简单的认为一个offset矩形)  
  48.   
  49. #define SCALE_FROM_0_TO_1(_pt)  (((_pt) + 1)/2)  
  50.     objProj[0] = SCALE_FROM_0_TO_1(objProj[0]);  
  51.     objProj[1] = SCALE_FROM_0_TO_1(objProj[1]);  
  52.     objProj[2] = SCALE_FROM_0_TO_1(objProj[2]);  
  53. #undef SCALE_FROM_0_TO_1  
  54.   
  55.     *winx = viewport[0] + objProj[0] * viewport[2];  
  56.     *winy = viewport[1] + objProj[1] * viewport[3];  
  57.   
  58.       
  59.     *winz = objProj[2];  
  60.     return GL_TRUE;  
  61. }  
  1.   


基本的思路就是:

1、将输入的顶点,通过模型视图矩阵,变换到模型视图矩阵的坐标系中;

2、将模型视图矩阵中的顶点,再变换到投影矩阵中;

3、将顶点缩放到[0, 1]的映射区间中;

4、通过视口的位置和大小,计算出当前3D顶点中的屏幕坐标(2D坐标)


gluUnproject

其实gluUnproject和gluProject是非常类似的,代码我暂时没有去找,但我认为应该是这样的(其实就是gluPorject反过来的过程,只是有一些数学运算要注意一下):
1、首先,需要将输入的顶点,通过视口变换到[0, 1]之间;
2、然后将顶点缩放到[-1, 1]之间,就是上面代码中的scaleMat矩阵的逆矩阵
3、然后乘上投影矩阵的逆矩阵;
4、最后就是乘上模型视图矩阵的逆矩阵;
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页