OpenGL红宝书正序解读(二)

OpenGL红宝书正序解读二

首先感谢大家对我博客的支持,这篇博客我们重点的解读一下第三章视图的相关概念,本人想在写完这篇博客以后写个代码,囊括第二章第三章的内容
本章视图的目标就是让读者学会,不再使用默认的坐标系和观察点,我们要会自己指定观察的方向和空间中我们要放的物体的位置,同时,我们在把一个三维空间中物体的位置对应到屏幕这种二维面上时,应该怎么做,在这里,给出了一个基本的顺序:
1.包括模型视图投影操作,这三个操作都是由矩阵乘法表示的。
2.裁剪掉裁剪平面之外的物体
3.视口变换,就是让变换的坐标和我们的屏幕像素之间,建立好对应关系

用照相机打比方

之前我们提到了观察方向,这里我们用照相机来做一个比喻,比如我们想拍一张照片,那么我们就要选好角度,这个过程就是视图变换,然后有了好的角度,场景中的物体要怎么摆放呢,这个就是模型变换,然后我们选择了照相机的镜头,调整放大倍数,这个是投影变换,最后我们洗照片,洗多大尺寸的,这个就是视口变换,我们在对顶点操作过程中,视图变换一定要在模型变换之前,在后续你们会看到,这两个其实融为一体了,对于投影和视口变换,可以在绘图之前的任何时候开始。而且绘图我们一般是在上面这四个步骤完成之后才开始的,不要把模型变换当作是绘制图形,只是指定了一个矩阵而已,用于后续我们绘制物体顶点时,进行变换。
对于视图模型投影这三个变换,我们一般会用一个4X4的矩阵来表示,在后面,这些矩阵会和所有绘制的顶点相乘。这个矩阵是放到乘法式子的左边的,大家看好,矩阵的乘法是有顺序的。
在这里选用4*4的矩阵是因为,我们在指定顶点的时候,所有的顶点都是四维的,虽然最后一个大部分情况为0或1(二维和三维),同时,视图和模型变换还会对顶点上的法线产生作用,法线是相对于视觉坐标而言的,这就保证了法线和坐标之间有正确的对应关系。
在这里涉及到了坐标的概念,我会逐步地向大家介绍。
视图和模型变换一起组成了视图模型矩阵,作用于物体坐标,那么最开始,每个物体的初始坐标为物体坐标,与前述矩阵相乘之后,形成的是视觉坐标,视觉坐标是相对于视觉的,所以得在指定视图变换之后才产生视觉坐标。如果此时,有裁剪平面的话,裁剪平面生效。
OpenGL使用投影矩阵对顶点进行变换以后,就产生了裁剪坐标,投影变换定义了一个视景体,视景体之外的空间会被裁剪,紧接着利用透视除法把坐标值除以w,产生规范化的设备坐标,最后经过视口变换,成为窗口坐标。
由于我们绘制的是二维的电脑屏幕,很多人觉得,我们只需要场景中物体的xy坐标即可,但是我们在进行各种变换时,我们的变换也会对物体的z坐标起作用,原因是,我们的z坐标可以表示不同的物体相对于我们照相机的位置,也是深度值,如果遇到了两个物体的遮挡,那么我们可以不绘制被遮挡的物体。

通用的变换函数

首先我们在进行各种变换之前,我们要先明确我们想要修改的是什么矩阵,是模型视图矩阵还是投影矩阵,
我们利用函数glMatrixMode(),来决定我们接下里要修改的矩阵,只有一个参数,是模式值,可以是GL_MODELVIEW,GL_PROJECTION,GL_TEXTURE,一次只能修改一个矩阵,默认情况下,修改的是模型视图矩阵,默认情况三个矩阵都是单位矩阵,在我们利用函数嵌套的调用时,一定记得对矩阵模式进行重置。
函数glLoadIdentity()函数用于将我们现在正在指定的矩阵变成单位矩阵,没有参数。
在这里我们还介绍了两个函数,glLoadMatrix()和glMultMatrix(),函数都是一个指针,指向一个具有十六个值的矩阵,load函数用于把这是十六个值加载到当前矩阵中,mult函数用于把当前矩阵和这十六个值组成的矩阵进行矩阵乘法,这里红宝书明确提出,当前矩阵总是在左边!!!!!!!
认真看书的读者应该有所发现,这里指定的矩阵是列主序的,这与我们c语言的风格截然相反,这里我们使用书中给出的两种方法,是矩阵转置加载和乘法:glLoadTransposeMatrix(),和glMultTransposeMatrix(),这样的话,与我们c语言的思想是完全吻合的。

模型和视图变换

这里的内容比较抽象,就算我用我的话跟大家说明白了,我的话同样抽象,所以我这里不多解释,重点以下几点,这里把模型和视图变换放到一起,原因是因为你不管是移动视角还是移动物体,其实两种方法选其一都是可以做到的,因为位置都是相对的,所以我们一般情况下把模型和视图放到一起讨论,还有一点,我们对于顶点的操作,对于在世界坐标系里,是反序的,对于局部移动坐标系,才是正序的。如果我们不使用默认的视图方式的话,也就是你要用函数来指定,这些函数应该出现在程序里模型变换的前面,这样,我们代码的意思是,我们现在场景里分布好了模型的,之后我们选择在什么位置进行观测。

模型变换

模型变换的函数本书介绍了三个。这三个函数的本质都是,产生一个变化矩阵,然后用这个矩阵为参数调用glMultMatrix*(),而且这样做比直接使用glMultMatrix*()函数速度要快。
glTranslate{fd}()函数参数三个。表示移动。
glRotate{fd}()参数有四个,第一个是旋转的角度,后三个参数表示了一个点,该点和坐标原点形成的线为旋转轴。
glScale{fd}(),参数有三个,分别代表了,拉伸各个轴的倍数,对于局部坐标系,是拉伸的坐标轴,然后里面的物体随之改变。
拉伸函数只有在必要时才使用,因为会改变表面的法线向量,对于光照计算来说,增加了计算成本。

视图变换

最先提到的方法就是利用了运动的相对性,函数就是利用到我们上面讲到的移动和旋转函数。
之后书中提到了一个函数glLookAT()这个函数有九个参数,每三个一组,分别指定了观察点的位置,定义了照相机瞄准的参考点,并提示哪个方向是向上的。
在这里提到了全景扫描的概念,利用glLookAt()实现全景扫描,这里我也不是很懂,希望大家读到这里可以和我讨论。
大家注意:在任何时候,只能有一个视图变换处于活跃状态,我们不能组合两个视图变换的效果,所以我们想使用试图变换的时候,一定要用函数glLoadIdentity()函数,消除之前所做的视图变换。
我们看书过程中应该读到,glLookAt()函数,是一个工具库里的函数,他是一个工具,是以为他封装了glTranslate和glRotate这两个函数,那么如果我们想自定义视图变换函数的话,其实只要在函数体里包含这两个移动和旋转函数即可。具体的书上也没多说,我也不多说哈哈。

投影变换

投影变换区别于模型试图变换,不需要组合其他变换,所以每次我们都要指定投影模式,然后把投影矩阵变成单位矩阵。投影变换的目的是定义一个视景体,有两个作用,第一是说明三维空间的物体是如何映射到屏幕上的,第二是进行裁剪。

透视投影

透视投影是接近我们生活的,就是我们俗称的近大远小,那么我们使用透视投影所用的视景体就是平截头体,距离视点近的,矩形平面小,反之,矩形平面大。我们可以使用glFrustum()函数来指定一个视景体,并且把它与当前矩阵相乘。这个函数有六个参数,大家自己看一下书,每个参数的意义,六个参数就是指定了靠近视点的左下和右上点的坐标,以及前后两个面的距离。这个视景体可以不是对称的,轴也没有要求,而且这个函数可以高分辨率的绘制一幅图像。
除了这个函数,我们还可以使用,gluPerspective()函数,这个函数有四个参数,大家看一下书,四个参数的作用是指定y方向的视野的角度,以及纵横比,还有远近平面的位置,但是这个函数比上面的glFrustum函数要求高一点,我们绘制的函数只能是沿着视线方向的,而且必须是对称xy轴的。而且我刚才查阅资料,视景体和我们的相机可以说是一体的,相机动,视景体也会动。

正投影

正投影是那种用于工业设计领域的,就是物体的大小不会因为距离我们照相机远就变小。所以这个正投影的视景体是一个长方体。我们使用glOrtho()函数来创建一个视景体,参数和glFrustum的参数一样。
这个参数还有一个二维的形式,意义是把一个二维图像投影到二位屏幕。
glOrtho2D()函数有四个参数。指定了一个矩形的左下角坐标和右下角坐标。这个函数假定所有的z值都是-1到1之间。

视口变换

这是我们最后一个步骤,冲洗照片,那么照片要多大的呢,就要用到这个小节的知识。

定义视口

在屏幕上打开窗口的任务不是OpenGL完成的,而是由窗口系统完成的,默认情况下,视口被设置为占据打开窗口的整个像素矩形,我们可以使用glViewPort()函数选择一个更小的绘图区域,还可以对窗口进行划分,显示多个视图。
我们使用函数glViewPort()函数,函数有四个参数,前两个指定视口左下角的坐标,后面两个参数指定视口的宽和高。
这里涉及到了视口和视景体的关系,视口和视景体的纵横比最好相等,这样我们把投影平面的照片洗出来的时候才不会变形,同时,我们改变窗口大小的时候,视口的大小并不会发生改变,所以我们应该在reshape函数里修改视口的大小。

变换深度坐标

深度坐标是在视口变换期间进行编码的,以后存储在深度缓冲区中,我们使用函数glDepthRange()对深度缓冲区的最大值和最小值进行了设定,默认为0-1.这里应用到了深度的知识,我也有点不明白。

操纵矩阵堆栈

模型视图还有投影矩阵,其实都存在于矩阵堆栈里,每次我们进行矩阵的操作,其实都是对堆栈最顶端的矩阵进行的。
书中提到的当前矩阵,都是堆栈最顶端的矩阵,我们可以使用glPushMatrix和glPopMatrix函数来操纵堆栈顶端的矩阵,书中对这两个函数的描述有点晦涩,不过那个里面形容这两个函数的话还是非常形象的,push就是记住自己的位置,pop就是回到原来的位置。
而且我们会在矩阵堆栈的底部压入一个单位矩阵,为了避免重复调用glLoadIdentity()
模型视图矩阵堆栈最多可以有三十二个,投影矩阵堆栈最多有两个,其中一个是用来透视投影,另一个是为了某些特殊的应用程序需要单独打开一个窗口来实现文本提示,由于文本最好选用正投影,所以可以来回切换。

其他裁剪平面

除了我们视景体的六个裁剪平面以外,我们还可以再指定最多六个裁剪平面,我们使用函数glClipPlane,该函数有两个参数,第一个数裁剪平面名,第二个是一个指针。每次使用这个函数时,我们还要使用glEnable(GL_CLIP_PLANEi);i的值可以是0和最大裁剪平面数减一。

逆变换和模拟变换

一般情况下我们是把三维空间里的一个点绘制到二维屏幕上,但是有时我们要这个相反的过程,就是我们点击屏幕上的一个点,可以返回这个点对应的三维空间的位置。我们使用函数glUnProject();(如果深度是默认的0-1),否则我们要用glUnProect4();
glProject与glUnproject相对应,给出一个三维坐标,可以指定二维坐标,这三个函数里的矩阵都是列主序的,我们可以使用glGetDoublev()和glGetIntegerv()来帮助我们,具体的例子见书p111-示例程序3-8.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值