在 OpenGL 4 中替换 glMatrixMode()?
文章目录
一、说明
OpenGL 版本 3.x和OpenGL 版本 4.x的版本有很大不同,在OpenGL 版本 3.x中使用的glMatrixMode在OpenGL 版本 4.x已经弃用,那么如何实现原glMatrixMode实现功能呢?是使用统一变量完成的。本篇将讨论如何在OpenGL 版本 4.x中替换OpenGL 版本 3.x中glMatrixMode的功能。
二、 glMatrixMode在OpenGL 版本 3.x 中使用
在 OpenGL 版本 3.x 中glMatrixMode(),该函数指定哪个矩阵是当前矩阵,即哪个矩阵堆栈当前是基于参数的所有矩阵操作的目标,选项为 GL_PROJECTION、GL_MODELVIEW 和GL_TEXTURE 。
问题1:glMatrixMode()已在 OpenGL 版本 4.x 中弃用。我试图找到在 4.x 中执行相同效果的等效方法,但没有成功。我怀疑 OpenGL 4 的理念发生了变化,而我没注意到这一点。在 OpenGL 版本 4.x 中如何执行glMatrixMode()等效操作?
问题2:glMatrixMode甚至在 OpenGL 4 到来之前,它就是一个遗留功能。由于它仅适用于即时模式,因此没有得到很好的支持。
问题3:值得注意的是,glMatrixMode 为其指定当前矩阵的其他矩阵堆栈调用也被弃用/删除,因此保留 glMatrixMode 毫无意义。但是,如果 OP 只希望将旧代码与新式驱动程序一起使用,则解决方案很简单:只需创建一个兼容性上下文,这些调用将再次可用。
问题4:您需要学习可编程流水线。它使用着色器而不是预定义的效果(例如,您需要创建自己的基于距离的雾,而不是简单地要求视频卡来执行)。它显然更难使用,但性能上的收益远远超过了负面影响。
三、 在OpenGL 版本 4.x 中如何实现glMatrixMode功能?
对于学习现代 OpenGL 如何在没有的情况下做事非常有帮助:【openGL教程 11 】关于坐标系统
其实,在旧版 OpenGL 实现了一系列转换矩阵,允许您定义如何转换顶点以从世界空间到屏幕空间。而无需 glMatrixMode()函数。
通常,该公式如下所示:
V
e
c
s
c
r
e
e
n
=
M
a
t
p
r
o
j
e
c
t
i
o
n
×
M
a
t
v
i
e
w
×
M
a
t
m
o
d
e
l
×
V
e
c
l
o
c
a
l
Vec_{screen} = Mat_{projection} \times Mat_{view} \times Mat_{model} \times Vec_{local}
Vecscreen=Matprojection×Matview×Matmodel×Veclocal
因此,您会看到传统 OpenGL 的内置矩阵函数允许您在各自的模式处于活动状态时修改这些矩阵。 GL_PROJECTION将是投影和视图矩阵的乘积,并且GL_MODELVIEW是堆栈上的当前模型矩阵。
现代 OpenGL 完全取消了内置矩阵,现在由程序员来实现从世界空间到屏幕空间的转换。这正是顶点着色器的作用!它为程序员提供了更多的控制和自由,一旦你掌握了着色器的基础知识,实现起来就很容易了。
下面是一个 GLSL 顶点着色器,实现了顶点从模型空间到屏幕空间的转换:(摘自【openGL教程 11 】关于坐标系统)
#version 330 core
layout (location = 0) in vec3 aPos;
...
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
// note that we read the multiplication from right to left
gl_Position = projection * view * model * vec4(aPos, 1.0);
...
}
每当您想要绘制具有特定世界空间变换的模型时,您只需在统一变量model中提供该模型的局部变换,而您的相机实现通常会提供其他两个矩阵。
四、一个具体问题如何实现?
我有一个渲染的球体,当它围绕中心位于世界坐标 (0, 0, 0)的椭圆轨道旋转时。我想加一个自转,绕 z 倾斜并绕局部 y 轴旋转。我可以做所有这些,只是我必须累积而不是增量地围绕局部 y 轴进行旋转,这样旋转就会呈现为抖动效果。对于每个渲染循环,我都从一个单位模型矩阵开始,将其转换为轨道位置,倾斜它,然后使用 glm 函数将其累积旋转到新的累积轴向位置。我还没有想出消除抖动的方法。有什么建议吗?
在现代 OpenGL 中,您可以定义要用作加载的着色器程序的一部分的任何矩阵。习惯编写着色器代码的想法。我知道这很可怕。我保证,这还不错。
具体而言,您需要将它们定义为“统一变量”,即着色器可用的数据(但不与顶点关联)。您可以将其他数据作为“统一变量”传递,包括矩阵的逆数据,以备不时之需…
例如,我的一些着色器代码是这样开始的:
#version 330 core
uniform vec2 resolution;
uniform float time;
uniform mat4 projection;
uniform mat4 inverseProjection;
uniform mat4 view;
uniform mat4 inverseView;
// ...
在这里,我将屏幕大小作为 vec2 分辨率传递,将当前时间作为用于在 GPU 中进行动画处理的浮点数传递,然后传递一些 mat4,每个传递矩阵都是一个变换矩阵。
那么,我们如何指定我们要设置的统一变量呢?
-
我们将使用着色器程序的 id(作为设置着色器过程的一部分从 glCreateProgram 获得)和所需制服的字符串名称来调用 glGetUniformLocation。
-
然后,根据所需的数据类型调用 glUniform 函数,并传递从 glGetUniformLocation 获取的位置。
-
由于我们将设置 mat4,因此我们将调用 glUniformMatrix4fv。你会发现,如果你想在设置矩阵时转置矩阵,你还需要指定一个bool(以防万一你使用的是行主序,但OpenGL想要列主序)。
例:
// Get the location of the matrix you want to set
int modelLocation = glGetUniformLocation(shader_id, "model");
// Set it
glUniformMatrix4fv(modelLocation, 1, false, model[0]);
// ^ ^ ^ ^
// | | | |
// | | | pointer to the start of the matrix
// | | no, do not transpose my matrix
// | only take one matrix from that pointer, thank you
// the location on the shader program where we are setting...
// ... that identifies what uniform we are setting
哦,对了,你可以有一个数组mat4作为 uniform,例如:
// somewhere in the shader header
uniform mat4 bones[10];
然后在代码中设置它:
int bonesLocation = glGetUniformLocation(shader_id, "bones");
glUniformMatrix4fv(bonesLocation, 10, false, bones[0]);
// ^
// |
// Take 10 mat4
在处理光照,也可以雷同的手法,如我目前正在编写一个光线投射器,我有这样的代码:
// ...
gl_Position = vec4(position.xy, 0.0, 1.0);
cameraRayDirection = ((inverseView * inverseProjection) * gl_Position).xyz;
cameraRayPosition = (inverseView * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
// ...
五、结论
从顶点着色器对几何处理,这是官方手册所提倡的,这无形中提高的编程人员的困难,正是因为这些困难存在,也使得现代渲染技术变得更加可贵。