https://zhuanlan.zhihu.com/p/131145847
本文已由原作者授权,不得擅自二次转载
UGM
Ubpa Graphics Mathematics,Ubpa 图形数学库
名字取自 GLM,OpenGL Mathematics
该处“图形”概念有点大,主要面向渲染,不同于“图形学”
特点
着重“正确”的代数概念(环、线性、度量、赋范、内积、欧式空间、仿射空间等)
面向对象(所有方法都是类方法)
只有头文件 head-only
高性能:SIMD 加速,各算法最优化
利用单继承化优化代码结构(不使用恶心的宏)
提供 natvis 优化 debug 信息
...
0. 前言
项目地址
https://github.com/Ubpa/UGM
直接把 glm,Eigen 等扔掉
目录
简介
示例
安装
设计思路
接口
SIMD
Natvis
1. 简介
UGM 是着重于代数概念的数学库,区分点、向量、法向、颜色等,从而尽可能地避免了错误的计算。
常用的数学库(如 Eigen,glm)只提供 vec
类,并使其能做各种运算(如 +-*/
等),但从代数方面考虑,这并不合理。
示例
- 点与点之间不能相加减
- 颜色与点之间没有关系
- 变换矩阵(4x4)与法向的乘积不同于一般向量
- 在考虑齐次坐标时变换矩阵(4x4)与向量和点的乘积不同
- ...
我们通过提供 point
,vec
,normal
,rgb
等来区分不同的代数概念,并仅让他们支持合理的操作,这样就能在编译期就发现各种代数方面的错误,另外还能减轻心智负担(根据类型执行不同的操作)。
此外,我们还通过单继承的技术实现了极佳的代码编写,特点如下
复用函数实现(不同于 c++20 的
concept
或者接口,他们只是对类支持的“操作”进行了约束)空基类优化
...
2. 示例
3. 安装
3.1 环境
Win10
Git
VS 2019
CMake-GUI 3.16.3 及以上
支持 SIMD 扩展指令集 SSE 4.1
其他环境自行测试,如成功请告知
3.2 步骤
详细参考项目 README.md
3.3 使用
4. 设计思路
为了更好地使用该数学库,我们很有必要先了解下该库的设计思路。
4.1 代数概念
该库着重于“正确”的代数概念,使用者可能对这方面并不了解,但只要知道基础的线性代数知识即可,然后遇到不了解的查查 wiki 即可。
下边我简单介绍下该库涉及的主要代数概念。
加法 IAdd:相同元素之间的运算,具有交换性(a+b==b+a)和可逆性(a+(-a)=0)
乘法 IMul:相同元素之间的运算,具有可逆性(a*(1/a)=1),不一定具备交换性(a*b==b*a)。
数乘 IScalarMul:类与标量(如 float)之间的运算,具有交换性。
线性 ILinear:加法 + 数乘,该空间中的元素称为向量
环 IRing:加法 + 乘法
度量 IMetric:也叫距离
赋范 INorm:满足非负齐次三角不等式的向量 => 标量的函数,一般也叫大小 / 长度,可自然诱导出度量(distance(a,b) == (a-b).norm())
内积 IInnerProduct:线性空间中的正定非退化共轭双线性的向量 x 向量 => 标量的函数,可自然诱导出范数(sqrt(dot(x, x)) == norm)
仿射空间 IAffine:具有位置概念的空间,该空间中的元素称为点,会对应一个线性空间,两空间之间的元素有关联,如 point-point => vector,point+vector => point
4.2 底层存储类型
数组 IArray:有序的元素序列,这将是我们各种类的基类,一般是 std::array,其中 T 可以是 float,int,也可以是 point,vec
矩阵IMatrix:一维数组的数组
由于底层存储类型不同,上述代数概念的具体实现有所不同(抽象 => 具体),并引申出新的代数概念
4.2.1 数组
底层存储类型为数组时,则可引申出如下代数概念
欧式(向量)空间 IEuclideanV:线性空间 + 内积(dot(a,b) == a.x*b.x + a.y*b.y + a.z*b.z)
欧式仿射空间 IEuclideanA:欧式(向量)空间对应的仿射空间
逐元素乘 IArrayHadamardProduct:a*b=(a.x*b.x, a.y*b.y, a.z*b.z)
上述各种概念在具体为数组时会有对应的实现,如
4.2.2 矩阵
由于该库用于离线渲染,实时渲染,游戏等,基本只要用 float4,因此也只需 3x3 和 4x4 的矩阵,因此该库也限制为只支持 3x3 和 4x4 的矩阵(并特化矩阵的乘法与逆的实现,如循环展开,simd 加速,以提高性能)。
大型矩阵的支持一般需要用线性代数库,如 Eigen 等。
底层通过一维数组的数组来实现,右乘,列优先,同于 OpenGL 与 DX(右乘+列优先的方案十分适合于 SIMD,同理左乘+行优先也如此)。
4.3 类
通过组合多个代数概念并加上具体类型支持的操作,可以轻松得到各种各样的代数类。他们满足不同的操作,极大地帮助使用者避免错误。
目前各种组合(主要部分)如下
图中含有的类有
向量 vec
法向 normal:本质是二重向量 bivector(wiki, stackoverflow)
点 point
四元数 quat:限制为单位四元数,用于表示旋转
矩阵 mat
变换 transform:可表示仿射变换(平移,旋转,缩放)和射影变换(正交投影,透视投影)
颜色 rgb
缩放 scale
该库还含有类
值 val:加法 + 数乘 + 逐元素乘
欧拉角 euler:roll -> pitch -> yaw,同于 Unity3D
透明颜色 rgba
表面向量 svec:切空间的单位向量,上方向为 z 轴
齐次向量 hvec
包围盒 bbox:axis-aligned bounding box (AABB)
三角形 triangle
直线 line
射线 ray
5. 接口
类由多个代数概念组合而成,所以关键在于把握代数概念的接口,各代数概念位于 :
https://github.com/Ubpa/UGM/tree/master/include/UGM/Interfaces/
所有接口都是类方法,方便使用,大部分情况下都可以利用 IDE 的代码提示功能(如 VS2019 的 intellisense)来查询接口。
此外还提供了图形学常见函数 / 算法,如相交(位于 line
,ray
内)、采样、材质 等。
6. SIMD
该库支持 SIMD,只要求支持 SSE 指令,使用了 xsimd 作为 SSE 指令的装饰类(wrapper),但大部分情况都直接使用 SSE 指令,并通过检阅汇编代码的指令数来优化与判断加速比。
加速的类为 float4
,包括 vecf4
,pointf4
等。
注意 float3
并没有 SIMD 加速,这是为了保持 sizeof(float3)==3*sizeof(float)
,部分数学库通过使用含 __m128
的 float3
来实现 SIMD 加速,但这样 sizeof(float3)==4*sizeof(float)
。目前可以通过显式将 float3
转成 float4
来获取加速效果。
加速部分包括
+-*/ ...
min/max/min_component/max_component/abs/sin/cos/...
transform * float4/bbox/transform
transform inverse
ray 和 sphere/triangle/bbox 的相交
float3 的 dot/cross(需要扩展成 float4 并使用 float4::dot3 和 float4::cross3)
7. Natvis
泛型编程在 debug 时会引入大量的单继承,该库使用了单继承化技术,单继承深度也很大,导致在 IDE 中查看类成员变量会很麻烦。
我们可使用 VS2019 的 natvis 功能来实现定制的视图
当使用 find_package(UGM REQUIRED)
时,会自动给解决方案添加一个项目,包含 UGM_.natvis
,从而使得其他项目都可以支持 natvis(VS2019 支持多种方式引入 natvis,但这是目前我能想到的最合适的方式)。
感谢大家的阅读,最后再重复贴一下项目地址
https://github.com/Ubpa/UGM