如果要看懂本文,需要了解简单的矩阵运算,包括乘法、逆矩阵、转置、正交矩阵这些概念。
下面内容纯手打,无AI,请放心食用。
3D坐标系
2D坐标系是x轴、y轴相互垂直,并且向右 向上为正,为了方便我们讨论,也要先规定一下3D中的x y z轴的方向。
左右手坐标系如何理解?
简单来说,左右手坐标系就是为了规定旋转的正方向。对于2D我们说一个向量旋转的正方向,那么默认就是逆时针方向。但是在3D中绕轴旋转 ,我们必须确定 朝哪边旋转才是正的。判断方式也很简单,类似于我们初中学过的右手螺旋定则:
使用哪种规定并没有正确错误,只不过是不同的领域的习惯不同,大家都遵守某个习惯,后续讨论可以免于很多重复说明的麻烦而已,不用过分纠结。
3D变换
对于平移和缩放,直接类比2D的变换矩阵
平移:
css transform写法:transform: translateX(x) translateY(y) translateZ(z)
缩放:
css中3d缩放用的是scale3d,但是由于大部分元素都是平面的,缩放z轴看不到有什么变化。如果一个面已经被3d旋转了,再进行scaleZ 是可以看到效果的
绕坐标轴旋转
如绕X轴,当一个向量绕X轴旋转的时候,它的x坐标不变,那么我们可以把它看作是在y-z平面旋转 , 回想2D旋转的矩阵,旋转 的矩阵为 ...
那么我们在3D的4x4齐次矩阵中,保持第一行和第一列不变(x不变):
把2D旋转矩阵的内容入剩下的位置,得到:
但是绕Y轴不太一样,因为在右手坐标系下,Z轴旋转到X轴才是正方向,但是我们说的旋转是X旋转到Z,所以旋转部分这里得取一个负号,也就是 变成
使用html+CSS实现一个立方体
可以利用上面学到的一些变换,来实现一个立方体,思路很简单,创建6个面,分别把他们进行平移、旋转得到前后左右上下6个面,注释在codepen代码里,感兴趣的自己看。 可以参考这个文章来实现:dev.to/joeattardi/…
绕任意轴旋转
3D旋转必须指定一个旋转轴和一个角度,对于一个旋转,我们可以把它分解成绕x、绕y和绕z轴三个方向的旋转,也就是:
感兴趣的可以看下 Rodrigues旋转公式,定义了绕任意轴旋转的变换矩阵。
MVP变换
我们想象一下,拍一张照片需要做什么?
- 调整好物体的位置
- 摆放好相机
- 按下快门
上面这三个步骤分别对应三个变换,分别是模型变换(Model)、视图变换(View)以及投影(Projection)。
任何3D的物体要想渲染在平面中,都需要经过这三个步骤变换。
相机位置视角的约定
在图形学里,我们一般假定相机不动,世界/物体做逆向变换。这个也好理解,物体和相机一起移动,成像不变,那么我们可以把相机往上移动,看成是相机不动,物体向下移动。这样的好处是,固定一个因素,其他计算可以稍微简化一点。
相机的成像取决于三个因素:
- 相机的位置
- 相机镜头的方向
- 向上的方向 。因为镜头是分上下的,你把镜头上下颠倒,拍出来的照片也是上下颠倒的。
我们也约定,相机固定在原点,向上方向为Y,看向-Z,那么就可以得到相机的坐标系三个方向:
那么按照约定,需要将相机坐标系与世界坐标系对齐一下,也就是把让 指向 , 指向 , 与gt平面垂直的 指向 。
这个变换并不好求,但是如果反过来,让 指向 , 指向,指向 比较简单:
看下面这个单位阵,代表初始x y z轴的方向,第一列表示x的单位向量
现在x的单位向量变成了 方向,那么第一列就可以写成
类似的,我们可以写出剩下两列
这个变换矩阵并不是我们想要的,我们想要的是他的逆变换,那么怎么求逆矩阵呢?其实这个矩阵是正交矩阵(三个列向量互相垂直,并且都是单位向量),他有一个很好的性质,就是他的逆矩阵就是他的转置:
投影(Projection)
为什么会有“一叶障目”这种现象?这是因为我们人眼看到物体符合近大远小的透视投影的效果。
正交投影可以理解为摄像机距离物体无限远,在这种情况下,不存在近大远小的效果,在工程领域常用在工程制图上(想一下小学做过的三视图问题)
这里有个图很好的区分了两种投影方式:
可以看到对于透视投影,远近平面形成了一个锥形,我们叫做视锥(frustum),连接远近平面对应的点,相较于一点点就是相机的位置。对于正交投影,远近平面构成是一个长方体。
在图中可以看到,透视投影的成像是存在近大远小的,正交投影不管远近大小都一样,只存在遮挡关系。
正交投影
约定:相机在原点,看相 -Z 方向,相机向上方向为Y
正交投影的过程:
- 物体映射到NDC:先确定物体空间的前后左右上线6个平面,这6个平面构成一个长方体,我们想要把它首先移动到原点,然后缩放成一个 的立方体,我们称之为正则(canonical)立方体。
这个变换比较简单,就是先平移,后缩放,两个变换矩阵相乘得到:
- 从NDC到屏幕
现在所有的点都在标准空间内了,对于一个点(x, y, z)如何显示到屏幕上呢?这里的z之后会用来计算遮挡关系,现在不管,那么只需要关注(x, y)。
标准空间中 ,我们想把他映射到屏幕上 ?怎么做到呢?就是先向右平移1,再把x的范围从2变成1,然后再乘width进行缩放。也就是下面的公式,y坐标也是类似的
透视投影
由于存在近大远小,一个人站在铁轨上向远处看,平行的铁轨看起来相交于一点。
透视投影的过程:
- 把视锥的远平面压缩,得到一个长方体
第一步压缩远平面,坐标变成什么了呢?看下面这个图,他是上面视锥的侧面, 代表原来视锥中任意一点,n代表近平面到相机距离。
首先我们要知道的是,远平面被压缩,z的坐标肯定不变,那么就看x y,看上图存在一个相似三角形,所以得到这个比例:
所以就得到,压缩后y坐标变成了:
- 在对这个长方体做上面正交投影, 这里不再赘述。
可以理解 透视投影 = 压缩远平面 + 正交投影
参考
- 本文内容主要参考了 GAMES 101的Lecture 03, 04。如果有不懂的,推荐去看闫令琪老师的视频。
- 对于其中的 推导过程,个人认为首次理解一下,后续记住结论就行。