Skip to content

在前面章节的学习中,大家已经接触过矩阵向量等数学概念了。那么除了矩阵向量, 关于 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 就有单独的 matrixvectoreuler等数学类。JavaScript没有提供向量和矩阵的表示和运算,所以我们需要封装。但是 GLSL 内置了大部分数学运算。

有的同学会问,既然 GLSL 内置了这些计算,Threejs 为什么还要封装呢?

这是因为有些运算没有必要放在 GLSL 中,如果放在 GLSL 中反而会影响性能。比如全局变换矩阵,如果放在 GLSL 中计算,那么每个顶点变换前,都要重新计算出矩阵。模型的顶点都是很多的,这会造成大量重复运算。尽管 GPU 的运算能力很强,但那么多的无用运算还是会造成性能问题。

下一节,我们开始详细介绍这些数学知识。