gluProject 和 gluUnproject 的详解

gluProject 和 gluUnproject 的详解


简介

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

gluPorject()

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

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

[cpp]  view plain copy
  1.   
[cpp]  view plain copy
  1. /* 
  2. * Transform a point (column vector) by a 4x4 matrix. I.e. out = m * in 
  3. * Input: m - the 4x4 matrix 
  4. * in - the 4x1 vector 
  5. * Output: out - the resulting 4x1 vector. 
  6. */  
  7. static void transform_point(GLdouble out[4] , const GLdouble m[16], const GLdouble in[4])  
  8. {  
  9. #define M(row,col) m[col*4+row]  
  10.     out[0] = M(0, 0) * in[0] + M(0, 1) * in[1] + M(0, 2) * in[2] + M(0, 3) * in[3];  
  11.     out[1] = M(1, 0) * in[0] + M(1, 1) * in[1] + M(1, 2) * in[2] + M(1, 3) * in[3];  
  12.     out[2] = M(2, 0) * in[0] + M(2, 1) * in[1] + M(2, 2) * in[2] + M(2, 3) * in[3];  
  13.     out[3] = M(3, 0) * in[0] + M(3, 1) * in[1] + M(3, 2) * in[2] + M(3, 3) * in[3];  
  14. #undef M  
  15. }  
  16.   
  17. GLint gluProject(GLdouble objx, GLdouble objy, GLdouble objz  
  18.                 , const GLdouble model[16], const GLdouble proj[16], const GLint viewport[4]  
  19.                 , GLdouble * winx, GLdouble * winy, GLdouble * winz)  
  20. {  
  21.     /* transformation matrix */  
  22.     GLdouble objCoor[4];  
  23.     GLdouble objProj[4], objModel[4];  
  24.   
  25.     /* initilise matrix and vector transform */  
  26.     // 4x4 matrix must be multi to a 4 dimension vector( it a 1 x 4 matrix)  
  27.     // so we need to put the original vertex to a 4D vector  
  28.     objCoor[0] = objx;  
  29.     objCoor[1] = objy;  
  30.     objCoor[2] = objz;  
  31.     objCoor[3] = 1.0;  
  32.   
  33.     // 由于原来的向量位于标准基向量(1, 0, 0), (0, 1, 0), (0, 0, 1)中,所以需要先转换到当前的模型矩阵中  
  34.     transform_point(objModel, model, objCoor);  
  35.   
  36.     // 然后将模型矩阵中的顶点转换到投影矩阵所在坐标系的矩阵中  
  37.     transform_point(objProj, proj, objModel);  
  38.   
  39.     // scale matrix  
  40.     /* 
  41.     GLdouble scaleMat[4][4] =  
  42.     { 
  43.         {0.5, 0, 0, objPr0j[3]}, 
  44.         {0, 0.5, 0, objProj[3]}, 
  45.         {0, 0, 0.5, objProj[3]}, 
  46.         {1, 1, 1,   1} 
  47.     }; 
  48.  
  49.     GLdouble objProjTemp[4]; 
  50.     memcpy(objProjTemp, objProj, sizeof(objProjTemp); 
  51.     transfrom_point(objProj, scaleMat, objProjTemp); 
  52.     */  
  53.   
  54.     /* or the result of normalized between -1 and 1 */  
  55.     if (objProj[3] == 0.0)  
  56.         return GL_FALSE;  
  57.   
  58.     objProj[0] /= objProj[3];  
  59.     objProj[1] /= objProj[3];  
  60.     objProj[2] /= objProj[3];  
  61.   
  62.     /* in screen coordinates */  
  63.     // 由于投影矩阵投影在[-1, 1]之间,所以需要将转换后的投影坐标放置到[0, 1]之间  
  64.     // 最后再在一个offset 矩形中转换为屏幕坐标就可以了(viewport[4]可以简单的认为一个offset矩形)  
  65.   
  66. #define SCALE_FROM_0_TO_1(_pt)  (((_pt) + 1)/2)  
  67.     objProj[0] = SCALE_FROM_0_TO_1(objProj[0]);  
  68.     objProj[1] = SCALE_FROM_0_TO_1(objProj[1]);  
  69.     objProj[2] = SCALE_FROM_0_TO_1(objProj[2]);  
  70. #undef SCALE_FROM_0_TO_1  
  71.   
  72.     *winx = viewport[0] + objProj[0] * viewport[2];  
  73.     *winy = viewport[1] + objProj[1] * viewport[3];  
  74.   
  75.     /* between 0 and 1 */  
  76.     *winz = objProj[2];  
  77.     return GL_TRUE;  
  78. }  
[cpp]  view plain copy
  1.   


基本的思路就是:

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

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

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

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


gluUnproject

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

gluProject,这里我暂时还没有验证,待我有空的时候再检查一下是否确实如此!呵呵

源自:http://blog.csdn.net/junjie020/article/details/7253151

你可能感兴趣的:(OpenGL,ES)