拥有一个通用数学库是一回事,但是对于 glm 数学库,它的设计考虑了 GLSL 的约定。它是这样设置的,因此当您在 GLSL 中编写着色器时,您已经熟悉 glm 数学库的使用,并且工作流程更加顺畅。
当在硬件上使用着色器和渲染而不是 CPU 时,所有数据集都存储在视频卡的内存上。矩阵不仅用于进行基本的数学变换,例如平移、旋转、缩放、倾斜等,它们还可以用于做其他事情。当您在现代视频卡上使用着色器进行编程时,由于它们的架构,它们在并行进程中工作得非常好。
由于 mat4x4 是一种模板类型,有什么可以阻止您存储指向函数调用或函数指针的指针?
您可以说一个 4x4 矩阵,其中每个索引中保存了一个函数指针,并且该矩阵充当对函数指针的引用,以在多个线程中并行执行相同的工作。然后有一个带有另一组函数指针的第二个 4x4 矩阵将以相同的方式运行。重载的 operator+() 在这里有什么用处?
使用 mat4x4 A + mat4x4 B 并不等于您期望的 mat4x4 C。不是在矩阵中添加每个元素的数据来为您提供一个新矩阵,而是您将定义的重载运算符将允许您在 A 离开范围后立即调用对 B 中的函数指针的调用。允许您在特定数据集上连接多个函数调用,同时在多个线程中并行工作。
假设您有一个大小为 wxh 的纹理,每个像素值代表 3D 地形上照明的正常值。然后,我们要对每个像素数据执行一次操作 -->(正常值),然后以确切的顺序依次执行另一次操作。假设这个函数指针 foo() 对每个法线执行一组操作,然后函数指针 goo() 对这些法线执行不同的操作,以提供所需的结果。
所以矩阵 A 有 16 个单元格,每个单元格代表对指向 foo() 的函数指针的引用,矩阵 B 对 goo() 做同样的事情。现在,对于引擎中的这个实现,设计者必须编写自己的重载 operator+() 函数,以便它在纹理 T(普通数据)上的并行线程中调用 foo() 调用,然后立即调用在 T 上并行调用 goo() 的函数,可能会返回一个向量或 char[x * h] 以及着色器使用的结果。
这将有 16 个线程,每个线程处理相同数据集上的相同操作。对于 T(正常值)中的每个像素条目,即 W x H;对 foo 和 goo 的每次调用都适用于 (W x H) / 16,这是数据类型的一个子集。由于这些操作是在逐个像素的基础上完成的,因此这比运行双 for 循环并调用一个方法、返回并执行另一个双 for 循环或执行双 for 循环并在数据集。
这个 Mat4x4 的返回类型不会像您期望的那样是一个翻译,而是一个使用两个函数调用并行处理的数据集。现在在这个例子中,重载的运算符必须由程序员编写,因为它没有完全包含在库中。
大多数库被设计为具有您所期望的大多数常见功能的松散通用和通用的。没有图书馆是完美的,有些图书馆比其他图书馆更好。每个图书馆都有自己的长处和短处。
在上面的示例中,您可能无法直接从提供的 mat4x4 类执行此操作,但在为此类引擎创建源代码时,您可以从 glm::mat4x4 类继承并创建自己的。它可能看起来像这样
namespace glm {
template<typename T>
class mat4x4 {
};
} // namespace
// In your class definition you might have
#include <glm/mat4x4.hpp>
template<typename T, typename FuncPtr>
class myMat4x4 : public glm::mat4x4 {
private:
std::vector<T> m_vData;
FuncPtr m_funcPtr;
public:
myMat4x4();
explicit myMat4x4( std::vector<T>& vData ); // std::vector<T> has the data
// from texture tex1 that was previously stored.
const std::vector<T>& operator+( const myMat4x4<T,FuncPtr>& rhs );
};
通过模板特化,第二个类型名可以接受的唯一类型是指向函数调用的指针。在这种情况下,传入的类型 T 还必须与 FuncPtr 所指向的方法所接受的类型相匹配。在您的构造函数中,您必须将保存的 FuncPtr 设置为 glm::mat4x4 的每个元素,但在派生类中。您的重载运算符可能类似于:
template<typename T, typename FuncPtr>
std::vector<T>& myMat4x4<T,FuncPtr>::operator+( const myMat4x4<T,FuncPtr>& rhs ) {
// Invoke this->m_funcPtr on this->m_vData save results back into
// this->m_vData where each this[m][n] element is called on m_vData[i]
// meanining this[0][0] FuncPtr works on m_vData[0] T, this[0][1] FuncPtr works on m_vData[1] ...
// until this[m][n] works on m_vData[last] then make sure m_vData is updated correctly and valid.
// Next would be to invoke rhs.m_funcPtr on this->m_vData in the same fashion and save data into this->m_vData.
// Here the rhs myMat4x4 doesn't have anything saved into its (rhs)m_vData since it used the default constructor.
// But it does have the pointer to goo() saved in rhs.m_funcPtr
// Check validity of data set if everything is okay and no errors or exceptions, now we can just return this->m_vData
}
如您所见,在这个 Matrix 类的示例中,它不遵循矩阵运算的数学规则来进行转换。但它是一种在数据集上构建并行多线程操作的方法,这有利于 GPU 的结构。