Opengl中的gluProject函数认识

1. 从官方说明如下

  https://www.opengl.org/sdk/docs/man2/xhtml/gluProject.xml

  Name  

    gluProject   — map object coordinates to window coordinatesC,匹配“物体坐标系”“Win窗口坐标系(坐标原点在左下方)”

  Specification

    GLint  gluProject(GLdouble objX,  GLdouble objY,  GLdouble objZ,  const GLdouble * model,  const GLdouble * proj,  const GLint * view,  GLdouble* winX,  GLdouble* winY,  GLdouble* winZ);

  Parameters  

    objX, objY, objZ
                    Specify the object coordinates.
                model
                    Specifies the current modelview matrix (as from a glGetDoublev call).
                proj
                    Specifies the current projection matrix (as from a glGetDoublev call).
                view
                    Specifies the current viewport (as from a glGetIntegerv call).
                winX, winY, winZ
                    Return the computed window coordinates.

  Description
            gluProject transforms the specified object coordinates into window coordinates using model, proj, and view. The result is stored in winX, winY, and winZ. A return value of GLU_TRUE indicates success, a return value of GLU_FALSE indicates failure.

2. 使用介绍

  把空间中的一个三维坐标转换成二维坐标,用这个gluProject函数

3. 原码分析

  原码的写法很好 nice, 先通过看代码,来一步一步分析它的数学原理吧!既学习代码,同时也要掌握数学原理, 在这里才是成长的关键所在!

static void transform_point(GLdouble out[4], const GLdouble m[16], const GLdouble in[4])
{
#define M(row,col) m[col*4+row]
    out[0] = M(0, 0) * in[0] + M(0, 1) * in[1] + M(0, 2) * in[2] + M(0, 3) * in[3];
    out[1] = M(1, 0) * in[0] + M(1, 1) * in[1] + M(1, 2) * in[2] + M(1, 3) * in[3];
    out[2] = M(2, 0) * in[0] + M(2, 1) * in[1] + M(2, 2) * in[2] + M(2, 3) * in[3];
    out[3] = M(3, 0) * in[0] + M(3, 1) * in[1] + M(3, 2) * in[2] + M(3, 3) * in[3];
#undef M
}
// gluProject source code (说明见OpenGL API文档)
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)
{
    // matrice transformation
    GLdouble in[4], out[4];
    //initialize matrice and column vector as a transformer
    in[0] = objx;
    in[1] = objy;
    in[2] = objz;
    in[3] = 1.0;

    transform_point(out, modelMatrix, in);  //乘以模型视图矩阵
    transform_point(in, projMatrix, out);   //乘以投影矩阵
    //齐次向量的第四项不能为0
    if(in[3] == 0.0)
        return GL_FALSE;
    //向量齐次化标准化
    in[0] /= in[3];
    in[1] /= in[3];
    in[2] /= in[3];

    //视口向量的作用
    *winx = viewport[0] + (1 + in[0]) * viewport[2] / 2;
    *winy = viewport[1] + (1 + in[1]) * viewport[3] / 2;
    *winz = (1 + in[2]) / 2;
    return GL_TRUE;
}

  上述原码可以看出:

  (1)观察transform_point函数中矩阵元素和列向量的相乘过程可知,这个矩阵是被转置后再和列向量相乘的。这就是为什么说OpenGL的矩阵相乘是遵循列主元的,而我们使用gluProject函数的时候输入的矩阵参数却是按照行主元的方式。

  (2)从列向量被变换的顺序可以看出,一个齐次表示的3D点是先经过模型矩阵变换(蕴含照相机的位移、旋转、缩放等几何变换),在进行投影变换(蕴含照相机的内参数,即本身的参数),最后使用视口向量将齐次表示的2D点限定在当前的视口内。

  (3)根据函数的输入输出可以看出,输入的3D点是通过直接将向量的第四个元素in[3]赋值为1而齐次化的,输出的齐次表示其实是(winx,winy,1)。winz并不是投影在视口内的2D坐 标的齐次表示的第三个元素,它携带的含义是多方面的:表面上看来,它表示了在视口上的深度值;实际上,它是由于照相机参数的不精确性(所有照相机模型都是 对实际照相机的模拟,因而不可能完全精确的表达照相机本身)和矩阵相乘过程中的计算误差所造成的;因此假如照相机参数对照相机的建模是完美的,并且计算过 程是没有误差的,那么winz的最终结果应该是0,在实际的计算中此参数所返回的值也应该是一个逼近于0的数,这个数的大小反映了误差的总量。所以我们在使用gluProject时通常不必关心winz返回的参数。

  (4)对于返回的(winx,winy),论坛上曾有朋友发问说为什么得到的投影点和实际的点是关于图像上下倒置的。这个是因为OpenGL窗口坐标系统默认(0,0)点是窗口的左下角,而通常Windows窗口和一般图像坐标系的(0,0)点默认在左上角,因此,当我们在这种不一致的坐标系之间进行点的运算时,要先进行一步winy = height – winy; 的转换。

  另一种原码解释

GLint gluProject(GLdouble objx, GLdouble objy, GLdouble objz
                 , const GLdouble model[16], const GLdouble proj[16], const GLint viewport[4]
                   , GLdouble * winx, GLdouble * winy, GLdouble * winz)
  {
       /* transformation matrix */
      GLdouble objCoor[4];
     GLdouble objProj[4], objModel[4];  

       /* initilise matrix and vector transform */
       // 4x4 matrix must be multi to a 4 dimension vector( it a 1 x 4 matrix)
      // so we need to put the original vertex to a 4D vector
      objCoor[0] = objx;
       objCoor[1] = objy;
       objCoor[2] = objz;
      objCoor[3] = 1.0;  

      // 由于原来的向量位于标准基向量(1, 0, 0), (0, 1, 0), (0, 0, 1)中,所以需要先转换到当前的模型矩阵中
      transform_point(objModel, model, objCoor);  

      // 然后将模型矩阵中的顶点转换到投影矩阵所在坐标系的矩阵中
      transform_point(objProj, proj, objModel);  

      // scale matrix
     /*
      GLdouble scaleMat[4][4] =
      {
           {0.5, 0, 0, objPr0j[3]},
          {0, 0.5, 0, objProj[3]},
          {0, 0, 0.5, objProj[3]},
          {1, 1, 1,   1}
      }; 

      GLdouble objProjTemp[4];
       memcpy(objProjTemp, objProj, sizeof(objProjTemp);
       transfrom_point(objProj, scaleMat, objProjTemp);
       */  

      /* or the result of normalized between -1 and 1 */
      if (objProj[3] == 0.0)
          return GL_FALSE;  

      objProj[0] /= objProj[3];
      objProj[1] /= objProj[3];
      objProj[2] /= objProj[3];  

      /* in screen coordinates */
       // 由于投影矩阵投影在[-1, 1]之间,所以需要将转换后的投影坐标放置到[0, 1]之间
      // 最后再在一个offset 矩形中转换为屏幕坐标就可以了(viewport[4]可以简单的认为一个offset矩形)  

   #define SCALE_FROM_0_TO_1(_pt)  (((_pt) + 1)/2)
       objProj[0] = SCALE_FROM_0_TO_1(objProj[0]);
      objProj[1] = SCALE_FROM_0_TO_1(objProj[1]);
      objProj[2] = SCALE_FROM_0_TO_1(objProj[2]);
  #undef SCALE_FROM_0_TO_1  

      *winx = viewport[0] + objProj[0] * viewport[2];
      *winy = viewport[1] + objProj[1] * viewport[3];  

     /* between 0 and 1 */
      *winz = objProj[2];
       return GL_TRUE;
}

基本的思路就是:

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

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

  (3)将顶点缩放到[0, 1]的映射区间中;应该是[-1, 1]

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

  其中,因为顶点缩放到[0, 1]映射后,(1 + in[0]) * viewport[2] / 2; 计算出窗口坐标中的宽度的像素,一种可能的解释是:顶点坐标缩放到[-1, 1]的范围。

  其实gluUnproject和gluProject是非常类似的, 其实就是gluPorject反过来的过程,只是有一些数学运算要注意一下:

  (1)首先,需要将输入的顶点,通过视口变换到[0, 1]之间;

  (2)然后将顶点缩放到[-1, 1]之间,就是上面代码中的scaleMat矩阵的逆矩阵

  (3)然后乘上投影矩阵的逆矩阵;

  (4)最后就是乘上模型视图矩阵的逆矩阵;

  

  

时间: 2024-11-10 08:00:05

Opengl中的gluProject函数认识的相关文章

opengl中对glOrtho()函数的理解

glOrtho是创建一个正交平行的视景体. 一般用于物体不会因为离屏幕的远近而产生大小的变换的情况.比如,常用的工程中的制图等.需要比较精确的显示. 而作为它的对立情况, glFrustum则产生一个透视投影.这是一种模拟真是生活中,人们视野观测物体的真实情况.例如:观察两条平行的火车到,在过了很远之后,这两条铁轨是会相交于一处的.还有,离眼睛近的物体看起来大一些,远的物体看起来小一些. glOrtho(left, right, bottom, top, near, far), left表示视景

OpenGL中实现双缓冲技术

在OpenGL中实现双缓冲技术的一种简单方法: 1.在调用glutInitDisplayMode函数时, 开启GLUT_DOUBLE,即glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);.这里将我们惯用的GLUT_SINGLE替换为GLUT_DOUBLE,意为要使用双缓冲而非单缓冲. 2. 调用glutDisplayFunc(display)注册回调函数时, 在回调函数中所有绘制操作完成后调用glutSwapBuffers()交换两个缓冲区指针. 3. 调用

什么是OpenGL中的深度、深度缓存、深度测试?

原文来自http://blog.csdn.net/xiaoquanhuang/article/details/6613705 1)直观理解 深度其实就是该象素点在3d世界中距离摄象机的距离,深度缓存中存储着每个象素点(绘制在屏幕上的)的深度值!深度测试决定了是否绘制较远的象素点(或较近的象素点),通常选用较近的,而较远优先能实现透视的效果!!! 2)Z值(深度值).Z buffer(深度缓存) 下面先讲讲Z坐标.Z坐标和X.Y坐标一样.在变换.裁减和透视除法后,Z的范围为-1.0~1.0.Dep

OpenGL中glVertex、显示列表(glCallList)、顶点数组(Vertex array)、VBO及VAO区别

OpenGL中glVertex.显示列表(glCallList).顶点数组(Vertex array).VBO及VAO区别 1.glVertex 最原始的设置顶点方法,在glBegin和glEnd之间使用.OpenGL3.0已经废弃此方法.每个glVertex与GPU进行一次通信,十分低效. glBegin(GL_TRIANGLES); glVertex(0, 0); glVertex(1, 1); glVertex(2, 2); glEnd(); 2.显示列表(glCallList) 每个gl

openGL中的混合

    之前在项目中就使用过混合,但是研究的不深入,近期美术的一个需求让我下决心重新深入的研究了一下混合以及它在cocos2d-x中的使用,在这里分享给大家. 混合(blend,有些翻译书上把它称作混融,以下简称混合),在openGL中,当一个输入的片元通过了所有相关的片元测试,就可以在与颜色缓存中当前的内容通过某种方式进行合并了.最简单的,也是默认的方式,就是直接覆盖已有的值,实际上不能称作是合并.除此之外,我们也可以将帧缓存中已有的颜色与输入的片元颜色进行混合.这是在openGL流程上的定义

OpenGL中的颜色混合功能(一)

我们知道,材料属性和光照参数可以极大地增加图形的逼真度,但除此之外,我们在对现实世界进行建模时,有许多效果是通过混合颜色的方式实现的.透明的物体,像是玻璃水杯,在它后面发射过来的光会与透明物体的颜色混合在一起.这种透明在OpenGL中的实现方式,是通过首先绘制背景物体,然后把前景物体(比如水杯)与颜色缓冲区中已经存在的颜色进行混合而实现的.在这一过程中,颜色的alpha值成分发挥了重要作用. 颜色的混合功能 在一般情况下,OpenGL在渲染时把颜色值存放在颜色缓冲区中,把每个片段(像素)的深度值

OpenGL中的深度、深度缓存、深度测试及保存成图片

1.深度 所谓深度,就是在openGL坐标系中,像素点Z坐标距离摄像机的距离.摄像机可能放在坐标系的任何位置,那么,就不能简单的说Z数值越大或越小,就是越靠近摄像机. 2.深度缓冲区 深度缓冲区原理就是把一个距离观察平面(近裁剪面)的深度值(或距离)与窗口中的每个像素相关联.      首先,使用glClear(GL_DEPTH_BUFFER_BIT),把所有像素的深度值设置为最大值(一般是远裁剪面).      然后,在场景中以任意次序绘制所有物体.硬件或者软件所执行的图形计算把每一个绘制表面

OpenGL中的多重采样

抗锯齿处理的最大优点之一就是它能够使多边形的边缘更为平滑,使渲染效果显得更为逼真和自然.点和直线的抗锯齿处理是得到广泛支持的,但遗憾的是,对多边形的平滑处理并没有在所有平台上都得到实现.并且,即使在可以使用GL_POLYGON_SMOOTH的时候,对整个场景进行抗锯齿处理也没有想象中的那么方便.这是因为,抗锯齿处理是基于混合操作的,这就需要从前到后对所有的图元进行排序,这是十分麻烦的. 在OpenGL中还有一个功能,称为多重采样(multisampling),可以用来解决抗锯齿处理中的这个问题.

在OpenGL中给场景添加光照

为了在OpenGL中使用光照计算,我们需要调用glEnable方法,并用GL_LIGHTING作为参数.这个调用告诉OpenGL在确定场景中每个顶点的颜色时使用光照参数和材料属性.当然,如果我们没有指定任何光照参数和材料属性,那么物体仍将会保持为黑暗的无光照状态. // 启用光照 glEnable(GL_LIGHTING); 一.设置环境光 OpenGL提供了一个全局光源,它只发射环境光.这种光源很有用,它可以照射没有被其它光源直接照射的物体的背面,并且如果场景看上去太暗,可以调节这种全局环境光