WebGL坐标系基础(二)

希沃ENOW大前端html

公司官网:CVTE(广州视源股份)前端

团队:CVTE旗下将来教育希沃软件平台中心enow团队git

本文做者: github

前言

本文假设读者已经对向量、矩阵有必定的了解。对此不了解的读者不妨先看一下这篇文章:《乘风破浪的WebGL系列-仿射变换数学基础》web

在上一篇文章:《WebGL 坐标系基础(一)》中,咱们介绍了WebGL 中常见的几种坐标系以及他们之间的关系。本期将更加“硬核”一些,从数学的角度,推导上期讲到的各个变换矩阵。segmentfault

基础概念

列向量

在下面的推导中,咱们统一使用列向量来表示一个坐标,所谓列向量就是一个N*1 矩阵。例如坐标(x, y, z) 能够表示为: Screen_Shot_2021-03-06_at_23.26.41.pngmarkdown

为何要用这样的 N1 矩阵来表示坐标?其实就是由于咱们是经过一个个的矩阵来表示各类各样的变换,而把坐标变成 N1 矩阵,就能让其与变换矩阵运算,从而变换咱们的坐标。工具

齐次坐标

上面用 N*1 矩阵表示的坐标,咱们称之为 N 维坐标,若是咱们再加一维,变成 N+1 维,就称之为齐次坐标,对应的 N+1 维矩阵就是齐次矩阵。oop

为何要引入齐次坐标的概念?常见的说法是经过齐次坐标新增长的维度来区分点与向量,新增维度的值为0则表明向量,不为0则表明点。post

关于为何新增的维度能用于区分点与向量,能够看一下这篇文章:齐次坐标理解

不过我的认为,齐次坐标更加巧妙的一点是可以把平移转换变成矩阵相乘的运算。

下面咱们会知道,旋转、缩放等变换都是经过矩阵乘法进行的,而且变换的组合(例如先缩放再旋转)也是经过变换矩阵相乘求出。可是惟独平移变换是经过矩阵加法进行。假设一个坐标分别往x,y,z 方向平移 Tx,Ty,Tz,那么用矩阵加法来表示就是:

Screen_Shot_2021-03-07_at_00.06.58.png

等式的右边就是咱们须要得到的平移变换以后的坐标,因为常量 Tx,Ty,Tz 的存在,咱们显然没法经过简单的矩阵乘法获得这样的坐标,那么咱们要如何经过乘法获得同样的坐标呢?只须要加一维便可。

Screen Shot 2021-03-09 at 10.05.09.png

此外还记得上一期谈到的份量 w 吗?他其实就是齐次坐标带来的额外的一维,因此齐次坐标的引入也方便咱们模拟透视效果。

总的来讲,齐次坐标的引入具备三个做用:

  1. 区分向量和点
  2. 使平移变换能经过矩阵乘法运算实现
  3. 方便模拟透视效果

基本变换

在齐次坐标下,仿射变换均可以套用如下的形式:

Screen Shot 2021-03-09 at 10.07.26.png 可见每个份量都符合仿射变换的定义。

平移变换

根据平移变换的定义,对一个点(x, y, z)平移,即对其三个份量分别加上一个常数:

Screen_Shot_2021-03-07_at_16.44.29.png 套入上面提到的矩阵模板,即a=1,b=0,c=0,显然平移矩阵能够写成:

Screen_Shot_2021-03-07_at_16.46.23.png

缩放变换

缩放变换意味着一个点的各个份量均为原来的 S 倍,以 x 轴份量缩放 Sx 倍为例:

Screen_Shot_2021-03-07_at_16.50.35.png 套入上面的矩阵模版,即a=Sx,b=0,c=0,Tx=0,显然缩放矩阵能够写成:

Screen_Shot_2021-03-07_at_16.52.52.png

旋转变换

以上两个矩阵的推导都很是直观,相比起来旋转矩阵的推导就略显复杂。 首先咱们定义,这里所说的旋转是指将点P 绕坐标原点逆时针旋转 θ 度。

Screen_Shot_2021-03-07_at_16.59.26.png 为了推导的方便咱们先使用极坐标表示。 在极坐标下,P点的坐标为(r, α)P 旋转 θ 度后的坐标为(r, α + θ)。 再将极坐标转回直角坐标,有:

Screen_Shot_2021-03-07_at_17.07.54.png

P点原来的极坐标与直角坐标的转换关系代入,有:

Screen_Shot_2021-03-07_at_17.10.03.png 上面的推导是二维的,可是咱们能够很容易的将上面的旋转等同于在 xyz 坐标系中,绕坐标轴 z 旋转,由于是绕坐标轴 z 旋转,因此旋转先后点 Pz 份量保持不变,x,y份量的变化与上面的推导结果一致,所以绕 z 轴旋转的结果为:

Screen_Shot_2021-03-07_at_17.19.26.png

代入咱们的矩阵模板,可得绕z 轴旋转的旋转矩阵:

Screen_Shot_2021-03-07_at_17.21.00.png

同理可得绕 x 轴,y 轴的旋转矩阵,这里就不列出来,读者能够本身写一下,答案能够参考:

LearnOpenGL

须要注意的是,更多时候咱们须要绕任意轴旋转,尽管绕任意轴旋转能够经过以上三轴旋转的组合实现,但会出现万向节死锁的问题,一个更好的方式是一步到位,求解出一个绕任意轴旋转的矩阵,可是这个矩阵会比较复杂而且也没法完全避免万向节死锁问题。

这样的矩阵我先贴出来,因为并不是本期重点因此略去了推导过程,有兴趣的童鞋能够看一下这篇:三维空间绕任意轴旋转矩阵的推导

Screen_Shot_2021-03-07_at_17.33.09.png 其中 (Rx,Ry, Rz) 表明旋转轴的向量。

要彻底解决该问题须要使用四元数,有兴趣的读者能够找另外的资料学习,(不妨看看这篇四元数与三维旋转),此处就再也不展开了。

模型变换矩阵

简单回顾一下模型变换:用于将模型坐标系转换到世界坐标系的变换,也就是将咱们的小车模型安放在世界坐标系的某一处。

而要实现模型变换,显然一个很好的方式就是借助于矩阵这一强大的数学工具。咱们将模型的某一个顶点的坐标用上文提到的列向量来表示,那么只要将咱们的模型变换矩阵左乘该列向量,就能获得变换以后的顶点坐标。

模型变换是上文提到的三个基本变换组合而成,咱们也知道矩阵乘法不符合交换律,所以基本变换的组合顺序相当重要,具体来讲,模型变换有如下公式: Screen Shot 2021-03-09 at 10.12.37.png 其中 T 是平移变换矩阵,R 是旋转变换矩阵,S是缩放变换矩阵。之因此有这样的顺序,咱们能够从两个角度来理解。

定性理解

咱们在推导旋转矩阵的时候,实际上是约定了顶点是绕坐标原点旋转,若是模型的原点与世界坐标原点重合,那么顶点绕模型原点与绕坐标原点旋转是同样的。然而若是咱们先平移,让两个原点不重合,那么在应用旋转矩阵的时候,顶点依旧绕坐标原点旋转,但咱们通常仍是但愿顶点能绕模型原点旋转,这就不符合咱们的期待。因此须要先进行旋转变换,再平移。缩放变换相似的,一样隐含着以坐标原点为中心的条件,因此也是要先缩放再平移。

至于缩放与旋转的次序,咱们在定义缩放矩阵的时候,是针对当前的坐标定义的各个份量的缩放比例,若是此时通过旋转,坐标已经发生了改变,那么再使用以前的缩放比例就会有问题。而旋转则没有相似的问题,由于他定义的是任何一个点须要绕坐标原点旋转某个角度,这样的定义适用于全部坐标,不存在坐标已经改变致使原定义不适用的问题。

数学理解

这里我举平移与旋转,旋转与缩放做为例子。

假如先平移再旋转

勘误:此处的矩阵相乘应为点乘

Screen_Shot_2021-03-07_at_23.02.09.png 注意变换以后的坐标的后半部分,从结果来看,再也不以原来的方向平移,平移的方向也旋转了。

假如先旋转再缩放

Screen_Shot_2021-03-08_at_22.12.21.png

从变换后的坐标可见,Sx 不只做用于原来的x份量,也做用于原来的 y 份量,Sy 也有相似的状况。同时咱们发现,若是 Sx,Sy,Sz 的值一致,那么旋转与缩放的顺序就再也不重要,结果都同样。

视图变换矩阵

这个矩阵就是将点从世界坐标系转换到观察(相机)坐标系的矩阵。在上一期咱们知道,咱们会在世界坐标系中放置一个相机(观察点),而且相机有方向。这就很像咱们在模型变换里面将物体放置在世界坐标系中作过的事情。咱们先经过平移变换将相机放在某一处,再经过旋转变换将相机朝向某一个方向。设 P1 是顶点在观察坐标系中的坐标,P0 是在世界坐标系的坐标,那么就有: Screen_Shot_2021-03-08_at_19.59.19.png

同时,根据矩阵两条的性质:

  1. 矩阵左乘逆矩阵等于单位矩阵
  2. 单位矩阵左乘矩阵等于原矩阵

咱们很容易就有如下的推导过程:Screen_Shot_2021-03-08_at_20.13.32.png

所以只须要求出相机的平移矩阵以及旋转矩阵的逆矩阵,就能组合起视图变换矩阵,将顶点在世界坐标系中的坐标 P0,转换成在相机坐标系中的坐标 P1

已知相机的坐标是(ex, ey, ez),那么根据前文的知识,很容易就能得出平移矩阵。而平移矩阵的逆矩阵也很容易就能看出来,这里就不详细推导了。

Screen_Shot_2021-03-08_at_20.25.48.png

下面咱们来推导如下旋转矩阵。

首先定义相机坐标系的三个基向量 Ux, Uy, Uz,其中Ux在世界坐标系的三个轴x, y, z的份量分别是:Uxx, Uxy, UxzUyUz同理。这样,将一个顶点在相机坐标系中的坐标 P1 转成世界坐标系的坐标P0的变换矩阵 R 就能用Ux,Uy, Uz的份量表示出来:

Screen_Shot_2021-03-08_at_22.20.20.png

关于如何得出这个矩阵,能够回忆一下旋转矩阵,但这一次咱们不是旋转顶点,而是先旋转坐标系,再将顶点从在旋转后坐标系中的坐标,转成在旋转前坐标系的坐标。简单起见,可先从二维旋转入手。

咱们须要的是这个变换矩阵的逆矩阵,而因为咱们旋转以后的相机坐标系的三轴是相互垂直的,因此矩阵是正交矩阵,正交矩阵的逆矩阵等于矩阵的转置矩阵。因而逆矩阵 R-1 即为:

Screen_Shot_2021-03-08_at_22.23.57.png

将矩阵代入以前的结果

Screen_Shot_2021-03-08_at_22.31.17.png

最后的问题就是,咱们要如何求出这几个基向量在世界坐标系的份量。

首先咱们会经过两个点(均为世界坐标系的点),以及相机头顶的方向向量来定义相机的位置与方向:

  1. 相机的位置点 e (ex, ey, ez)
  2. 相机看向的点 T (Tx, Ty, Tz)
  3. 摄像机上方方向向量 u (ux, uy, uz)

很符合咱们的现实世界是否是?

接着咱们会定义观察的方向为 Uz,那么根据向量加减法, Uz 显然等于:

Screen_Shot_2021-03-08_at_22.46.04.png Screen_Shot_2021-03-08_at_22.48.40.png 第二个等式表明须要归一化处理。 有了 Uz 以后再将其与上方方向向量 u 进行叉乘再归一化,获得 Ux。同理,再利用 Uz 与 Ux 叉乘并归一化,获得 Uy。 至此整个视图变换矩阵就获得了。

投影变换矩阵

上期提到,投影分正交投影和透视投影,下面会分别推导。

正交投影

在正交投影中,咱们使用一个长方体来定义可视范围:

1f5f8ecaca6348fc9d931249874d0489_tplv-k3u1fbpfcp-zoom-1.png

咱们须要把模型中的一个点 P, 投影到近平面中,成为坐标 P'。P' (x', y', z') 坐标具备如下特色:

  • x' 在 -1, 1之间
  • y' 在 -1, 1之间
  • z' 在 -1, 1之间

这样意味着通过变换以后一步到位来到来规范化的设备坐标系(NDC)。

根据这一特色,咱们以 x 为例,推导 x 与 x' 的关系: Screen_Shot_2021-03-09_at_00.26.53.png

其中 l 为左平面位置,r 为右平面位置。运用相似的方式也能得出 y' 与 y,z' 与 z 的关系。代入矩阵中便可获得正交变换矩阵:

Screen_Shot_2021-03-09_at_00.31.32.png

其中t、b、n、f 分别对应上平面,下平面,近平面,远平面的位置。

透视投影

8cb3e9827d2a4cb38432cfe7b0ab814b_tplv-k3u1fbpfcp-zoom-1.jpg

相比起来透视投影会复杂一些。因为透视投影的可视范围是一个锥体,所以咱们会须要用到类似三角形的知识,回忆一下:

Screen_Shot_2021-03-09_at_00.34.50.png

假设有以上的坐标关系,那么在n上的那个交点 p' 的 x 坐标 x1 能够这么求出:

Screen_Shot_2021-03-09_at_00.36.33.png

一样的,x', y', z' 均在 -1 到 1之间。接下来咱们先推导x,y的值。以 x 为例,近平面上的,x1通过归一化后的值为:

Screen_Shot_2021-03-09_at_01.03.02.png

上面的公式咱们在推导正交变换的时候已经见过。可是与正交不一样,x并不等于x1,可是咱们能够经过类似三角形原理求出:

Screen_Shot_2021-03-09_at_01.07.58.png 其中n是近平面的位置。这里的负号是由于投影坐标系和相机坐标系的 z 轴相反。y' 的状况同理。 所以咱们有:

Screen_Shot_2021-03-09_at_01.16.24.png 接下来咱们求解 z 与 z‘ 的关系。已知当 z 为近平面时,裁剪空间中的 z 为 -1,z 为远平面时则为1。因而有:

Screen_Shot_2021-03-09_at_01.31.27.png

Screen_Shot_2021-03-09_at_01.32.12.png 因此:

Screen_Shot_2021-03-09_at_01.34.06.png

从上面的关系中能够看到,x' 和 y' 都带来一个与 z 相关的常量,回忆一下上期降到的 w 份量,那个常量即为 w 份量,可见 z 越大,离近平面越远,越小。

结合起来,咱们的矩阵能够写成:

Screen_Shot_2021-03-09_at_01.40.47.png

从矩阵也能够看出,通过变换以后原来为1的齐次坐标会变成与z相关的-z。

总结

本期咱们推导了常常用到的各类矩阵,本人也是现学现卖,不免会有一些错漏,还请你们在评论区指出。

参考资料

  1. 掘金小册《WebGL 入门与实践》
  2. LearnOpenGL
  3. 交互式计算机图形学——基于WEBGL的自顶向下方法(第7版)
  4. 知乎:为何directX里表示三维坐标要建一个4*4的矩阵?
  5. OpenGL矩阵变换的数学推导
  6. webgl 开发第一道坎:矩阵与坐标变换
  7. 计算机图形学视图矩阵推导过程
相关文章
相关标签/搜索