很久很久以前,那还是大一下,苦逼的学习了openGL,也算踏进了图形学领域吧,如今在中科院的工作也和图形学的相关,一切的偶然加上本人也喜欢图形学的缘故,在这条路走的还那么算回事,基本的知识差不多还是掌握了,也对openGL比较熟悉。可是以后的工作却和图形学一点都不搭边,本来也确实想放弃在这个领域浪费时间了,可是不知道为何又舍不得,所以,心里也暗自说,不管怎样,还是坚持一点点的学习图形学吧,正如乔布斯所说,你学习的任何东西,抑或改变你的一身,原话貌似不是这样啦。结合项目要求,研究了三维点的拾取,其实老早就想写一篇类似的文章了,但是比较偷懒,没有做,也不想发一篇很水的文章。
接吻
在充满三维物体的游戏中,如何能够准确的拾取你指定的物体,对游戏体验很重要,所以今天就来介绍一下如何在三维场景中拾取三维点。并附上了代码。也来明白3维点,如果映射到二维屏幕上面的,本问特别适合初学者。
前戏
我们知道,所谓的3D,其实还是显示在二维屏幕上,只不过图像的呈现非常具有真实感。那么3维的点如何转换到屏幕上的二维坐标呢?
这里已openGL的流程图为例,其他的图形库如DirectX也差不多。
先明白几个图形学中的几个概念,所谓的世界坐标系就是全局坐标系,物体的点坐标就是依据这个坐标系给定的。点,物体的旋转,缩放以及平移都是在这个坐标系中进行的;观察坐标系也称摄像机坐标系,想想一下我们照相的时候,其实是以摄像机为原点建立了坐标系,然后将所拍的景色投影到胶片上,所以这个坐标系决定了点和物体如何映射到观察平面,我们一般有二种投影方式,正投影和透视投影。观察平面可以想象成以前老式相机的胶片,也可以想象成你的屏幕。
有了之前的知识,我们就能明白openGL中一些高级的东西。在openGL中,openGL本身维护了二套矩阵,模型视图矩阵和投影矩阵。我们本身看不到,诸如glTranslate, glRotatef,gluLookAt和glLoadIdentity都是在操作这二套矩阵,至于openGL当前状态下的是什么矩阵,由glMatrixMode决定,相应的参数有GL_MODELVIEW和GL_PROJECTION。
模型视图矩阵M0作用是,将P0点进过一些的动作转换到观察坐标系,这些动作包含旋转,缩放和平移。转换后的P0坐标是以摄像机为原点建立起来的坐标P1。
P1 = M0 * P0
投影矩阵M1作用是,将P1坐标转换到屏幕坐标P2,注意这里的屏幕坐标已经正规化,也就是说,P2的坐标范围已经在【-1,1】里了。至于为什么要正规化,我也不知道,不过貌似很多数学知识都要正规化,可想而知,0,1这二个数字的神奇,哈哈,乱说的,意淫一下。
P2 = M1 * P1
给一个二维的例子吧
在黑色坐标系中的P0,如果在x,y轴平移的话,就会达到P1,记住这里P1还是相对于黑色坐标系,在转换到红色坐标系中后,此刻的P1才有红色坐标系中的坐标,以上的所有动作就相当于模型视图矩阵的作用,即P1 = M0 * P0.
接下来在观察坐标系中的坐标P1,按照下图进行投影(以透视投影为例)
openGL中的观察平面就是上图中的近平面。所以在视野范围的内的点都将会投影到这个近平面上。视野范围就在近平面和远平面之间,其余空间的物体会被裁剪。
高潮
在计算机中,做的工作远比以上介绍的多,诸如裁剪,正规化和光栅化都没有提及,不过单独从编程的角度来看,以上的知识足以。
- void display(void)
- {
- M3DMatrix44f mat_proj, mat_modelview;
- int width = glutGet( GLUT_WINDOW_WIDTH ), height = glutGet( GLUT_WINDOW_HEIGHT );
- glViewport (0, 0, (GLsizei) width, (GLsizei) height);
- glGetIntegerv(GL_VIEWPORT, viewport);
- glClear (GL_COLOR_BUFFER_BIT);
- glPushAttrib(GL_POLYGON_BIT);
- glPolygonMode( GL_FRONT_AND_BACK, GL_LINE);
- glMatrixMode (GL_PROJECTION);
- glLoadIdentity ();
- gluPerspective(65.0, (GLfloat) width/(GLfloat) height, 1.0, 300);
- // 获取投影矩阵
- glGetFloatv(GL_PROJECTION_MATRIX, mat_proj);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- gluLookAt(0, 0, 70, 0, 0, 0, 0, 1, 0);
- glColor4f(0.1, 0.4, 0.6, 0.7);
- glPushMatrix();
- // 获取模型视图矩阵
- glGetFloatv(GL_MODELVIEW_MATRIX, mat_modelview);
- /*** 画出你想要的模型 ,具体可以看代码,在后面的连接中***/
- glPopMatrix();
- glPopAttrib();
- // 配置
- selection.set_config( corners, 8, left_bottom, right_top, mat_modelview, mat_proj, viewport);
- /************************************************************************/
- /* 构造一个新的环境 */
- /************************************************************************/
- if( bool_select_area ){ // 如果在拾取的话,就显示出来
- selection.draw_area();
- selection.highlight_selected_pts();
- }
- }
注意看上面的工作主要是获取当前openGL状态,即得到模型视图矩阵,投影矩阵和视口参数。
既然我们知道得到正规化的坐标点是如下方式:
P2 = M1 * M0 * P0,然后再通过视口参数转换成像素坐标,在判断P2点是否落在所选的区域中,即可判断是否选中三维点。
具体的代码请看后面的连接中 math3d.cpp中的函数m3dProjectXY。
睡觉
具体的代码全部放在我的github上了,具体的点击一下连接
https://github.com/baiyang/opengl
在上几个图,
-----------------打造高质量的文章 更多关注 把酒泯恩仇---------------
为了打造高质量的文章,请 推荐 一下吧。。。。谢谢了,请关注我后续的文章,会更精彩哦
请关注sina微博:http://weibo.com/baiyang26
把酒泯恩仇官方博客:http://www.ibaiyang.org 【推荐用google reader订阅】
把酒泯恩仇官方豆瓣:http://www.douban.com/people/baiyang26/
如果您想转载本博客,请注明出处
如果您对本文有意见或者建议,欢迎留言