OpenGL mvp矩阵使用及其相乘的顺序思考 以及案例 03:金字塔、六边形、圆环的绘制的demo代码分析

本分析基于第四节课 001--OpenGL图元绘制(综合) demo

1 先谈一谈理论依据,这里借用一下cc老师的课件里几张图

image.png

image.png

也就是说 物体的坐标 ,再经过模型变换(m矩阵),视变换(v矩阵),投影变换(p矩阵)后(也就是m矩阵 v矩阵 p矩阵 进行矩阵乘法运算后)就得到了裁剪坐标。目前我们的操作呢也仅仅就是到这一步,至于裁剪后到屏幕显示,那都是内部操作了,目前暂时不需要我们关心。

那么,各个矩阵是按什么顺序相乘的呢?是m * v * p 还是 v * m * p 还是 p * m * v还是什么组合呢?这里我们先猜一下,根据图片中初步估计呢,应该是m * v * p?

2

当然在解释此篇文章所讨论的点时,结合第4节课cc老师讲的一些重点,前提条件是你需要有对矩阵栈的使用和理解,以及使用transformPipeline几何变换的管道来得到最后的结果矩阵。
关于矩阵栈看这图就应该明白了,就不过多解释了,这里借用一下凡几多这位作者的图。


image.png

这张图已经描述的很清楚了,只是稍微遗漏了一点讨论,图中只说了相乘,是矩阵2乘矩阵3 得到的矩阵6.还是矩阵3乘矩阵2 得到的矩阵6呢(这里插播一句:矩阵的相乘是不满足交换律的,所以对于 a * b 和 b * a(单元矩阵除外),其得到的结果是2种完全不同的哦)(其实也就是分析矩阵相乘方法m3dMatrixMultiply44,void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b),到底是 a * b的结果放到product里呢,还是b * a的结果放到product里呢,因为也没有m3dMatrixMultiply44的源码看,所以这里我们都不知道)。这将是我们接下来分析讨论的一个点。
transformPipeline呢,没什么好分析的,名词解释,其使用呢,也有源码可看。所以这里就不分析他了(当然在下面分析的时候,会顺带提一句其源码的操作)。

其实矩阵栈也好,transformPipeline也好,都只是一个工具而已,只不过里面封装了一些方法罢了,而使用它们的目的基本上都是为了得到一个所需要的矩阵,就这么简单。在demo里,你甚至有2种其他的写法,1:只使用矩阵栈,2.矩阵栈和transformPipeline都不使用,结合矩阵相乘方法m3dMatrixMultiply44,自己封装一些简单的方法一样可以实现demo里的效果(当然这2种写法不是此篇文章讨论的重点,感兴趣的各位看官当你看完这篇文章后,自然就知道这2种写法该怎么写了)。

那么最后我们需要得到的结果矩阵到底是按什么顺序相乘的呢,正如前面第一张和第二张理论依据的图片,m矩阵 v矩阵 p矩阵 是按什么顺序进行矩阵乘法运算呢?

Talk is cheaper, show me the code

接下来结合demo代码运行 ,以及断点调试,一步步来分析各个矩阵是按什么顺序相乘(老司机们已经在旁边准备好纸和笔 瓜子和花生慢慢品了)

先下断点。首先我们在

image.png

GetProjectionMatrix的源码实现里下一个断点,也就是这里
image.png

在这里下个断点主要是为了看一下p矩阵的打印结果。
(ps:说明一点呀,p矩阵就是投影矩阵)

然后在这里分别下两个断点

image.png

在这里下断点主要是为了看一下v矩阵和m矩阵的打印结果。
(说明一点呀,v矩阵就是观察者矩阵或者叫视矩阵,也就是那个mCarmer参数。m矩阵就是物体矩阵或者叫模型矩阵,也就是那个mObjectFrame参数 ps:这个参数命名有点乱哈,但这不是重点,解释一下就好了,各位看官应该是懂的,咱们继续)

然后再在

image.png

GetModelViewProjectionMatrix的源码里下个断点,也就是这里
image.png

在这里下断点主要是为了看一下m矩阵 v矩阵 p矩阵 经过乘法运算后得到的结果矩阵的打印结果。

接下来把我们的demo跑起来
我们可以看到
这是p矩阵的打印结果


image.png

这是v矩阵的打印结果


image.png

这是m矩阵的打印结果
image.png

矩阵呢是用一个数组表示的。


image.png

我们把上面的p矩阵,v矩阵,m矩阵整理一下。
m[16] = {-1,0,0,0,0,1,0,0,0,0,-1,0,0,0,0,1}
v[16] = {1,0,0,0,0,1,0,0,0,0,1,0,0,0,-15,1}
p[16] = {2.37869596,0,0,0,0,3.17159462,0,0,0,0,-1.00400805,-1,0,0,-2.00400805,0}

好了,各位看官看到这里,会不会好奇那这个矩阵到底长怎么样子呢(没看懂我想表达的意思?您接着往下瞧)
我给各位举个栗子。有这么个矩阵a,各位看官是觉得是第一种呢?还是第二种呢?


image.png

先别急着选。这里我把第1种读法,叫做行矩阵读法。把第2种叫做列矩阵读法。

3 我们先分析第一种读法:行矩阵读法

接下来mvp就依次写为矩阵(这里我就不写了,各位看官可以自己在纸上写一下,记录一下)。
程序进入这个断点后


image.png

我们依次看一下各个参数的打印结果

p _mProjection->GetMatrix() 也就是打印出我们的p矩阵结果


image.png

p _mModelView->GetMatrix() 也就是打印出我们的modeview栈里 m矩阵和v矩阵相乘的结果(这里我们假装还不知道到底是m * v 还是 v *m,当然聪明的各位看官相信已经拿起笔进行矩阵运算,并且已经得到了结果)


image.png

在打印一下_mModelViewProjection,这里也就是打印出我们的m矩阵和v矩阵和p矩阵相乘的结果(这里我们假装还不知道m矩阵和v矩阵和p矩阵是按怎么个相乘的顺序得到的结果,当然聪明的各位看官相信已经拿起笔进行矩阵运算,并且已经有了结论)


image.png

各位看官拿起手中的纸和笔进行一下矩阵乘法运算后,相信已经知道结果了,
这里我就直接给出结论了(当然各位看官最好是自己验算一下)。
_mModelView->GetMatrix() 呢,是 m和v进行乘法运算得来的结果,最后证明是 m * v得来的
_mModelViewProjection呢,这个最后的结果 将是 由 m * v * p 得来的
从而也证明了m3dMatrixMultiply44方法 是 b * a 后放入 product里的

(你以为这就完了?还早呢。。。哈哈)

4 我们再分析第二种读法:列矩阵读法

数据都是一样的,只是把数组读成矩阵时我们换了一下。
相信聪明的各位看官已经拿起纸和笔 在进行运算了

这里就不给大家卖关子了,直接给出结论了(当然各位看官最好也是自己验算一下)。
此时呢
_mModelView->GetMatrix() 呢,是 m和v进行乘法运算得来的结果,最后证明是 v * m得来的
_mModelViewProjection呢,这个最后的结果 将是 有 p * v * m 得来的
从而也证明了m3dMatrixMultiply44方法 是 a * b 后放入 product里的

看到这里大家伙是不是有点懵逼呢?
到底是m * v * p还是p * v * m。
到底m3dMatrixMultiply44方法 是 a * b 后放入 product里的。

如果你对这个感兴趣了,那么当你在网上查找相应的文章时,将会看到和这张图里描述的内容差不多的话语。


image.png

对于图里描述的内容,我是这么理解的
如果你的Vlocal向量是一个行向量(也就代表你采用是行矩阵读法),那么进行矩阵数学运算时,你就得从右往左读,从而来进行矩阵运算,也就是Vlocal * Mmodel * Mview * Mprojection,最后得到的结果就是Vclip(它也是个行向量)

如果你的Vlocal向量是一个列向量(也就代表你采用是列矩阵读法),那么进行矩阵数学运算时,你就得从左往右读,从而来进行矩阵运算,也就是Mprojection * Mview * Mmodel * Vlocal ,最后得到的结果就是Vclip(它也是个列向量)

其实这两种都能说通,为什么这样说呢,原因是矩阵里的转置运算。这里就直接用百度搜索的图啦


image.png

5 最后结论:

mvp到底是按什么排列相乘呢? 只能是 m * v * p 或者 p * v * m。绝对不可能是其他的乘法顺序。
哈哈,至于为什么是m * v * p 或者 p * v * m,我只能告诉你,这是因为你对数组读成矩阵时,选择哪一种读法有关。

你采用行矩阵读法
那 就是 m * v * p ,m3dMatrixMultiply44方法 就是 b * a 后放入 product里的。
最后结合物体坐标vlocal(行向量) 也就是 m * v * p * vlocal,最后得到裁剪坐标 vclip(行向量)
你采用列矩阵读法
那 就是 p * v * m ,m3dMatrixMultiply44方法 就是 a * b 后放入 product里的。
最后结合物体坐标vlocal(列向量) 也就是 vlocal * p * v * m,最后得到裁剪坐标 vclip(列向量)

所以呢,其实他两本质上都是一样的,原因上面也解释过了,这是因为矩阵里的转置运算。因为行矩阵读法和列矩阵读法,实际上就是进行了一下矩阵转置嘛。所以才会这样。

至于我们到底是行矩阵读法,还是列矩阵读法?前面也说了,都差不多,如果非要现在给出一个答案。

目前我学的知识还不多,还不太了解,所以不支持我给出答案,后续如果学到了相关知识,有了答案再来补上,不过通过查阅了一下M3DMatrix44f的头文件声明里的注释
image.png

6 目前来看,我选择列矩阵读法

7看到这里就结束了?哈哈,其实还有一个疑问点,这里将这个新的问题的点抛出来给大家想一想?

如同所示。不知道各位看官有没有好奇过GetCameraMatrix和GetMatrix这两个方法。以及为什么cameraFrame就要调GetCameraMatrix,而objectFrame就要调GetMatrix呢?相信有人就会说了,你这个不会看名字嘛,cameraFrame当然是调GetCameraMatrix呢,objectFrame就调GetMatrix,名字搭配嘛。看似好像很合理,实则只能说明这种想法太浅薄了。为什么这样说呢?


image.png

各位看官接着往下看。
我们玩一下这个代码,怎么个玩法呢?
第一种玩法,我们将1的位置那里原来是cameraFrame.GetCameraMatrix(mCamera);我们调用GetMatrix,也就是改为了cameraFrame. GetMatrix(mCamera);当然2那里我们不改,还是老样子objectFrame.GetMatrix(mObjectFrame);然后我们把代码跑起来,我们会发现最后什么也看不到。底下各位看官估计有人就会想了,我早就想到了,cameraFrame必须和GetCameraMatrix配合使用,正是因为博主你不配合使用,所以就没有结果了,博主你就是个2货。 好像很有道理喔?

no no no.如果你这么想的话,那就说明你有了一个先入为主的思考了。我们继续辩证。

第二种玩法,我们1那里不变,1那里还是cameraFrame.GetCameraMatrix(mCamera);,我们变动一下2,objectFrame我们调用一下GetCameraMatrix这个方法,也就是改为objectFrame.GetCameraMatrix(mObjectFrame);假如各位看官你先别跑起来代码,你猜一下会是什么结果?估计有人会说了有啥结果可猜的,肯定也是什么都不显示了,因为博主你没有搭配使用,博主你在乱使用。那我们跑起来看看。
哈哈,你会发现实际上也是有东西显示了,只不过相比于原来,点位置变了,连线位置变了,三角形位置变了,后面几个图形看起来好像是没变,其实也是变了,不信你操作一下上下左右看看,其实也和原来有点不一样了。而且原来你会发现,当你按着右方向键时,你会发现图形是按逆时针方向旋转的,而修改了之后,你会发现,同样当你按着右方向键时,你会发现图形是按顺时针方向旋转的(反正就是你修改前和修改后,你都按着右方向键,你会发现两者的旋转方向是不一样的)。

通过第二种改法,我们就应该知道,所谓cameraFrame搭配GetCameraMatrix,objectFrame搭配GetMatrix使用,根本是不成立的。

只不过这个demo最初让你看到的是cameraFrame.GetCameraMatrix(mCamera),objectFrame.GetMatrix(mObjectFrame)这么个搭配而已。如果这个demo最初不是这么个搭配法,而是第二种改法的这种搭配法让你最初看见了,相信各位看官应该就不会产生这种所谓搭配使用这种想法了。(如果你还要坚持这个搭配使用这种想法,那我也没办法,就当我提出的这个问题不存在吧)

那么GetCameraMatrix和GetMatrix到底怎么使用,到底哪个frame用哪个呢,什么情况下用这个,什么情况下用那个,这就是需要思考的问题啦?各位看官可以自己先思考一下或者问一下cc老师是怎么处理!

哈哈

本篇文章就到此结束啦。

你可能感兴趣的:(OpenGL mvp矩阵使用及其相乘的顺序思考 以及案例 03:金字塔、六边形、圆环的绘制的demo代码分析)