在前面章节的学习中,大家已经接触过矩阵、向量等数学概念了。那么除了矩阵和向量, 关于 3D 开发我们还需要哪些数学知识?这些数学知识又能够帮助我们解决什么问题呢?
本节为大家做一个概括,接下来的章节我们就深入学习这些数学知识。
你能学到什么?
- 明确图形学开发与数学基础的密切关系。
- 学好图形学开发需要掌握哪些数学知识。
- 这些数学知识能够帮助我们解决什么问题。
那么,3D 应用涵盖哪些数学知识呢?
一、 坐标系
坐标系是一个重中之重的概念,我们在开发 3D 应用的过程中,经常会涉及到坐标系之间的转换,最经典的坐标转换流水线就涉及到六种坐标系,由此可见坐标系的重要性。
二、 点
点在 3D 领域通常指顶点坐标,3D 世界由很多模型组成,模型又由很多面组成,而面又由很多点组成。因此,点是组成 3D 世界的基本元素。WebGL 渲染的过程,就是将组成模型的顶点传递给 GPU,GPU 按照指定图元装配并插值这些顶点数据,然后通过片元着色器对像素进行着色,最终渲染成 3D 模型。由此可见顶点是模型的起始数据,重要性可见一斑。
三、向量
向量是另一个非常重要的知识点。物理和数学中的向量代表既有大小又有方向的量。
通常我们使用向量容器来表示数学中的点和向量。
比如我们经常会使用 3 维向量来表示顶点坐标,3 维向量还可以理解为一个 3 x 1 阶矩阵,将 3 x 3 阶变换矩阵左乘顶点坐标(3 * 1 阶矩阵),按照矩阵的运算法则,m行n列 矩阵左乘 n 行 o 列矩阵可以得到一个 m 行 o 列矩阵,所以我们矩阵和向量相乘结果是一个新的 3 * 1 阶 矩阵,也就是 3 维向量,它表示变换后的顶点坐标。
这从数学角度解释了
变换矩阵左乘顶点坐标向量的结果是一个新的顶点坐标的原因。
通常在对模型执行坐标转换的时候,变换矩阵左乘顶点坐标代表对这个顶点执行坐标转换,这是顶点着色器中对顶点执行的最常用的操作。
又比如在为模型表面计算光照强度的时候,我们会用到法向量和光线入射向量,利用他们之间的点积计算光照强度。
向量的重要性可见一斑。
向量计算
3D 编程中经常使用的向量运算有以下几种。
- 点积
- 叉积
- 归一化向量
- 向量长度
- 两个向量之间的距离
- 向量基本运算
- 向量相加
- 向量相减
- 向量相乘
- 向量相除
后续章节我们会详细学习它们的运算规律,并提供 JavaScript 版本的分析与实现。
四、矩阵
3D 数学中最重要的一个知识点我想就是矩阵了,矩阵能够帮助我们以一种非常简单的方式解决大量运算的问题。
比如坐标转换。
那有同学会问了,坐标转换无非就是对顶点坐标的计算,我用普通的加减乘除和三角函数运算也能做到呀。
说的没错,那大家有没有想过,如果要对一个坐标执行多种变换,大家需要写多少计算逻辑?
我想大家会崩溃的。
矩阵就是为了帮助我们解决这个问题,一个矩阵代表一种变换,多个矩阵相乘就代表多个变换。有了矩阵,我们就不用再使用三角函数,加减乘除等繁杂的数学公式来完成坐标转换,仅仅使用一个矩阵就可以代替多种运算步骤。
前面在构建立方体的章节我们已经接触到了矩阵,仅仅让一个旋转矩阵左乘顶点坐标,就实现了立方体的旋转,由此可见矩阵在 3D 数学运算中的重要性。
这就是矩阵的主要作用。但使用矩阵还有一点考虑,就是在 GPU 中执行矩阵运算性能会高很多,这得益于 GPU 并行计算的优势,在绘制大量顶点的场景下会有很大的性能提升。
矩阵运算
3D 编程中经常用到的矩阵运算有以下几种。
- 单位化矩阵
- 矩阵基本运算
- 矩阵相加。
- 矩阵相减。
- 矩阵相乘。
- 转置矩阵
- 逆矩阵
- 旋转矩阵
- 绕 X 轴旋转。
- 绕 Y 轴旋转。
- 绕 Z 轴旋转。
- 绕轴向量旋转。
- 根据欧拉角推导旋转矩阵。
- 根据四元数推导旋转矩阵。
- 旋转矩阵与欧拉角、四元数之间的变换。
- 平移
- 沿 X 轴平移。
- 沿 Y 轴平移。
- 沿 Z 轴平移。
- 缩放
- 沿 X 轴缩放。
- 沿 Y 轴缩放。
- 沿 Z 轴缩放。
- 观察矩阵
- 正射投影矩阵
- 透视投影矩阵
如果我们不理解矩阵和向量的运算规则,我们就写不出数学库,自然就没办法开发 3D 应用。接下来我们就要着重讲解这方面的内容,另外我还会详细推导变换矩阵的 JavaScript 算法实现,封装出一个我们自己的数学库。
五、 常用数学函数
3D 开发时我们经常需要使用一些数学函数完成一些数值运算,常用的有如下几种:
sin (θ)
- 指定角度 θ 的正弦值。
asin (value)
- 指定正弦值 value 对应的角度值。
cos (θ)
- 指定角度 θ 的余弦值。
acos (value)
- 指定余弦值 value 对应的角度值。
atan (value)
- 指定正切值 value 对应的角度。
tan (θ)
- 求 θ 的正切值。
abs (value)
- 取 value 的绝对值。
max (value1, value2)
- 取 value1 和 value2 之间的最大值。
min (value1, value2)
- 取 value1 和 value2 之间的最小值。
clamp (value, min, max)
- 如果 value 小于 min,返回 min,如果 value 大于 max ,返回 max,如果 value 介于 min 和 max 之间,返回 value。
pow (x, n)
- 求 x 的 n 次幂。
。。。
还有一些函数就不一一介绍了,在需要的时候大家可以查一下。
六、其它高等函数
上面的数学知识,足以支撑我们完成大部分 3D 效果了,但在做一些曲线相关应用的时候,我们还会用到一些曲线公式来求坐标,如正弦、余弦、贝塞尔公式等。本小册不对它们做过多介绍,感兴趣的话大家可以去看看 ThreeJS 对它们的实现。
回顾
以上就是我们在 3D 编程中将会用到的数学知识,业界一般将这些数学算法抽象出来以方便调用,比如 Threejs 就有单独的 matrix 、vector、euler等数学类。JavaScript没有提供向量和矩阵的表示和运算,所以我们需要封装。但是 GLSL 内置了大部分数学运算。
有的同学会问,既然 GLSL 内置了这些计算,Threejs 为什么还要封装呢?
这是因为有些运算没有必要放在 GLSL 中,如果放在 GLSL 中反而会影响性能。比如全局变换矩阵,如果放在 GLSL 中计算,那么每个顶点变换前,都要重新计算出矩阵。模型的顶点都是很多的,这会造成大量重复运算。尽管 GPU 的运算能力很强,但那么多的无用运算还是会造成性能问题。
下一节,我们开始详细介绍这些数学知识。