从零开始实现3D软光栅渲染器 (4) 三维空间变换

世界是3D的,显示器是2D,将三维空间的物体变换到二维空间,再到最终屏幕上成像的过程,在图形学中叫做3D渲染流水线。这个过程着实有点复杂了,让我们慢慢来,本节我们先介绍一些基本的空间变换知识。

这部分知识点非常重要,不管以后做游戏、仿真项目,还是其他图形应用,物体的空间变换是必须要理解的,因为很多实际的需求需要你自己分析,再去实现。就Unity来说吧,它已经把图形学中的矩阵、向量运算封装的相当易用了,但是如果你不明白它是怎么玩的话,你自己的需求还是很难实现。很多人说Unity上手快,易学,确实,拖拖拽拽,加个脚本组件就能控制游戏对象,但是要想玩得好,就必须要理解相关的图形学背后的原理。虽然你不用真的把Unity封装的那些高数实现一边,但是你要懂,一个函数调用了它会发生什么。

好了,进入今天的主题。

我们先以2D空间为例,推导空间变换的矩阵表示,3D空间的推理是一样的,照葫芦画瓢就行,后面我们给出3D空间的变换的矩阵表示。

什么是矩阵

我们就把它看成一堆神奇的有规律的数即可,不用想的太复杂,它就是个工具。我们不是搞科研,那是数学家的事儿,我们只是应用矩阵这个工具,来解决图形学的问题。我们没必要对矩阵研究非常深入,我们只需要理解它如何在图形学中的应用即可。就像你会开车,不会修车,但这并不会影响你漂移。

记得线性代数书上引入矩阵给了这么个例子:

这个初中就学过了吧 – 二元一次方程。

这俩方程还能写成:

对,这就是矩阵乘法。

一个2x2(两行两列)的矩阵 乘以 一个2x1的矩阵,得到一个2x1的矩阵。

可以看出:一个 n x n 的矩阵 和 一个 n x m 的矩阵相乘,等于一个 n x m 的矩阵。

问题1

那么,请问:n x n 的矩阵 和 m x n 的矩阵相乘,等于多少。

答案:不能相乘。不相信你乘给我看看。

好了。现在已经学会了矩阵乘法了。你看,矩阵也不是很难嘛。

问题2

矩阵和向量(或者说点)相乘怎么计算?

其实点和向量在表示上是没有区别的(x,y),矩阵是工具,它不会因为你是点不是向量而改变运算规则,对于运算的结果,在于人为的解读。举个例子,你炒菜加糖还是加盐,锅是不会拒绝的,但是对于炒出来的菜,你是要负责任的。我想,你应该懂我意思了吧。

这其实不算问题,上面的就是矩阵和向量相乘呀。

如果有人告诉你(x,y)是个向量,那就默认为它是从原点到点(x,y)位置的一个向量。总不能上面把它竖着写,你就不认识了吧。

那个T叫做转置符号,就是横着写和竖着写的区别。向量还是原来的向量,只是后来有钱了,走路都横着走了,但还是原来那个向量。

现在我们知道,向量其实也可以看作是一种特殊的矩阵。

问题3

此时,你可能会问,为什么要转置啊,不转置不行嘛?

行!当然行。

在我看来,转不转置都一样。都是形式而已。中国人这还用问?

不转置的话,上面的矩阵,就得这么写:

1x2 的矩阵 乘以 2x2 的矩阵,得到 1x2 的矩阵。 没毛病。

我们始终要记住,我们最终关心的是最后得到的操作数,怎么解释它是人干的事,或者说你干的事,和你横着写竖着写有关系嘛?只要你遵循对应的运算法则就是咯。你不要问为啥要有转置,存在即合理,就像学校查卫生时,垃圾桶不准放垃圾一样,谁知道那些人咋想的。

问题4

我想看看两个“真”矩阵相乘。

其实,你仔细品下,你自己也能写出来。

下面,我们来看看矩阵是如何表示变换的。

常见的变换就是:平移、旋转、缩放。像其他错切变换、对称变换啥的,哎,怎么说呢,你平时应该用到不到,用到了再学吧。道理都是相通的嘛,学会了基本的,其他的照葫芦画瓢呗。

平移变换

话说,空间中某个物体,算了,来张我的照片吧。

把我的照片从一个地方挪到另一个地方,需要几步?咳咳,你先别管为什么移我的照片。

很简单,数学上,就是把每个点都平移一下。

假设(x,y)为图像上某一点,移动另一个位置(x’,y’),假设就位移了(a,b)吧,意思就是x方向位移了a,y方向位移了b。

那么有:

x' = x + a
y' = y + b

好了。现在你看能不能写成矩阵的形式。

是不是写不出来啊~~~

先放一放啊,我们后面再说。

旋转变换

现在把我的照片转一转。算了,换张你们喜欢看的吧。

现在我们规定逆时针旋转为正。你要规定顺时针旋转为正。也行。前面都说了,关键看人(你)怎么解释。

我们现在取图像上任一点A(x,y),旋转beta角度之后,到达A’

A点原本与X轴的夹角为alpha,为了方便,设: |OA|=|OA’|=r

则我们可以列出以下方程:

整理下:

现在,你把这个写成矩阵乘法的形式看看。

是不是很简单。

缩放变换

这个很简单。

A(x,y) 放大 Sx 和 Sy倍,得到 A’(x’, y’)

x' = x * Sx
y' = y * Sy

写成矩阵的形式:

齐次座标

好了。基本变换到此结束。

此时,你是不是发现,除了平移变换,旋转和缩放变换都能写成矩阵的形式。别人有的,它当然也想要啊。

所以,引入了齐次座标。

不知道谁起的这个名字,想象力丰富的我,打死我也想不出来它干的事和它的名字有啥关系。

简单说,就是给向量或者矩阵多加一个维度,方便将基本的变换(平移、旋转、缩放)都可以用矩阵乘法的形式表示。

其实,我们知道,就是为了照顾平移变换。

座标变换的齐次座标表示

那么如何表示呢?

我们先将之前的三个变换用齐次座标表示的矩阵形式写出来:

平移变换:

旋转变换:

缩放变换:

大家快速验算一下,应该都成立吧。

通过观察可知,我们将点(x,y) 变成了齐次座标(x,y,1).

有时候给你一个(x,y),你知道它是点还是向量?因为大家都知道,向量的代数表示和点是一样的。

这个时候就是齐次座标的第二个妙用。

点(x,y) 写成齐次座标是: (x,y,1)

向量(x,y) 写成齐次座标是: (x,y,0)

为啥?

我也不知道为啥,我就觉得这么干是对的,而且很神奇。

咱们来验算一下:点是有位置的,向量是没有位置的,是吧。

那咱们用平移矩阵来验算一下不就好了嘛。

数学是不是很奇妙。

那这个有啥用呢?

后面推导3D渲染流水线的投影变换阶段好像用到,到时候再说吧。

其实,一个点或者向量的齐次表示不是唯一的,点(x,y)可以表示成(kx,ky,k),向量(x,y)可以表示成(kx,ky,0). 这个不难理解吧。

三维空间变换

三维空间变换矩阵的推导和二维空间很相似,只不过多了一个维度嘛。

三维座标系,有2种,左手座标系和右手座标系,区别就在于Z轴的朝向不同。为啥有2种。额,我想这个和人有左撇子和右撇子道理应该差不多吧。

那么,我们用那种呢?前面已经说了,随便你,只要你选定一种座标系,数学推导的过程是完全一样的。我们这里推导使用的是右手座标系,因为我是右撇子。

平移矩阵,几乎和二维变化一样:

旋转矩阵,二维空间绕的是原点,三维空间绕的是座标轴。

绕z轴旋转某个角度的旋转矩阵:

大家是不是有点眼熟?对吗,这不就是二维旋转矩阵加了一个维度嘛。大家想想,绕z轴旋转,是不是在XY平面内?那是不是就是二维旋转?这不就得了,那绕x轴和绕y轴旋转,不就是一样的道理嘛?

绕x轴旋转某角度:

绕y轴旋转某角度:

有个事情要注意下啊,我们这里推导的变换矩阵和你们在其他地方看到的可能不一样,别着急。你转置一下看看是不是就一样的了。这是最后一遍唠叨了啊,转不转置,你自己看着办,你喜欢用哪种就用哪种,关键要用对场合。我的建议是,根据你以后学习的图形编程API来决定使用那种风格的矩阵、座标系。比如说,OpenGL,使用的是右手座标系,使用的是向量右乘(矩阵x向量, 而不是向量x矩阵,前者是列向量,就是竖着写的那个,后者是横向量,即使横着写的那个)。所以,我就是按着这个推导的,并不是因为我是右撇子。

本节介绍的基本变换,经过组合,可以形成复杂的变换。

怎么组合?

多个变换矩阵相乘呀,注意,相乘的顺序哟。这个现在你是感受不到的,只有你实战的时候,你才能发现区别。

好了,本节的内容到此就结束了。

小伙伴们有什么建议,可以留言。我也不知道是不是讲得太细,或者讲得太粗,就按照自己的想法来。大家有什么建议可以说出来。或者如果大家有感兴趣的话题,也可以说出来。只要我会的,我也会写相关的教程。其实,我也不是科班出身,只是兴趣使然,欢迎小伙伴一起探讨学习。

欢迎大家关注我的公众号【OpenGL编程】,定期分享OpenGL相关的3D编程教程、算法、小项目。欢迎大家一起交流。

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章