一.首先我们先来看看这些矩阵在透视投影下的位置
特别说明:objectFrame是用来记录物体发生旋转/平移/缩放等的矩阵(暂且命名为模型矩阵吧),模型视图矩阵(modelViewMatrix)在物体没有发生变化的时候,它是由单元矩阵*观察者矩阵*投影矩阵,当物体发生变化,模型视图矩阵就变成:先观察 -> 物体变换 -> 投影,也就是说单元矩阵*观察者矩阵*objectFrame*投影矩阵
世界上任何事物,当观察者出于的位置不同,事物所呈现在观察者的眼中的画面也是不一样的,就比如当一个帅哥/美女站在你面前,你是观察者,你说处于的位置不同,所看到的也不一样,更加形象的说,世界上任何一种物体(包括人),都可以用一个正方体或者长方体包裹起来,那么从观察者的角度,你最多也就只能看到三个面(总共6个),所以观察者非常重要。绝大部分计算机的显示器是二维的(a 2D surface)。在OpenGL中一个3D场景需要被投影到屏幕上成为一个2D图像(image)。这称为投影变换,需要用到投影矩阵(projection matrix)。首先,投影矩阵会把所有顶点坐标从eye coordinates(观察空间,eye space或view space)变换到裁剪坐标(clip coordinated,属于裁剪空间,clip space)。然后,这些裁剪坐标被变换到标准化设备坐标(normalized device coordinates, NDC,即坐标范围在-1到1之间),这一步是通过用用裁剪坐标的wcwc分量除裁剪坐标实现的。因此,我们要记住投影矩阵干了两件事: 裁剪clipping(即frustum culling,视景体剔除)和生成NDC。
得到了观察者矩阵/投影矩阵/模型视图矩阵后下一步我们就要涉及到一个新的感念------->压栈 PushMatrix()&&出栈PopMatrix();此图引用于:https://www.jianshu.com/p/ce3b51b8f168,作者:凡几多
二.什么是矩阵/矩阵的作用
我们可以把矩阵理解为之一种工具,它可以描述的内容有很多,比如:
1.线性变换
2.线性方程(很多教材都是从这里开始写矩阵的)
3.用于表示一些代数
4.排列数(permutation)
5.可达性(图论)
6.以及很多应用上,比如层次分析法中的比较矩阵、神经网络中数据传递的过程等等
这里我们主要讲的是线性变换(如果你已经忘了大学的线性代数,可以在网上查一查资料复习一下,没有学过线性代数的程序猿问题不大,只是在这里坐一个简单的介绍,你只需要在代码中知道调用矩阵函数的时候在做什么,怎么传参数就行)
什么是线性变换?
对于一个变换 的A,它可以满足可加性与齐次性
然后矩阵可以很清晰地刻画这种变换。为什么这么说?
通过齐次性能够发现,对于一条线上的子空间所有向量经过变换后的结果,只与该空间的单位向量差一个系数。这也就是说,如果想要真正具体地描述一个线性变换,只需要分别知道:每个维度上单位向量经过线性变换后的结果。然后用由单位向量得到的“结果”来刻画整个变化。
比如说:在3维空间中,很容易想到就只有三个坐标轴,
比如说,你想把x轴投射到y轴,y轴投射到z轴,z轴投射到x轴(长度不变)这一变换。那么就需要,让x轴出现在y'轴的位置,y轴出现在z'轴的位置,z轴出现在x'轴的位置。即一个排列组合
就成为这样的矩阵,将左边三个向量都横过来,然后第一个向量摆在第一行,第二个向量摆在第二行,第三个向量摆在第三行。
矩阵就是描述这一过程的重要手段
三.OpenGL变换数据概览
变换 应用
1.视图 制定观察者或照相机的位置
2.模型 在场景中移动物体
3.模型视图 描述视图和模型变换的二元性
4.投影 改变视窗体 的大小或重新设置它的形状
5.视口 这是一种伪变换,只是对窗口上的最终输出进行缩放
视觉坐标
笛卡尔坐标系:从观察者的角度来看,x轴和y轴的正方向分别指向右方和上方。z轴的正方向从原点指向使用者,而z轴的负方向则从观察者指向屏幕内部。 当我们利用OpenGL进行3D绘制时,就会使用笛卡尔坐标系。如果不进行任何变换,那么使用的坐标系将与刚刚描述的视觉坐标系相同。
视图变换
视图变换允许我们把观察点放在所希望的任何位置,并允许在任何方向上观察场景。确定视图变换就像在场景中放置照相机并让它指向某个方向。
模型变换
模型变换用于操纵模型和其中的特定对象。这些变换将对象移动到需要的位置,然后再对它们进行旋转和缩放。
投影变换
投影变换将在模型视图变换之后应用到顶点上,它将指定一个完成的场景(所有模型变换都已完成)是如何投影到屏幕上的最终图像。
正投影:所有多边形都是精确地按照指定的相对大小来在屏幕上绘制的。
透视投影:透视投影的特点是透视缩短,这种特性使得远处的物体看起来比进出同样大小的物体更小一些。
视口变换
当所有变换完成后,就得到了一个场景的二维投影,它将被映射到屏幕上某处的窗口上。这种到物理创口标的映射是我们最后要做的变换,称为视口变换。
模型视图矩阵
模型视图矩阵是一个4X4矩阵,它表示一个变换后的坐标系,我们可以用来放置对象和确定对象的方向。一个包含单个顶点数据的矩阵乘以模型视图矩阵后得到新的视觉坐标。OpenGL并不是将一个4X4矩阵表示为一个浮点值的二维数组,而是将它表示为一个由16个浮点值组成的单个数组。
重点重点重点(重要的事情说三遍):前三纵列分别对应x轴,y轴,z轴上的方向。 如果有一个包含一个不同坐标系的位置和方向的4X4矩阵,然后用一个表示原来坐标系的向量(表示为一个列矩阵或向量)乘以这个矩阵,得到的结果是一个转换到新坐标系下的新向量。这就意味着,空间中任意位置和任何想要的方向都可以由一个4X4矩阵唯一确定,并且如果用一个对象的所有向量乘以这个矩阵,那么我们就将整个对象变换到了空间中的给定位置和方向。
单元矩阵(Identity)
单元矩阵中除了对角线上的一组元素之外,其他元素均为0。将一个向量乘以一个单位矩阵,就相当于用这个向量乘以1,不会发生任何改变。
四.上代码
1.设置观察者矩阵
在函数SetupRC中,我们可以看到这样一行代码:cameraFrame.MoveForward(-15.0f);在上面我已经讲到了视觉坐标,这行代码的意思就是观察者向后移15,注意注意注意:此时我们得到的只是观察者的坐标位置,想用矩阵来表示这个位置,我们把这个点乘以单元矩阵就得到了我们的观察者矩阵,具体代码是://调用顶部载入单元矩阵modelViewMatrix.LoadIdentity();它就自动将观察者位置用矩阵表示出来,当然你也可以不调用,因为在上面的压栈/出栈的流程图里可以看出有一个矩阵1,这个矩阵1就是单元矩阵,默认存在单元矩阵,个人理解是在压栈的时候把传人的点的坐标变成矩阵(理解不对的,请大神批评教育)
2设置投影矩阵
在函数void ChangeSize(int w, int h)里面,
步骤说明:创建矩阵,加载矩阵
参数:35----->你可以理解为眼睛张开的角度
float(w) / float(h)---->(纵横比)
1.0f ----->近平面离观察者的位置
500.f---->远平面的位置
注意:近平面和远平面的参数可以修改,但是你想完整的在2D空间展示图形,这个之间形成的立方体要足够当得下物体,不然超出的部分将被裁减
3.设置模型矩阵(模型矩阵objectFrame,不是模型视图矩阵,个人理解,如有错误请批评教育)
重新强调一下:objectFrame是用来记录物体发生旋转/平移/缩放等的矩阵(暂且命名为模型矩阵吧),模型视图矩阵(modelViewMatrix)在物体没有发生变化的时候,是一个单元矩阵,当物体发生变化,模型视图矩阵就变成:先观察 -> 物体变换 -> 投影,也就是说单元矩阵*观察者矩阵*objectFrame*投影矩阵
所以我们用GLFrame objectFrame记录物体的点,然后乘以单元矩阵,最后就得到了模型矩阵,在
我们会看到关于模型视图矩阵:
特别的是在最后,你会看到
这里就是为了还原模型视图矩阵,其本质就是把模型视图矩阵设置为单元矩阵,防止影响其他视图
记录(objectFrame)物体的点在物体坐标的的位置,
通过乘以单元矩阵
就得到了模型矩阵,最后在压栈的过程中,将观察者矩阵,模型矩阵,投影矩阵相乘,得到模型视图矩阵,最后通过变换管道,提交给shaderManager
最后:附上代码https://pan.baidu.com/s/1OeSWGPGBlOgFrPZeGMS_bw提取码:ae9x