为什么突然想起写这个
很多讲Unity渲染,Shader等等的文章或者书里都会提到相机投影,因为渲染管线里比较重要的一环就是如何把一个object投影到View上。大部分时候这个Matrix都是直接给出来的,或者简单两句解释。我看的似懂非懂,所以找了一些资料想来详细看看如何证明的。
准备工作
准备工作自然是线性代数,最重要的一个知识是线性代数。首先复习下面的两个知识点
- 矩阵向量的几何意义是把一个空间向量通过一个矩阵,变换成另外一个空间里的向量
- 齐次坐标的导入
关于1,我们先在平面坐标系,也就是二维空间看几个经典变换。
平面缩放矩阵
如果我们想要把一个平面图形缩放,缩放矩阵很容易写出来。
如果不是等比缩放,比如y轴不变,只改变x轴也是可以的,缩放矩阵的基本形式如下
平面切变变换矩阵
切变稍微复杂一点,但是可以看关键点的对应关系,比如所有点变换后y值都是不变的,也就是说
关于x可以看特殊点,这个点变换后变成了,数轴y上的点变换后的满足 而y轴以外的其他点的x'我们可以在原来的基础上加上这个点本身初始的x值,很容易得出
旋转矩阵
懒得证明了直接放图应该很好懂
需要注意的是说到旋转矩阵我们一般默认是向量沿着坐标系的原点进行旋转。
平面平移矩阵?
现在问题来了,如果我们把一个向量平移要怎么办?
根据上面的图我们很容易写出平移后的坐标和平移前的坐标关系:用矩阵加减法可以表示为下面的样子这就很不舒服了。
之前我们都可以通过一个矩阵来表示变换,但是到了平移这里突然变成了一个算式,失去了一般性,那么有什么办法能让我们把平面内向量的基本变换都压缩在一个矩阵里呢?
这里就引出了第二个重要的知识,齐次坐标
齐次坐标
齐次坐标的定义是为向量或者点增加一个维度。齐次坐标怎么发明出来的先不说,总之这个定义下坐标依然满足:点-点 = 向量,点+向量=点(点按照向量平移),向量-向量=向量。
这就很妙了。
然后让我们用齐次坐标来推导平移矩阵,很容易得出下面的方程和矩阵。
引入齐次坐标后,缩放变换,旋转变换,平移变换所对应的变换矩阵如下所示
组合变换
在现实里我们通常会遇到的是不止一种变换,而是变换的组合比如下面这种情况:
很显然这是一个平移+旋转的组合变换,但是平移和旋转的顺序应该如何呢?
如果我们先平移再旋转就会有下面情景出现:
而先选择再平移才会达到我们的效果
这个例子旨在说明矩阵相乘的顺序很重要。也就是矩阵乘法不满足交换律。
那么如何先变换呢?一个向量先✖️的矩阵就是先做的变换,比如先对v进行M1变换,然后进行M2变换,最后进行M3变换。
在这里,虽然我们的矩阵乘法不满足交换律,但是它满足结合律,我们完全可以先把三个矩阵相乘形成一个3*3的矩阵,然后最后再对向量v做变换。
三维空间变换
3D 变换本质上与 2D 变换一样,都是在向量和点的表示上多了一个维度。
当我们引入齐次坐标之后,3D 的点和向量可以采用如下方式表示。
相对应的各个变换矩阵如下所示
缩放变换
平移变换
旋转变换
旋转变换比较麻烦,沿着x,y,z轴不同的旋转方法有不同的矩阵
仿射变换
仿射变换是指在对一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间。我们可以把上面的所有结合起来把这个齐次方程写为下面的形式。a到i是3*3的矩阵,表示缩放,切变和旋转,是表示各个轴上平移的距离。
还有一个很重要的约定俗成的定义:
通常来说
自此前期准备工作基本完成,我们可以开始着手计算Perspective To View的空间变换矩阵。
两种投影方式
首先在大部分软件里都有两种投影方式,一种是透视投影(Perspective Projection),另一种是正交投影(Orthograhpic Projection)
正交投影映射矩阵
正交投影的核心目标就是:把长方体 [-l,r] × [-b,t] × [n,f] 线性映射到 NDC (归一化的设备坐标空间,是一个 [-1,1] × [-1,1] × [-1,1]的空间)
为何要这样,你可以想象一个摄像机在空间中的任何位置,我们需要把这个摄像机的视锥体(frustum)平移到原点,然后把范围压缩在这样一个空间里。这样摄像机这个视锥体里的每一个点都会同样映射到这个新的视锥体里,而我们就可以得到这个变换矩阵了。
至于为什么要设置成NDC空间这个似乎是有利于GPU的计算,计算机视觉是一门实践科学,总之好用方便的话就约定俗成这么做了。
理解上面这一点就很容易推导出整个矩阵了
至于为什么没有旋转,旋转是在上一个View/Camera的变换里做了,那里把Camera移到原点然后它对着-Z看过去,Y朝上。(此处WIP,可以补充)
自此我们可以得出整个正交变换矩阵如下:
透视投影映射矩阵
总算来到了透视投影矩阵这一part了。这里不同于别的教材,直接从空间投影到平面,GAMES101里面介绍了一个更好理解的方法,我们可以先透视投影变换到正交投影,然后在利用正交投影的变换矩阵进行变换就是最终的透视投影矩阵了。
至于怎么变换,我们还是基本的那个思想,找一个点(x,y,z)然后找它映射到的点(x',y',z')之间的关系
首先我们看空间中任意一点在近平面上的投影,根据这个相似三角形我们很容易得出
进而推导出
同理
在齐次坐标下我们其实做了如下变换
之前我们提到过在其次坐标下, 所以这里我们可以给变换后的向量乘以一个z(只要z不等于0就可以)
我们的persp to ortho变换要做的事情就是下面这个变换:
其实根据这个我们已经可以得出三行了,因为nx对x做了缩放,ny对y进行了缩放,而最后一行z和x,y都没关系
现在我们来看第三行,这一行应该是对z进行变换,关于z值我们有下面两个观测:
- 进平面上的点变换后还是它自己
- 远平面上的点变换后z值不变
已知平面上的点z值为n,且进平面上的点变换后还是它自己,我们可以得到如下变换:
这里当我们用矩阵第三行乘以这个向量时必须得到n的平方,这是一个和x,y无关的值,因此我们可以确定前两个数是0
我们有知道远平面上的点变换后z值不变
我们只需要解一个方程组
很容易可以得出
而最终的透视投影矩阵为:
在Unity里,相机是左右上下对称的,因此
Unity的矩阵好像整体还给这个结果乘以了-1,但是齐次坐标下整体除以一个不为0的数之后表示的还是同一个点,所以这个不影响最终结果。
文章评论