如何使用着色器和矩阵进行翻译?
How to translate using shaders and matrices?
我正在使用 LWJGL 和着色器尝试转换我在着色器程序中绘制的所有内容。
我正在使用 4 x 4 矩阵
1,0,0,x,
0,1,0,y,
0,0,1,z,
0,0,0,1
其中 x、y、z 是我用来翻译的值。然后我用
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex * transformMatrix
其中 transformMatrix 是我从上面的矩阵中得到的统一 mat4。
它可以很好地平移 x 和 y,但不能在 z 轴(向前和向后)上平移。
是矩阵错误还是着色器方法错误? (或两者)
如何解决这个问题,使其翻译成 glTranslatef
?谢谢
矩阵本身看起来没问题。请记住,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
指定为 [=15= 的第三个参数 (transpose) ]'.
您似乎卡在了两个不同级别的 OpenGL 之间。为了说明这一点,让我对 OpenGL 的一些主要发展阶段进行编号。以下不是官方术语,我自己编的。
- 第 1 阶段:固定功能流水线。渲染纯粹是通过指定状态和发出绘制命令来执行的。状态包括矩阵堆栈、光照和material属性等
- 第 2 阶段:具有固定功能属性的可编程流水线。着色器用于控制渲染,但仍使用矩阵堆栈等固定功能状态。使用 GLSL 代码中的预定义变量访问此状态。
- 阶段 3:完全可编程。不再使用固定功能状态,所有着色器的输入都使用通用属性和制服指定。
与 OpenGL 版本匹配:
- OpenGL 1.x 严格使用阶段 1。
- OpenGL 2.x 代码通常使用第 2 阶段,尽管它完全向后兼容并且仍然支持第 1 阶段的所有内容。它已经包含第 3 阶段所需的大部分内容。
- OpenGL 3.x 及更高版本仅支持带有 Core Profile 的 Stage 3(在 3.2 中引入)。第 1 阶段和第 2 阶段的所有固定功能方面仅在兼容性配置文件中可用。
您的代码片段表明您正在使用第 2 阶段,并正在尝试转移到第 3 阶段。在此过程中,您在两者之间创建了一个有问题的组合。根据你想去的方向,解决方案是不同的。
固定功能状态的使用
只要您在着色器 (gl_ModelViewProjectionMatrix
) 中使用固定函数矩阵堆栈中的矩阵,最简单的解决方案就是完全使用它。
因此,要进行翻译,您可以调用 glTranslatef()
,它会将翻译添加到您的模型视图矩阵,然后在 GLSL 代码中使用 gl_ModelViewProjectionMatrix
和类似的内置变量对其进行访问。
固定函数和统一函数的混合体
对于您尝试使用的方法,最合理的解决方案是在内置 gl_ModelViewProjectionMatrix
:
之前将额外的平移矩阵与向量相乘
gl_Position = gl_ModelViewProjectionMatrix * transformMatrix * gl_Vertex;
您需要注意,这会应用额外的转换 before 您在模型-视图转换中可能拥有的所有其他模型转换。假设您还使用固定函数调用指定了旋转:
glRotatef(...);
此旋转是模型视图矩阵的一部分,现在将在您添加的翻译之后应用。所以你会旋转翻译后的坐标,这可能不是你想要的。
使用这种方法,无法在其他模型转换后应用转换。你基本上需要的是:
gl_ProjectionMatrix * gl_ViewMatrix * transformMatrix * gl_ModelMatrix * gl_Vertex;
但是由于View矩阵和Model矩阵是在固定函数状态下组合的,所以这是不可能的。
无固定功能状态
为了充分的灵活性,以及与当前 Core Profile 的兼容性,您需要停止使用固定功能状态,以及相应的 GLSL 内置变量,如 gl_ModelViewProjectionMatrix
。为此,您需要在着色器代码中将所有需要的矩阵指定为统一变量。这使您可以完全控制使用的矩阵以及如何在 GLSL 代码中应用它们。
例如,如果您只需要翻译,则无需使用完整矩阵。您可以使用 vec3
作为制服,只需将其添加到位置向量即可。
完全可编程方法的主要缺点是您不能再使用 glRotatef()
、gluLookAt
和 gluPerspective()
等便利函数。有提供替代品的开源库,如果您愿意,自己编写类似的功能也很容易。您也不再有矩阵堆栈,因此如果您依赖转换层次结构,则需要在自己的代码中添加更多逻辑。
我正在使用 LWJGL 和着色器尝试转换我在着色器程序中绘制的所有内容。 我正在使用 4 x 4 矩阵
1,0,0,x,
0,1,0,y,
0,0,1,z,
0,0,0,1
其中 x、y、z 是我用来翻译的值。然后我用
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex * transformMatrix
其中 transformMatrix 是我从上面的矩阵中得到的统一 mat4。 它可以很好地平移 x 和 y,但不能在 z 轴(向前和向后)上平移。
是矩阵错误还是着色器方法错误? (或两者)
如何解决这个问题,使其翻译成 glTranslatef
?谢谢
矩阵本身看起来没问题。请记住,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
指定为 [=15= 的第三个参数 (transpose) ]'.
您似乎卡在了两个不同级别的 OpenGL 之间。为了说明这一点,让我对 OpenGL 的一些主要发展阶段进行编号。以下不是官方术语,我自己编的。
- 第 1 阶段:固定功能流水线。渲染纯粹是通过指定状态和发出绘制命令来执行的。状态包括矩阵堆栈、光照和material属性等
- 第 2 阶段:具有固定功能属性的可编程流水线。着色器用于控制渲染,但仍使用矩阵堆栈等固定功能状态。使用 GLSL 代码中的预定义变量访问此状态。
- 阶段 3:完全可编程。不再使用固定功能状态,所有着色器的输入都使用通用属性和制服指定。
与 OpenGL 版本匹配:
- OpenGL 1.x 严格使用阶段 1。
- OpenGL 2.x 代码通常使用第 2 阶段,尽管它完全向后兼容并且仍然支持第 1 阶段的所有内容。它已经包含第 3 阶段所需的大部分内容。
- OpenGL 3.x 及更高版本仅支持带有 Core Profile 的 Stage 3(在 3.2 中引入)。第 1 阶段和第 2 阶段的所有固定功能方面仅在兼容性配置文件中可用。
您的代码片段表明您正在使用第 2 阶段,并正在尝试转移到第 3 阶段。在此过程中,您在两者之间创建了一个有问题的组合。根据你想去的方向,解决方案是不同的。
固定功能状态的使用
只要您在着色器 (gl_ModelViewProjectionMatrix
) 中使用固定函数矩阵堆栈中的矩阵,最简单的解决方案就是完全使用它。
因此,要进行翻译,您可以调用 glTranslatef()
,它会将翻译添加到您的模型视图矩阵,然后在 GLSL 代码中使用 gl_ModelViewProjectionMatrix
和类似的内置变量对其进行访问。
固定函数和统一函数的混合体
对于您尝试使用的方法,最合理的解决方案是在内置 gl_ModelViewProjectionMatrix
:
gl_Position = gl_ModelViewProjectionMatrix * transformMatrix * gl_Vertex;
您需要注意,这会应用额外的转换 before 您在模型-视图转换中可能拥有的所有其他模型转换。假设您还使用固定函数调用指定了旋转:
glRotatef(...);
此旋转是模型视图矩阵的一部分,现在将在您添加的翻译之后应用。所以你会旋转翻译后的坐标,这可能不是你想要的。
使用这种方法,无法在其他模型转换后应用转换。你基本上需要的是:
gl_ProjectionMatrix * gl_ViewMatrix * transformMatrix * gl_ModelMatrix * gl_Vertex;
但是由于View矩阵和Model矩阵是在固定函数状态下组合的,所以这是不可能的。
无固定功能状态
为了充分的灵活性,以及与当前 Core Profile 的兼容性,您需要停止使用固定功能状态,以及相应的 GLSL 内置变量,如 gl_ModelViewProjectionMatrix
。为此,您需要在着色器代码中将所有需要的矩阵指定为统一变量。这使您可以完全控制使用的矩阵以及如何在 GLSL 代码中应用它们。
例如,如果您只需要翻译,则无需使用完整矩阵。您可以使用 vec3
作为制服,只需将其添加到位置向量即可。
完全可编程方法的主要缺点是您不能再使用 glRotatef()
、gluLookAt
和 gluPerspective()
等便利函数。有提供替代品的开源库,如果您愿意,自己编写类似的功能也很容易。您也不再有矩阵堆栈,因此如果您依赖转换层次结构,则需要在自己的代码中添加更多逻辑。