OpenGL中的矩阵变换(上)

OpenGL中的矩阵变换(上)

这里有一句,我认为,最能够消解头脑中的云雾的话: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()}
//1.用户创世第1天,说:苹果! 世上便有了苹果---苹果在模型空间(local 空间)被永恒描述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. //1.用户创世第1天,说:苹果! 世上便有了苹果---苹果在模型空间(local 空间)被永恒描述
  5. DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}
//2.用户创世第2天,说:苹果右移! 于是世界坐标系便左移了,结果存入世界空间glMatrixMode(GL_MODEL);//表示接下来要作“模型转换”glTranslatef(10,0,0);//相当于一个平移矩阵//1.用户创世第1天,说:苹果! 世上便有了苹果---苹果在模型空间(local 空间)被永恒描述 DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}

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

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

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

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

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


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

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

 

转自:http://www.zwqxin.com/archives/shaderglsl.html

你可能感兴趣的:(图形学)