« Shadow Volume Demo2乱弹OpenGL中的矩阵变换(下) »

乱弹OpenGL中的矩阵变换(上)

在前面的日志(Shadow Volume 阴影锥技术之探Ⅲ )中,自己稍微提及了NEHE的"3D矩阵求逆"方法之高,并谈了谈自己的一点拙略理解。呵呵,然后,既然如此,一不做二不休,在本篇中得继续乱弹一下下,关于OpenGL矩阵的理解。因为我实在不知道会不会在哪儿就把你忽悠+误导了(但还是期盼你的信任),请看官自重哈哈。
——ZwqXin.com

这里有一句,我认为,最能够消解头脑中的云雾的话:OpenGL中所有的变换,都是在变换坐标系

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/opengl/opengl-matrix-what.html

你还好吗?你还能看见你眼前书桌上那个苹果吗?你把它向右边移动10厘米看看?移好了吗?想一想,假如你眼前就是OpenGL的一个渲染窗口的话,苹果是不是往x轴正方向移动了10单位(假设单位:厘米)?恩,你现在所感受到的真实告诉你:苹果确实不再在原位置,而是向右移了10厘米到了一个新的位置。我想说的是,当你完全迈进3D图形学殿堂后,请不要再那么轻易相信的眼睛所感受到的真实——它是真实,但不是真实的全部。在你刚才一瞬间想象出的OpenGL渲染窗口里,苹果没有移动,它一直在那里,一直....而移动的是整个空间,整个世界,包括书桌,包括你,包括你的眼睛!

在OpenGL的那个世界中,最初存在着几个重叠的空间。有模型空间,有世界空间,视图空间,屏幕空间等。每个空间实质就是一个坐标系(统)——坐标系统当然就有坐标轴。在最初的这个时刻,也许整个OpenGL世界也就只有这一套坐标轴了:恩,一套。世界永远只有一个,但是依附于这个世界的空间则平行地同时存在——实体(例如坐标轴,苹果)唯一,而实体的表示(各个空间中对应的坐标轴,苹果)则多样。我是这么理解的,OpenGL世界。

最初的OpenGL世界只有一套看不见的坐标轴,然后,用户说:苹果!于是世上就有了苹果。苹果轻轻地出现在映射着这个世界的各个空间中——就在那个万物之源的坐标原点上。原点与苹果的“中心”对应(当然“中心”不一定指苹果中间,它由上帝...不,用户在制造这个苹果时决定。确切地说,我们在画物件(或者直接叫:模型)时给予这些东西的各个glVertex3顶点(坐标x,y,z),它们的存在必然以位置(0,0,0)为标尺——这个位置就是此物件的“中心”)。就在苹果出现的此瞬间,模型空间(Model-Coordination,也称Local-Corrdination)安静了,此刻的整个世界的一切如同屏幕截图般被保存起来,储存在模型空间——永远以当前这个“中心”为原点,以当前这些坐标(x,y,z)为苹果各顶点的"模型坐标系坐标"。

  1. //1.用户创世第1天,说:苹果! 世上便有了苹果---苹果在模型空间(local 空间)被永恒描述
  2. DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}

当然,苹果出现在模型空间坐标系统原点,必然也就出现在其他空间坐标系统的原点,此时各空间的坐标系是重合的,只不过模型空间被固定了,其他空间还没而已。苹果怎么移动到别处呢?恩,编程的时候可以这么问,但当你正在了解OpenGL世界的时候(如现在),这个问法很不靠谱。你看上面的代码(glVertex3f(A); glVertex3f(B)....)这里点A,B...不是给定了吗?就算是变量,在“画点”的时刻变量的值也是给定的呀。是的,变的不该是苹果,而是坐标系,准确地理解,是世界空间(World-Coordination)的那个坐标系要变,在原先与模型空间坐标系重合的基础上变。这个变的过程,叫模型变换(Model-Translation)。具体来说就是用一个表示“转动/移动/缩放”的矩阵左乘苹果的各个顶点坐标,得出的结果(是一些不同的坐标)作为苹果对应顶点在世界空间坐标系中的位置坐标。诶?苹果变了吗?可以这么说,苹果的“坐标”还是原来那些A.B...但对应于世界坐标系的“位置”变化了。

这里确实是很容易迷糊,默念吧:OpenGL中所有的变换,都是在变换坐标系。不是苹果右移了10厘米,而是世界坐标系左移了10厘米!假设苹果中心最初恰就是出现在模型空间并与模型坐标系原点重合,然后考虑以此为初状态的世界空间,在用户做了模型变换(向苹果中心点所在之坐标(0,0,0),左乘一个表示右移10厘米的模型变换矩阵)后,这个坐标由(0,0,0)变成(10,0,0),相当于坐标系原来的虚拟坐标(10,0,0)变成现在的(0,0,0)。看,坐标系(由无限的虚拟坐标构成)左移10厘米!我想说,这才是模型变换(Model-Translation)的真正所为。与之前一样,当一切变换完成,结果被截屏存入世界空间。世界空间的动荡结束,从此定型。

  1. //2.用户创世第2天,说:苹果右移! 于是世界坐标系便左移了,结果存入世界空间
  2. glMatrixMode(GL_MODEL);//表示接下来要作“模型转换”
  3. glTranslatef(10,0,0);//相当于一个平移矩阵
  4.  
  5. //1.用户创世第1天,说:苹果! 世上便有了苹果---苹果在模型空间(local 空间)被永恒描述 
  6. DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}

接下来发生的事情跟之前一样,只是从模型空间---(模型变换)---》世界空间的关系,改成世界空间---(视图变换 View Translation)---》视图空间的关系而已。请好好再模拟一次:一模一样的过程,这次的移动所需要的左乘矩阵(左乘刚才保存的模型转换结果嘛)由相机(眼睛,视线)的设置提供,结果存入视图空间而已。

  1. //3.用户创世第3天,说:我是主角! 于是他拥有了第一人称视觉,结果存视图空间
  2. glMatrixMode(GL_VIEW);//表示接下来要作“视图转换” 
  3. gluLookat(eye,look,up)//也相当于一个平移矩阵 
  4.  
  5. //2.用户创世第2天,说:苹果右移! 于是世界坐标系便左移了,结果存入世界空间 
  6. glMatrixMode(GL_MODEL);//表示接下来要作“模型转换” 
  7. glTranslatef(10,0,0);//相当于一个平移矩阵 
  8.  
  9. //1.用户创世第1天,说:苹果! 世上便有了苹果---苹果在模型空间(local 空间)被永恒描述  
  10. DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}

噢,接下来是:视图空间---(投影变换)---》屏幕空间。投影变换的变换手法与之前的不同,屏幕空间以视图空间结果为基础,先用一个平头锥体(视景锥)把视线范围外的空间割了(裁剪),再把投影到一个可以覆盖渲染屏幕窗口的矩形上(具体做法是,XYZ除以隐含的齐次坐标W,然后舍弃深度Z),保存为屏幕空间——一个平面,让用户所能感悟到这一切不过显示器屏幕一部分像素的把戏。当然,最后的坐标还要隐映射为渲染窗口的客户区坐标(原点在窗口左上角,Y轴下X轴右的平面坐标),算是走出了OPENGL世界。

  1. //4.用户创世第4天,说:到显示屏来吧! 世界便是"平"的了.最后得屏幕空间
  2. glViewport(0,0,width,height);//设置那个"视景区矩形"大小
  3. glMatrixMode(GL_PROJECTION));//表示接下来要作“投影转换” 
  4. gluPerspective(fov,aspect,near,far);//视景体应用
  5.  
  6. //3.用户创世第3天,说:我是主角! 于是他拥有了第一人称视觉,结果存视图空间 
  7. glMatrixMode(GL_VIEW);//表示接下来要作“视图转换”  
  8. gluLookat(eye,look,up)//也相当于一个平移矩阵  
  9.  
  10. //2.用户创世第2天,说:苹果右移! 于是世界坐标系便左移了,结果存入世界空间  
  11. glMatrixMode(GL_MODEL);//表示接下来要作“模型转换”  
  12. glTranslatef(10,0,0);//相当于一个平移矩阵  
  13.  
  14. //1.用户创世第1天,说:苹果! 世上便有了苹果---苹果在模型空间(local 空间)被永恒描述   
  15. DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}

事实上OpenGL的glMatrixMode函数里没有GL_MODEL和GL_VIEW这两种设值。上面提到过,模型变换和视图变换其实是同一种处理,而且视图变换只由相机控制,有则有,无则默认。因此OpenGL直接就用GL_MODELVIEW一起转换了。简洁是简洁,但是这样一来用户就不知道世界空间里的坐标系什么时候改变了,也难得到物体在世界坐标系下的位置坐标了(事实上还是有办法的,不过搞复杂了,见以后的博文啦)。

  1. glViewport(0,0,width,height);//设置那个"视景区矩形"大小 
  2.  
  3. glMatrixMode(GL_PROJECTION));//表示接下来要作“投影转换”  
  4. glLoadIdentity();
  5. gluPerspective(fov,aspect,near,far);//视景体应用 
  6.  
  7. gluLookat(eye,look,up)//控制整个视景体   
  8.  
  9. glMatrixMode(GL_MODELVIEW);//表示接下来要作“模型视图转换”  
  10. glLoadIdentity(); 
  11. glTranslatef(10,0,0);//相当于一个平移矩阵   
  12.  
  13. DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}
  14. ........

此外,每次变换不是以之前保存的那个空间的位置信息为基础吗?这要用到glLoadIdentity()函数,初始化当前矩阵,为什么?还是看下篇吧:
乱弹OpenGL中的矩阵变换(下)


P.S.有时候发觉,有些BLOG文是行文一塌糊涂,但写着写着自己心里“塌实”了。

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/opengl/opengl-matrix-what.html

  • quote 1.pbhy1212
  • 你关于OpenGl渲染世界的世界观写得太好了,直观又有条理。从你的博客的文章中学到了不少东西,谢谢。1
  • 2009-2-25 8:34:48 回复该留言
  • quote 2.chris
  • 写的挺好的,有点疑问,gluLookat写在glMatrixMode(GL_MODELVIEW)之上是不是欠妥?
    这样的话它会把glulookat产出的矩阵乘到投影矩阵里去了,如果我unproject就不对了
  • 2009-2-25 12:11:32 回复该留言
  • quote 3.zwqxin
  • To pbhy1212:
    谢谢,呵呵,你是本博客第一个评论者呢~~

    To chris:
    你好,阁下真是金睛火眼呢~的确这样做是不好的.glMatrixMode(GL_MODELVIEW)的作用是表明接下来的操作是模型变换和视图变换.gluLookAt正是生成视图变换矩阵(UVN)的函数.你的考虑是正确的.虽然就左乘顺序效果来说是一样的







  • 2009-3-3 21:31:55 回复该留言
  • quote 4.王鑫
  • 你写的文章真是太精彩了,很受益,谢谢楼主...
  • 2010-5-6 14:26:38 回复该留言
  • quote 5.adouta
  • 写到不错,最近在看3D图形学第三版,理论性太强,有的时候都坚持不下去了,就转过来看看贴近编程的东西
  • 2012-1-11 10:01:24 回复该留言
  • quote 6.eggman
  • view翻译过来应该叫观察 叫观察变换 什么视图变换莫名其妙的名字
    还有观察变换并不只是平移还包括旋转 否则look,up向量是干嘛的
  • 2017-7-2 18:42:53 回复该留言

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

IE下本页面显示有问题?

→点击地址栏右侧【兼容视图】←

日历

Search

网站分类

最新评论及回复

最近发表

Powered By Z-Blog 1.8 Walle Build 100427

Copyright 2008-2013 ZwqXin. All Rights Reserved. Theme edited from ipati.