WebGL第四十一课:3D前置知识点之齐次空间

244 阅读6分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第3篇文章,点击查看活动详情

本文标题:WebGL第四十一课:3D前置知识点之齐次空间

友情提示

这篇文章是WebGL课程专栏的第41篇,强烈建议从前面开始看起。因为花了大量的工夫来讲解向量的概念和矩阵运算。这些基础知识会影响你的思维。

引子

首先有一个问题,一直横亘在新手面前,也就是在vertex shader里的内置变量gl_Position既然代表了一个位置信息,那么为什么这东西有四个分量: xyzw?
难不成,WebGL 还考虑到四维空间???
第四维难不成代表了时间,难道是用来做动画用的???

显然,这是不可能的。本文就来说明一下这个 w分量的效果和作用。

一维数轴上的变换

我们来考虑最简单的一种空间,就是一维空间,就是一根数轴,我们一般称之为x 轴

我们现在给出这x 轴上的两个点 -11

一般情况下,我们有以下两种需求:

  • 对这两个点进行拉伸一个固定系数,比如说变成原来的2倍,亦即变成-1 -> -21 -> 2
  • 对这两个点进行位移一个固定系数,比如向右移动2个单位,亦即变成-1 -> 11 -> 3

第一个拉伸,很容易表示:

x' = 2 * x;

第二个位移,也容易表示:

x' = 2 + x;

问题到这里,好像已经结束了。但是实际情况就是,我们更喜欢用矩阵来表示这些变换操作。

也就是说, 这种形式:

x' = 矩阵 * x; 才是我们需要的形式。

在一维的情况下,矩阵本身就是一个数字,所以能方便的表示拉伸。

但是,用来表示位移就不可能了。因为他已经表示拉伸了。换句话说:

x' = A * x; 这个式子,无法形成一个效果:

让 任意的x 进行位移一个常量。

到此为止,问题好像无解。但是,我们喜欢用矩阵,不代表,我们非要用一维矩阵。

就算是来解决一维向量的位移,我们还是可以使用更高维(比如说二维)的矩阵。只要是矩阵就行了,我们就这一个需求。

使用二维矩阵来表示一维数轴上的位移

现在我们试图来寻找一个二维矩阵T, 使得这个T可以来代表一维向量的位移。

划重点:我们现在还是来解决一维向量的位移,而非二维。

我们现在的工作空间是二维的,所以,我们可以利用二维空间的所有特性了,比如说,把原始要进行位移的向量坐标x写成两个数:

  • (x, 1)

有人问了,第二维写个1,算怎么回事?

先不管为什么要写1,我们接着往下推导。

我们将(x, 1)这个点,往右位移A,得到的结果坐标是(x + A, 1)

我们发现一个东西,得到的结果,如果,我们把 1 去了,那就是我们想要的效果,让x变成了x+A

好了,现在问题变成了:

  • 找到个二维矩阵T,使得 T(x1)=(x+A1) T* \left(\begin{array}{cc} x\\ 1\end{array}\right) = \left(\begin{array}{cc} x + A\\ 1\end{array}\right)

我们假设能找到这个T,我们的需求就已经达成了,即使运算的结果是二维向量,但是我们只需要简单的剔除那个1, 就可以得到真正的x+A这个数。

一维向量的位移矩阵

这个矩阵T,其实很容易得到,如果你对矩阵乘法十分熟悉的话,那么是可以口算的,如果你不能口算,建议你看一下我前面的一篇文章WebGL第十四课:从向量到矩阵 - 掘金 (juejin.cn)

我们这里直接给出最终形式:

[1A01]\begin{bmatrix} 1 & A \\ 0& 1\end{bmatrix} * (x1)\left(\begin{array}{cc} x\\ 1\end{array}\right) = (x+A1)\left(\begin{array}{cc} x + A\\ 1\end{array}\right)

  • 上面的形式是矩阵式
  • 上面能得出 x + A

不过,这个矩阵只能表达位移,我们不妨把拉伸融合进去,如果你对矩阵乘法十分熟悉的话,也是可以口算出来:

[BA01]\begin{bmatrix} B & A\\ 0& 1\end{bmatrix} * (x1)\left(\begin{array}{cc} x\\ 1\end{array}\right) = (Bx+A1)\left(\begin{array}{cc} Bx + A\\ 1\end{array}\right)

在上面的式子里,B和A,分别代表了拉伸系数和位移系数。简单吧,用一个矩阵就能表达出来。

目的最终是完成了。

二维空间的变换

数学最大的好处就是扩展,上面的理论对于一维能适用,那么对于二维也同样。

我们给出最终形式,来进行直接对比:

[Sx0Dx0SyDy001]\begin{bmatrix} S_x & 0 & D_x \\ 0 & S_y & D_y \\ 0 & 0 & 1 \end{bmatrix} * (xy1)\left(\begin{array}{cc} x \\ y \\ 1 \end{array}\right) = (Sxx+DxSyy+Dy1)\left(\begin{array}{cc} S_x * x + D_x \\ S_y * y + D_y \\ 1\end{array}\right)

通过上面,我们可以看出,完全可以表达二维向量(x,y)的拉伸和位移了。

只不过,二维向量,还有一个重要的变换,就是旋转,这个东西也可以融合到上面去。口算有点难度,这里就不给式子了。可以参考我前面另一篇文章WebGL第十九课:旋转及其矩阵表达(涉及数学推导)| 8月更文挑战 - 掘金 (juejin.cn)

三维空间的变换

一维到二维的扩展,简单如斯,那么二维到三维,也是简单如斯。

简单到,我都懒得写了,有兴趣的朋友,可以自己试试手写一下,三维空间的拉伸和位移矩阵式。

旋转就别手写了,需要用到,就去网上复制粘贴吧。

文章后面,给出拉伸位移矩阵式,可以用来对照一下,看看写得对不对。

齐次空间

到现在为止,我相信大概已经明白了,WebGL 实践中,大量出现的四维矩阵四维向量到底是为啥了。

但是到现在,我们还没提到标题中的齐次空间

为了不当标题党,这里要总结一下。

我们发现每次需要解决位移问题的时候,我们都需要将原始向量,扩充一个维度,但是这个维度的值,永远是1

我们拿一维向量为例:

x 扩充 变成 (x, 1)。

我们知道,所有的x形成了一个一维空间。

那么所有的(x, 1)形成的一个空间就叫齐次空间。他是二维空间的内的一个子空间。

对于这个空间,没什么需要特殊注意的地方,甚至连齐次空间这个名字都不大用记住,因为记住他也没啥用。

gl_Positionw 分量

由于我们真正的坐标是三维的,所以在真正使用的时候,扩充到齐次空间的时候,变成了四个分量。

所以在一些中间运算过程中,一些向量是四维的是很正常的。

但是上面我们说了,不管怎么运算,这个被扩充的一个维度的值,永远是1

如果这个扩充的值不是1, 那么会出现什么结果呢?

WebGL 里, 如果 gl_Position.w != 1, 那么最终得到的xyz就会有点变化。

怎么变的:

  • x = x / w
  • y = y / w
  • z = z / w

我们可以看出,全都除以了 w

那这个效果,是WebGL自动进行的,你可以试试,在vertex shader里,将 w 慢慢变大,看看,是不是你画的图像会慢慢缩小。

那么这个效果到底有啥用呢,难道就是进行一个缩放?

这个本文不提,后面的文章里再说吧。

三维向量的拉伸和位移矩阵式(参考对照)

[Sx00Dx0Sy0Dy00SzDz0001]\begin{bmatrix} S_x & 0 & 0 & D_x \\ 0 & S_y & 0 & D_y \\ 0 & 0 & S_z & D_z \\0 & 0 & 0 & 1 \end{bmatrix} * (xyz1)\left(\begin{array}{cc} x \\ y \\ z \\ 1 \end{array}\right) = (Sxx+DxSyy+DySzz+Dz1)\left(\begin{array}{cc} S_x * x + D_x \\ S_y * y + D_y \\ S_z * z + D_z\\1\end{array}\right)