矩阵本身看起来不错。请记住,OpenGL 默认使用列优先存储矩阵,因此当您指定它时,该矩阵的元素将按如下顺序排列:
{1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
x, y, z, 1.0f}
如果您想在代码中坚持行优先顺序,您还可以指定 GL_TRUE 作为 glUniformMatrix4fv()' 的第三个参数 (transpose)。
您似乎被困在两个不同级别的 OpenGL 之间。为了说明这一点,让我列出 OpenGL 的一些主要发展阶段。以下不是官方术语,我只是编造的。
-
第一阶段:固定功能管道。渲染纯粹是通过指定状态和发出绘图命令来执行的。状态包括矩阵堆栈、光照和材质属性等。
-
第 2 阶段:具有固定功能属性的可编程管道。着色器用于控制渲染,但仍使用矩阵堆栈等固定功能状态。使用 GLSL 代码中的预定义变量访问此状态。
-
第 3 阶段:完全可编程。不再使用固定函数状态,并且所有对着色器的输入都使用通用属性和制服指定。
将此与 OpenGL 版本相匹配:
- OpenGL 1.x 严格使用 Stage 1。
- OpenGL 2.x 代码通常使用第 2 阶段,尽管它完全向后兼容并且仍然支持第 1 阶段的所有内容。它已经具备第 3 阶段所需的大部分功能。
- OpenGL 3.x 及更高版本仅支持带有核心配置文件的 Stage 3(在 3.2 中引入)。第 1 阶段和第 2 阶段的所有固定功能方面仅在兼容性配置文件中可用。
您的代码片段表明您正在使用第 2 阶段,并且正在尝试移至第 3 阶段。在此过程中,您在两者之间创建了一个有问题的组合。根据你想走的方向,解决方案是不同的。
固定功能状态的使用
只要您在着色器 (gl_ModelViewProjectionMatrix) 中使用固定函数矩阵堆栈中的矩阵,最简单的解决方案就是充分利用它。
所以要翻译,你调用glTranslatef(),它会为你的模型视图矩阵添加一个翻译,然后在GLSL代码中使用gl_ModelViewProjectionMatrix和类似的内置变量来访问。
固定函数和均匀的混合
使用您尝试使用的方法,最合理的解决方案是将附加的平移矩阵与内置gl_ModelViewProjectionMatrix之前的向量相乘:
gl_Position = gl_ModelViewProjectionMatrix * transformMatrix * gl_Vertex;
您需要注意,这会在模型视图转换中在所有其他模型转换之前应用附加转换。假设您还使用固定函数调用指定了旋转:
glRotatef(...);
这种旋转是模型视图矩阵的一部分,现在将在您添加的平移之后应用。因此,您将旋转翻译后的坐标,这可能不是您想要的。
使用这种方法,没有好方法可以在其他模型转换后应用翻译。你基本上需要的是这样的:
gl_ProjectionMatrix * gl_ViewMatrix * transformMatrix * gl_ModelMatrix * gl_Vertex;
但由于 View 和 Model 矩阵在固定函数状态下组合,这是不可能的。
无固定功能状态
为了充分的灵活性和与当前核心配置文件的兼容性,您需要停止使用固定的函数状态,以及相应的 GLSL 内置变量,如 gl_ModelViewProjectionMatrix。为此,您需要在着色器代码中将所有需要的矩阵指定为统一变量。这使您可以完全控制所使用的矩阵,以及如何在 GLSL 代码中应用它们。
例如,如果您只需要翻译,则无需使用完整矩阵。您可以使用vec3 作为制服,并将其添加到位置向量中。
完全可编程方法的主要缺点是您不能再使用glRotatef()、gluLookAt 和gluPerspective() 等便利功能。有提供替换的开源库,如果您愿意,也很容易自己编写类似的函数。您也不再有矩阵堆栈,因此如果您依赖于转换层次结构,则需要在自己的代码中添加更多逻辑。