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[], const GLdouble m[], const GLdouble in[])
{
#define M(row,col) m[col*4+row]
out[] = M(, ) * in[] + M(, ) * in[] + M(, ) * in[] + M(, ) * in[];
out[] = M(, ) * in[] + M(, ) * in[] + M(, ) * in[] + M(, ) * in[];
out[] = M(, ) * in[] + M(, ) * in[] + M(, ) * in[] + M(, ) * in[];
out[] = M(, ) * in[] + M(, ) * in[] + M(, ) * in[] + M(, ) * in[];
#undef M
}
// gluProject source code (说明见OpenGL API文档)
GLint gluProject(GLdouble objx, GLdouble objy, GLdouble objz,
const GLdouble modelMatrix[], const GLdouble projMatrix[],
const GLint viewport[], GLdouble *winx, GLdouble *winy, GLdouble *winz)
{
// matrice transformation
GLdouble in[], out[];
//initialize matrice and column vector as a transformer
in[] = objx;
in[] = objy;
in[] = objz;
in[] = 1.0; transform_point(out, modelMatrix, in); //乘以模型视图矩阵
transform_point(in, projMatrix, out); //乘以投影矩阵
//齐次向量的第四项不能为0
if(in[] == 0.0)
return GL_FALSE;
//向量齐次化标准化
in[] /= in[];
in[] /= in[];
in[] /= in[]; //视口向量的作用
*winx = viewport[] + (1 + in[0]) * viewport[2] / 2;
*winy = viewport[] + ( + in[]) * viewport[] / ;
*winz = ( + in[]) / ;
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[], const GLdouble proj[], const GLint viewport[]
, GLdouble * winx, GLdouble * winy, GLdouble * winz)
{
/* transformation matrix */
GLdouble objCoor[];
GLdouble objProj[], objModel[]; /* 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[] = objx;
objCoor[] = objy;
objCoor[] = objz;
objCoor[] = 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[] == 0.0)
return GL_FALSE; objProj[] /= objProj[];
objProj[] /= objProj[];
objProj[] /= objProj[]; /* in screen coordinates */
// 由于投影矩阵投影在[-1, 1]之间,所以需要将转换后的投影坐标放置到[0, 1]之间
// 最后再在一个offset 矩形中转换为屏幕坐标就可以了(viewport[4]可以简单的认为一个offset矩形) #define SCALE_FROM_0_TO_1(_pt) (((_pt) + 1)/2)
objProj[] = SCALE_FROM_0_TO_1(objProj[]);
objProj[] = SCALE_FROM_0_TO_1(objProj[]);
objProj[] = SCALE_FROM_0_TO_1(objProj[]);
#undef SCALE_FROM_0_TO_1 *winx = viewport[] + objProj[] * viewport[];
*winy = viewport[] + objProj[] * viewport[]; /* between 0 and 1 */
*winz = objProj[];
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)最后就是乘上模型视图矩阵的逆矩阵;
Opengl中的gluProject函数认识的更多相关文章
- OpenGL中常用的函数
OPengl的官方文档如下:https://www.opengl.org/sdk/docs/man4/ void glGetIntegerv( GLenum pname, GLint * ...
- opengl中对glOrtho()函数的理解
glOrtho是创建一个正交平行的视景体. 一般用于物体不会因为离屏幕的远近而产生大小的变换的情况.比如,常用的工程中的制图等.需要比较精确的显示. 而作为它的对立情况, glFrustum则产生一个 ...
- OpenGL中glRotatef()函数究竟对矩阵做了什么
OpenGL中glRotatef()函数究竟对矩阵做了什么 我们知道OpenGL中维持着两套矩阵,一个是模型视图矩阵(model view matrix),另一个是投影矩阵(projection ma ...
- openGL中的gl,glu,glut
OpenGL函数库相关的API有核心库(gl).实用库(glu).辅助库(aux).实用工具库(glut).窗口库(glx.agl.wgl)和扩展函数库等.gl是核心,glu是对gl的部分封装.glx ...
- CSharpGL(26)在opengl中实现控件布局/渲染文字
CSharpGL(26)在opengl中实现控件布局/渲染文字 效果图 如图所示,可以将文字.坐标轴固定在窗口的一角. 下载 CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入( ...
- OpenGL中坐标系的理解(一)
在OpenGL中,存在着至少存在着三种矩阵,对应着函数glMatrixMode()的三个参数:GL_MODELVIEW,GL_PROJECTION,GL_TEXTURE. 以下主要描述GL_MODEL ...
- CSharpGL(6)在OpenGL中绘制UI元素
CSharpGL(6)在OpenGL中绘制UI元素 2016-08-13 由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了.CSharpGL源码中包含10多个独立的Demo,更适合入 ...
- OpenGL中glVertex、显示列表(glCallList)、顶点数组(Vertex array)、VBO及VAO区别
OpenGL中glVertex.显示列表(glCallList).顶点数组(Vertex array).VBO及VAO区别 1.glVertex 最原始的设置顶点方法,在glBegin和glEnd之间 ...
- OpenGL中实现双缓冲技术
在OpenGL中实现双缓冲技术的一种简单方法: 1.在调用glutInitDisplayMode函数时, 开启GLUT_DOUBLE,即glutInitDisplayMode(GLUT_RGB | G ...
随机推荐
- ant使用备忘
ant是一个脚本构建工具,可能就是持续集成里面所需要的构建工具. 如果使用eclipse,里面会自带有ant工具,不需要再安装了,创建一个build.xml(或者其他的名字都可以),使用ant来运行就 ...
- 扩展C#与元编程(二)
如果你对Windows Workflow Foundation(WF)一无所知,当看到扩展C#与元编程(一)中由MW编译器生成的FirstLook.mw.cs时,也许这么在想:我KAO,这是C#版的汇 ...
- hive 显示分区
显示某一张表的分区值 show partitions table_name;
- PEP8 Python 编码规范整理(转)
决定开始Python之路了,利用业余时间,争取更深入学习Python.编程语言不是艺术,而是工作或者说是工具,所以整理并遵循一套编码规范是十分必要的.所以今天下午我根据PEP 8整理了一份,以后都照此 ...
- ionic 2,带着运气成分
npm config set loglevel info 查看安装信息 npm cache clean 清除缓存 cnpm sync ionic ...
- redis的五种常见数据类型的常用指令
一.String字符串,key-value 应用场景:string是redis的最基本数据类型,key-value格式,一个key对应一个值的情况下 1.设置key = value:set key ...
- git冲突解决方案 Intellij IDEA
一般在团队合作开发一个项目的过程中,经常出现两个人同时修改一个文件然后都向主master提交commit,这样就会产生冲突(conflict),那么这种情况如何解决? 1 新建分支 如果项目的主分支是 ...
- windows下多个文件合并成一个文件
如果你拿到的是一堆文件,那么你想把它合并成一个文件来使用,那么按下面的步骤,轻轻松松就可以搞定. 第一步:把所有要合并的文件放到同一个文件下面 第二步:在CMD里面进入到你的文件目录 第三步:输入如下 ...
- 【Linux 进程】之关于父子进程之间的数据共享分析
之前我们通过fork()函数,得知了父子进程之间的存在着代码的拷贝,且父子进程都相互独立执行,那么父子进程是否共享同一段数据,即是否存在着数据共享.接下来我们就来分析分析父子进程是否存在着数据共享. ...
- 13-前端不通路径同一个请求访问同一个页面时,有时样式没有加载出来(jss,image,css)
通过如下方式访问同一个网站时,下面一个可以加载样式,而下面一个加载的页面却没有样式,思考良久没有想通,当时也忘记了用浏览器看下 css,js,image的请求路径,其实在前端页面里面我直接: 这样引 ...