创建OpenGL上下文

本节介绍如何配置OpenGL上下文。对于大多数应用程序来说,这里描述的信息级别太低,不会引起任何关注,但是更高级的应用程序可以利用小程序提供的完整控制。

显示、屏幕、配置和环境

../_images/context_flow.png

从抽象画布到新创建的窗口及其上下文的构造流。

环境和配置

在pyglet中的窗口上绘制时,您是在OpenGL上下文中绘制。每个窗口都有自己的上下文,上下文是在创建窗口时创建的。您可以通过窗口的 context 属性。

上下文是从OpenGL配置(或“配置”)创建的,它描述了上下文的各种属性,例如使用什么颜色格式、可用的缓冲区数量等。可以通过上下文的访问用于创建上下文的配置 config 属性。

例如,在这里,我们使用默认配置创建一个窗口,并检查其一些属性:

>>> import pyglet
>>> window = pyglet.window.Window()
>>> context = window.context
>>> config = context.config
>>> config.double_buffer
c_int(1)
>>> config.stereo
c_int(0)
>>> config.sample_buffers
c_int(0)

请注意,配置属性的值都是ctype实例。这是因为配置不是由pyglet指定的。相反,它是由pyglet从系统支持的配置列表中选择的。您不能保证给定的配置在系统上有效,除非它是由系统提供给您的。

通过允许您创建只指定您感兴趣的值的“模板”配置,pyglet简化了选择系统配置之一的过程。看见 简单的情景配置 了解更多细节。

显示

系统实际上可能支持几组不同的配置,具体取决于所使用的显示设备。例如,具有两个视频卡的计算机可能不支持每个显卡上的相同配置。另一个例子是远程使用X11:显示设备将支持与本地驱动程序不同的配置。即使本地计算机上的单个视频卡也可能支持插入两台显示器的不同配置。

在pyglet中,一个 Display 是连接到单个显示设备的“屏幕”的集合。在Linux上,显示设备对应于正在使用的X11显示器。在Windows和Mac OS X上,只有一个显示器(因为这些操作系统将多个视频卡作为一个虚拟设备呈现)。

这个 pyglet.canvas 模块提供对显示器的访问(S)。使用 get_display() 函数以获取默认显示::

>>> display = pyglet.canvas.get_display()

备注

在X11上,您可以使用 Display 类直接指定要使用的显示字符串,例如用于使用远程连接的显示。名称字符串的格式与 DISPLAY 环境变量::

>>> display = pyglet.canvas.Display(name=':1')

如果您有多个物理屏幕,并且您正在使用Sinerama,请参见 屏风 来选择所需的屏幕,就像选择Windows和Mac OS X一样。否则,您可以通过 x_screen 论点::

>>> display = pyglet.canvas.Display(name=':1', x_screen=1)

屏风

一旦您获得了显示,您就可以列举所连接的屏幕。屏幕是连接到显示设备的物理显示介质;例如,计算机显示器、电视或投影仪。大多数计算机将只有一个屏幕,但连接到投影仪的双头工作站和笔记本电脑是常见的情况,其中将出现多个屏幕。

在下面的示例中,列出了双头工作站的屏幕:

>>> for screen in display.get_screens():
...     print(screen)
...
XlibScreen(screen=0, x=1280, y=0, width=1280, height=1024, xinerama=1)
XlibScreen(screen=0, x=0, y=0, width=1280, height=1024, xinerama=1)

由于该工作站运行的是Linux,因此返回的屏幕为 XlibScreen ,是的子类 Screen 。这个 screenxinerama 属性特定于Linux,但 xywidthheight 属性出现在所有屏幕上,并描述屏幕的几何形状,如下所示。

../_images/screens.png

屏幕的排列及其报告的几何图形的示例。请注意,根据该特定用户的喜好,主显示器(标记为“1”)位于右侧。

总是有一个“默认”屏幕,这是由返回的第一个屏幕 get_screens() 。根据操作系统的不同,默认屏幕通常是包含任务栏(Windows)或菜单栏(OS X)的屏幕。您可以使用以下命令直接访问此屏幕 get_default_screen()

OpenGL配置选项

在配置或选择 Config ,则根据该配置的属性执行此操作。Piglet支持由AGL、GLX、WGL及其扩展提供的选项的固定子集。特别是,这些约束被放置在所有OpenGL配置上:

  • 缓冲区始终是组件(RGB或RGBA)颜色,而不是调色板索引。

  • 缓冲区的“级别”始终为0(该参数基本上不受现代OpenGL驱动程序的支持)。

  • 无法设置缓冲区的透明颜色(同样,此特定于GLX的选项不受很好的支持)。

  • 不支持pBuffers(使用帧缓冲区对象可以更简单、更高效地实现同等功能)。

缓冲区的可见部分(有时称为颜色缓冲区)配置有以下属性:

buffer_size

每个样本的位数。公用值为24和32,每种颜色分量专用8位。缓冲器大小16也是可能的,其通常分别对应于红色、绿色和蓝色的5、6和5比特。

通常不需要设置此属性,因为默认情况下,设备驱动程序将选择与当前显示模式兼容的缓冲区大小。

red_size, blue_size, green_size, alpha_size

它们每个都给出了专用于其各自颜色分量的位数。应避免设置任何红色、绿色或蓝色大小,因为这些大小由驱动程序根据 buffer_size 财产。

如果颜色缓冲区中需要Alpha通道(例如,如果要在多个过程中合成),则应指定 alpha_size=8 以确保创建此频道。

sample_buffers and samples

配置多重采样缓冲区(MSAA),其中使用多个颜色样本来确定每个像素的颜色,从而产生更高质量的抗锯齿图像。

通过设置启用多重采样(MSAA) sample_buffers=1 ,然后给出每像素要使用的样本数。 samples 。例如, samples=2 是速度最快、质量最低的多样本配置。 samples=4 仍然受到广泛支持,即使在英特尔高清和AMD Vega上也表现相当出色。大多数现代GPU支持2×、4×、8×和16×MSAA样本,具有相当高的性能。

stereo

创建单独的左侧和右侧缓冲区,用于立体声硬件。只有立体眼镜等专用视频硬件才会支持这一选项。使用时,您需要手动渲染到每个缓冲区,例如使用 glDrawBuffers

double_buffer

创建单独的前台缓冲区和后台缓冲区。在没有双缓冲的情况下,绘图命令在屏幕上立即可见,当图像在他们面前重新绘制时,用户将注意到可见的闪烁。

建议您设置 double_buffer=True ,它创建一个单独的隐藏缓冲区,在该缓冲区中执行绘制。当 Window.flip 调用时,将交换缓冲区,使新图形几乎瞬间可见。

除颜色缓冲区外,还可以根据这些属性的值有选择地创建其他几个缓冲区:

depth_size

3D渲染通常需要深度缓冲区。典型的深度大小为24位。指定 0 如果不需要深度缓冲区。

stencil_size

模板缓冲区是屏蔽其他缓冲区和实现某些体积阴影算法所必需的。典型的模板大小为8位;或指定 0 如果你不需要的话。

accum_red_size, accum_blue_size, accum_green_size, accum_alpha_size

累积缓冲区可用于简单的抗锯齿、景深、运动模糊和其他合成操作。它现在的使用正在被浮点纹理的使用所取代,但对于在较旧的硬件上实现这些效果来说,它仍然是一个实用的解决方案。

如果需要累积缓冲区,请指定 8 这些属性中的每一个(当然,Alpha组件是可选的)。

aux_buffers

每个辅助缓冲器被配置为与颜色缓冲器相同。通常最多可以创建四个辅助缓冲区。指定 0 如果您不需要任何辅助缓冲区。

像累积缓冲区一样,辅助缓冲区现在不常使用,因为可以使用更有效的技术,如渲染到纹理。然而,它们几乎在较旧的硬件上普遍可用,而较新的技术不可能实现。

如果您希望直接使用OpenGL,您可以请求更高级别的上下文。如果您希望使用现代的OpenGL可编程管道,这是必需的。然而,请注意,Piglet目前在其许多内部模块(如文本、图形和精灵模块)中使用遗留的OpenGL功能。请求更高版本的上下文当前将阻止使用这些模块。

major_version

对于OpenGL 3.x或4.x环境,此值为3或4。

minor_version

请求的上下文的次要版本。在某些情况下,OpenGL驱动程序可能返回比请求的版本更高的版本。

forward_compatible

将其设置为 True 将要求驱动程序从上下文中排除旧的OpenGL功能。Khronos不建议使用此选项。

备注

要在Mac OSX上请求更高版本的OpenGL上下文,必须禁用pyglet阴影上下文。要执行此操作,请设置pyglet选项 pyglet.options['shadow_window']False before 创建窗口或导入 pyglet.window

默认配置

如果您创建一个 Window 在不指定上下文或配置的情况下,pyglet将使用具有以下属性的模板配置:

属性

价值

double_buffer

千真万确

depth_size

24

简单的情景配置

只能从系统提供的配置创建上下文。枚举和比较所有可能配置的属性是一个复杂的过程,因此Piglet提供了一个基于“模板”配置的更简单的接口。

要获得具有所需属性的配置,请构造一个 Config 并且只设置您感兴趣的属性。然后,您可以将此配置提供给 Window 构造函数来创建上下文。

例如,要创建具有Alpha通道的窗口:

config = pyglet.gl.Config(alpha_size=8)
window = pyglet.window.Window(config=config)

有时有必要自己创建上下文,而不是让 Window 构造函数为您完成此操作。在本例中,使用 get_best_config() 要获取“完整”配置,然后可以使用该配置来创建上下文:

display = pyglet.canvas.get_display()
screen = display.get_default_screen()

template = pyglet.gl.Config(alpha_size=8)
config = screen.get_best_config(template)
context = config.create_context(None)
window = pyglet.window.Window(context=context)

请注意,您不能直接从模板(任何模板)创建上下文 Config 你构建了你自己)。这个 Window 如果给定了模板配置,则构造函数执行与上面类似的过程来创建上下文。

并非所有配置都可以在所有计算机上使用。呼唤 get_best_config() 将筹集 NoSuchConfigException 如果硬件不支持请求的属性。它永远不会返回不符合或超过您在模板中指定的属性的配置。

您可以使用它来支持可用的较新硬件功能,但也可以在必要时接受较低的配置。例如,如果可能,下面的代码将创建一个具有多采样的窗口,否则将禁用多采样:

template = pyglet.gl.Config(sample_buffers=1, samples=4)
try:
    config = screen.get_best_config(template)
except pyglet.window.NoSuchConfigException:
    template = gl.Config()
    config = screen.get_best_config(template)
window = pyglet.window.Window(config=config)

选择最佳配置

允许Piglet基于模板选择最佳配置对于大多数应用程序来说已经足够了,然而,一些复杂的程序可能希望指定自己的算法来选择一组OpenGL属性。

方法枚举屏幕的配置。 get_matching_configs() 方法。您必须提供模板作为最低规格,但您可以提供“空”模板(未设置属性)以获取屏幕支持的所有配置的列表。

在以下示例中,打印具有辅助缓冲区或累积缓冲区的所有配置:

display = pyglet.canvas.get_display()
screen = display.get_default_screen()

for config in screen.get_matching_configs(gl.Config()):
    if config.aux_buffers or config.accum_red_size:
        print(config)

除了支持更复杂的配置选择算法外,枚举还允许您高效地找到属性的最大值(例如,每像素的最大样本数),或向用户显示可能的配置列表。

在上下文之间共享对象

Piglet中的每个窗口都有自己的OpenGL上下文。每个上下文都有自己的OpenGL状态,包括矩阵堆栈和当前标志。但是,上下文可以选择性地与一个或多个其他上下文共享其对象。可共享对象包括:

  • 纹理

  • 显示列表

  • 着色器程序

  • 顶点和像素缓冲区对象

  • FrameBuffer对象

共享对象有两个原因。第一个是允许对象只在视频卡上存储一次,即使对象被多个窗口使用。例如,您可以有一个显示实际游戏的窗口,而其他“调试”窗口则显示各种对象被操纵时的情况。或者,可以在应用程序中的所有窗口之间共享一组图形用户界面所需的小部件纹理。

第二个原因是避免在需要重新创建上下文时必须重新创建对象。例如,如果用户希望打开多重采样,则需要重新创建上下文。与其销毁旧版本并丢失已创建的所有对象,您还可以

  1. 创建新上下文,与旧上下文共享对象空间,然后

  2. 破坏旧的背景。新上下文将保留所有旧对象。

pyglet定义了一个 ObjectSpace :一个或多个上下文使用的对象集合的表示形式。每个上下文都有一个单独的对象空间,可通过其 object_space 属性。

默认情况下,只要至少有一个使用对象空间的上下文是“活动的”,所有上下文就共享相同的对象空间。如果共享一个对象空间的所有上下文都丢失或被破坏,则该对象空间也将被破坏。这就是为什么在重新创建上下文时需要遵循上面概述的步骤来保留对象的原因。

一开始,pyglet就会创建一个隐藏的“影子”上下文 pyglet.gl 是进口的。默认情况下,所有窗口都将与该阴影上下文共享对象空间,因此通常不需要上述步骤。阴影上下文还允许在创建窗口之前加载纹理等对象(请参见 shadow_window 在……里面 pyglet.options 以了解更多详细信息)。

当您创建 Context ,您告诉pyglet它将从哪个其他上下文获取对象空间。默认情况下(使用 Window 构造函数来创建上下文),将使用最近创建的上下文。中指定另一个上下文或不指定上下文(以创建新的对象空间 Context 构造函数。

跟踪对象是在哪个对象空间中创建的非常有用。例如,当您加载字体时,pyglet会缓存所使用的纹理并重复使用它们;但前提是要将该字体加载到同一对象空间中。执行此操作的最简单方法是在 ObjectSpace 对象。

在下面的示例中,在对象空间上设置一个属性,该属性指示已经加载了游戏对象。这样,如果重新创建了上下文,您可以检查此属性以确定是否需要再次加载它们::

context = pyglet.gl.current_context
object_space = context.object_space
object_space.my_game_objects_loaded = True

避免在上使用属性名称 ObjectSpace 开头是 "pyglet" ,因为它们可能与内部模块冲突。