Java软光栅渲染器-3D数学库

目标

把顶点从三维世界空间变换至二维屏幕空间,绘制顶点(如银河星系数据),操控摄像机旋转模型。

在渲染3D场景之前,先得做一些数学工作。上面提到的“顶点”、“空间变换”、“旋转”等术语,都需要依赖3D数学库来实现。

至少要包括:

  • 三维向量(Vector3)
  • 四元数(Quaternion)
  • 矩阵(Matrix)
  • 空间变换(Transform)

这一阶段的主要目标就是实现这些数学库。

在实现的过程中,我的主要参考资料是 jMonkeyEngine 数学库的源代码,以及《3D数学技术:图形与游戏开发》这本书。

实现

约定

在做数学计算之前,应该先约定坐标系和计算规则:

  • 使用OpenGL的右手坐标系
  • 使用列向量和列矩阵进行计算

行向量和列向量的区别,可以参考这篇文章:矩阵和向量的乘法顺序

除此之外,还有另外一个条件:

  • 尽量使用float精度进行运算。

这么做的原因,是想和OpenGL保持一致。同时,我在学习jMonkeyEngine时已经习惯了这种规则。

数学库是较为基础、也较为复杂的内容,我将用几章来分别实现。网上有很多关于3D数学的资料,如果读者对我下面写的东西不感兴趣,可以直接跳过一这一部分。

三维向量

Vector3f 可以用来表示三维空间中的一个“点”,这时 (x, y, z) 描述了该“点”在自身坐标系统中的相对位置

Vector3f 也可以用来表示方向,看作是从原点 (0, 0, 0) 出发,指向 (x, y, z) 的箭头。这个“箭头”具有长度,也称为向量的模

四元数

四元数包含一个标量分量和一个3D向量分量。经常记标量分量为 w,记向量分量为单一 v 或分开的 x, y ,z。两种记法的分别如下:

  • [w, v]
  • [w, x, y, z]

四元数的主要用来描述三维空间中的旋转,它可以解决“万象锁”的问题。当今3D数学中四元数存在的主要理由是一种称为 slerp 的运算,它是球面线性插值的缩写(Spherical Linear Interpolation)。slerp 运算非常有用,因为它可以在两个四元数之间平滑插值。slerp 运算避免了欧拉角插值的所有问题。

矩阵

矩阵用来对向量进行线性变换。它可以用来变换物体,比如旋转、缩放、投影、镜像、仿射;

矩阵也可以用来变换坐标系,比如从模型空间(model space)到世界空间(world space)、从世界空间转换(world space)到眼睛空间(Eye space)。

这两种变换在某种意义上是等价的。

空间变换

在3D程序中,模型一般有三种空间变换:

  • 缩放,用一个三维向量来表示(scale)
  • 旋转,用一个四元数或3*3矩阵来表示(rotation)
  • 平移,用一个三维向量来表示(translation)

对于一个三维向量vec来说,原本需要通过上述三个变换,才能得到最终结果。注意这个变换的顺序不能改变,否则可能会变换错误。

// 先缩放,再旋转,再平移
Vector3f dest = position.add(rotation.mult(scale.mult(vec)));

现在可以用一个4*4矩阵来代表这三种变换,一次性完成空间变换。

Matrix4f mat = ...;// 计算矩阵
// 空间变换
Vector3f dest = mat.mult(vec);

一般在开发中,我们会定义一个Transform类来记录三种空间变换,编程时可以单独计算这三种变换。同时还应该提供一个转换为矩阵的方法,这样在变换物体时就可以一步到位。

总结

建立数学库的基本目标已经达成,下面可以继续做渲染管线了。