为什么教程使用不同的方法进行OpenGL渲染?


43

http://www.sdltutorials.com/sdl-opengl-tutorial-basics

http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/

这两个教程使用完全不同的方法来获得几乎相同的结果。第一种使用类似的东西glBegin(GL_QUADS)。第二个使用vertexBufferObjects基于GLEW的着色器之类的东西。但是结果是一样的:您获得了基本形状。

为什么存在这些差异?

第一种方法似乎更容易理解。复杂的第二种方法的优点是什么?


4
永远不会只有一种为猫皮的方法。
菲利普

4
@Philipp是的,但是有正确的方法和错误的方法,旧方法和新方法(并且如以下答案所示,旧方法和新方法可能在所有情况下都不兼容)
Andrew Hill

3
没有正确的方法和错误的方法,只有更坏的方式,更好的方法(在几个不同的维度)。
user253751

glBeginglEnd已弃用,因为它们对于当前的图形体系结构效率极低
Alex

Answers:


77

OpenGL有四个不同的主要版本,不包括移动设备和嵌入式系统(OpenGL | ES)以及通过JavaScript通过Web(WebGL)的版本。就像Direct3D 11与Direct3D 8具有不同的处理方式一样,OpenGL 3与OpenGL 1也具有不同的处理方式。最大的不同是OpenGL版本基本上只是旧版本的附件(但不是完全)。

除了OpenGL的不同版本外,主要OpenGL还添加了配置文件的概念。即兼容性配置文件(启用对较早版本的API的支持)和核心配置文件(禁用那些旧的API)。glBegin当您使用Core Profile时,诸如此类的东西根本不起作用,而当您使用Compatibility Profile(默认情况)时,则不会。

另一个严重的麻烦是,某些OpenGL实现(例如Apple的实现)仅在使用Core Profile时才启用较新的OpenGL功能。这意味着您必须停止使用较旧的API才能使用较新的API。

然后,您将遇到几种非常令人困惑的教程场景:

  1. 本教程很旧,仅使用不推荐使用的API。
  2. 本教程是新的且编写良好,仅使用与Core兼容的API。
  3. 本教程是新的,但是会犯一个错误,即假定您正在使用在兼容模式下启用所有API并自由地混合使用新旧API的驱动程序。
  4. 本教程适用于其他版本的OpenGL,例如OpenGL | ES,该版本完全不支持任何版本的旧API。

类似glBegin的事情是有时称为即时模式API的一部分。这也非常令人困惑,因为在OpenGL中没有保留模式,而“即时模式”在图形中已经有了不同的定义。最好将它们称为OpenGL 1.x API,因为自OpenGL 2.1以来它们已经过时了。

过去,OpenGL的1.x API会立即将顶点提交给图形管道。当渲染顶点的硬件速度与生成顶点数据的CPU速度大致相当时,此方法效果很好。当时的OpenGL仅卸载了三角形栅格化,而没有其他事情。

如今,GPU可以以极高的速度咀嚼大量顶点,同时执行高级顶点和像素转换,CPU甚至无法远程跟上。最重要的是,CPU和GPU之间的接口是围绕这种速度差异而设计的,这意味着不再可能一次将顶点提交给GPU。

所有GL驱动程序都必须glBegin在内部进行仿真,方法是在内部分配一个顶点缓冲区,将与提交的顶点一起glVertex放入此缓冲区,然后在调用时在单个绘制调用中提交整个缓冲区glEnd。这些功能的开销远比您自己更新顶点缓冲区要大得多,这就是为什么某些文档(非常错误!)将顶点缓冲区称为“优化”(这不是优化;这是实际实现的唯一方法)的原因。与GPU对话)。

多年来,OpenGL中已弃用或废弃了各种其他API。所谓的固定功能管道就是另一个这样的部分。一些文档可能仍然使用此管道或与可编程管道混合使用。固定功能流水线源于过去的日子,当时图形卡对用于渲染3D场景的所有数学运算进行硬编码,并且OpenGL API限于为该数学运算设置一些配置值。如今,硬件几乎没有硬编码的数学运算了(就像您的CPU一样),而是运行用户提供的程序(通常称为着色器)。

再次,驱动程序必须模拟旧的API,因为硬件上不再存在固定功能。这意味着驱动程序中嵌入了许多兼容的着色器,这些着色器从固定功能开始执行旧的数学运算,而当您不提供自己的着色器时会使用该数学运算。修改该旧固定功能状态的旧OpenGL函数(例如旧OpenGL照明API)实际上是在使用现代OpenGL功能(例如统一缓冲区)将这些值提供给驱动程序的兼容性着色器。

支持兼容性的驱动程序必须做很多幕后工作,才能弄清楚何时使用这些过时的功能,并确保可以将它们与现代功能平滑地结合在一起,这增加了开销,并使驱动程序大大复杂化。这是一些驱动程序迫使您启用Core Profile以获得更新功能的原因之一;它不必同时支持新旧API,从而大大简化了其驱动程序内部。

许多文档可能建议您仅从旧的API开始,因为它们较容易上手。Direct3D通过提供一个随附的库(DirectX Tool Kit)为初学者解决了此问题,该库提供了更简单的绘图API和预编写的着色器,可以随您的专业知识的增长与Direct3D 11的原始用法自由地混合。不幸的是,更广泛的OpenGL社区大多数人都坚持使用Compatibility Profile,这是有问题的,因为有些系统不允许您将旧的OpenGL API与较新的OpenGL API混合使用。有一些非官方的库和工具,可以在新的OpenGL上使用功能和目标用例和语言的不同级别简化渲染(MonoGame (例如.NET使用者),但没有任何正式认可或广泛同意的内容。

您找到的文档甚至可能不是针对OpenGL的,而是针对其他类似API之一的。OpenGL | ES 1.x具有固定功能的渲染,但没有用于顶点提交的OpenGL 1.x API。OpenGL | ES 2.x +和WebGL 1+根本没有任何固定功能,并且这些API没有向后兼容模式。

这些API与主要的OpenGL非常相似。它们并不完全兼容,但是OpenGL有官方扩展,某些(并非全部)驱动程序支持这些扩展以与OpenGL | ES(基于WebGL的)兼容。因为之前的事情还不够混乱。


4
+1很棒的答案!如果您能提到其中一些用于在新OpenGL上进行简单渲染的非官方库和工具,那就太好了:)
Mehrdad15年

2
辉煌的答案。那天我在DirectX上遇到了同样的麻烦-比OpenGL要简单得多,但是从保留/立即模式到着色器的飞跃是巨大的。幸运的是,文档提供了很多帮助(至少与OpenGL不同,这与OpenGL无关),但是“我什至不发光”的开头很疯狂:D
Luaan

我是opengl-tutorial.org的作者,我同意Sean的观点。API之所以如此发展,主要是出于性能方面的考虑。
Calvin1602 2015年

关于该主题的很好的信息..
reynmar 2015年

1
@Mehrdad:我不记得有什么事想念。有像SDL2或SFML这样的库,它们添加了简化的2D渲染,各种场景图库,C#的MonoGame等,但是考虑到这一点,我实际上并不知道直接与Direct TK等效的任何东西。因为说“很多”可能是一个大谎言,所以将编辑帖子。:)
肖恩·米德迪奇

9

主要区别在于策略的最新程度。第一个教程中使用的立即模式:

glBegin(GL_QUADS);
    glColor3f(1, 0, 0); glVertex3f(0, 0, 0);
    glColor3f(1, 1, 0); glVertex3f(100, 0, 0);
    glColor3f(1, 0, 1); glVertex3f(100, 100, 0);
    glColor3f(1, 1, 1); glVertex3f(0, 100, 0);
glEnd();

已过时,较新版本不支持。

使用顶点缓冲区和着色器是当前使用OpenGL渲染的方法。它可能看起来更复杂,但性能要好得多。此外,一旦您的支持代码包装了OpenGL内容,大部分差异就被抽象化了。


2

只是为其他出色的答案添加了更多背景信息。

正如其他人所说,第一个链接中所述的立即模式是OpenGL(1.1)最早版本中的遗留代码。当GPU只不过是三角形光栅化器并且不存在可编程管线的想法时,就使用了它。例如,如果您查看某些早期的硬件加速游戏(例如GLQuake和Quake 2)的源代码,则会看到即时模式正在使用中。简单来说,CPU一次将一个顶点的指令发送给GPU,以开始在屏幕上绘制三角形。作为记录,GL_QUADS具有与GL_TRIANGLES相同的结果,除了GPU必须将这些四边形自己动态地变成三角形。

现代(3.2+)OpenGL采用不同的方法。它将顶点数据缓冲到GPU内存中以便快速访问,然后可以使用glDrawArrays或glDrawElements发送绘图指令。您还具有可编程管线(glUseProgram),可用于自定义GPU如何对顶点进行定位和着色。

不建议使用立即模式的原因有很多,主要原因是性能。正如肖恩(Sean)在回答中所说的那样,当今GPU处理数据的速度比CPU上传数据的速度快,因此您将成为GPU性能的瓶颈。您对OpenGL的每次调用都涉及很小的开销,这是微不足道的,但是当您每帧进行成千上万次调用时,它就会开始堆积。简而言之,要使用即时模式绘制纹理化模型,每帧每个顶点至少需要调用2次(glTexCoord2f和glVertex3f)。在现代OpenGL中,您一开始会使用几个调用来缓冲数据,然后可以绘制整个模型,无论它包含多少个顶点,只需几次调用即可绑定顶点数组对象,并启用一些属性指针和然后一次调用glDrawElements或glDrawArrays。

哪种技术正确?好吧,这取决于您要做什么。一个简单的2D游戏,不需要任何花哨的后处理技术或着色器,使用即时模式就可以很好地工作,并且编写代码可能会更容易。但是,更现代的3D游戏确实会遇到困难,如果您打算学习GLSL(着色器语言),那么一定要学习现代技术。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.