pyglet/pyopengl 如何使用透明层而不是纯色清除屏幕

How to clear screen with a transparency layer instead of a solid color in pyglet/pyopengl

我是 pyglet 的新手,我正在尝试绘制一个可以四处移动的对象。我想显示这个物体的轨迹,但是让旧的位置逐渐淡化。因此,我试图在每次绘制时使用半透明颜色清除屏幕。

但是,由于某些原因,window clear 方法是不透明的,即使我将颜色设置为几乎透明也是如此。如果我省略 clear 并绘制一个与 window 一样大的半透明正方形,则代码有效,但我不明白为什么 clear 方法会产生纯色。

这是我的代码:

import pyglet


class GameWindow(pyglet.window.Window):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.px, self.py = 10, 10
        self.vx, self.vy = 15, 60
        self.ax, self.ay = 0, -5

    def update(self, dt):
        self.px, self.py = self.px + self.vx * dt, self.py + self.vy * dt
        self.vx, self.vy = self.vx + self.ax * dt, self.vy + self.ay * dt

    def on_draw(self):
        # clear screen with semitransparent layer
        pyglet.gl.glEnable(pyglet.gl.GL_BLEND)
        pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA, pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)
        pyglet.gl.glClearColor(0, 0, 0, 0.01)  # black and almost transparent
        self.clear()

        # calculate and draw object
        relative_vertices = [[20, 20], [20, -20], [-20, -20], [-20, 20]]
        vertices = []
        for v in relative_vertices:
            vertices.append(self.px + v[0])
            vertices.append(self.py + v[1])
        colors = [255, 0, 0] * len(relative_vertices)
        pyglet.graphics.vertex_list(len(relative_vertices), ("v2f", vertices), ("c3B", colors)).draw(pyglet.gl.GL_POLYGON)


if __name__ == "__main__":
    config = pyglet.gl.Config(double_buffer=False)
    window = GameWindow(400, 400, config=config)
    pyglet.clock.schedule_interval(window.update, 1/120)
    pyglet.app.run()

由于某些原因,我可以绘制透明对象,但是使用clear 时背景是不透明的。为什么这不起作用?

此外,我想知道是否有一种方法可以使所有背景像素更加透明并保持其颜色,而不必在它们之上绘制半透明的黑色层。

PyGlet 是一个 OpenGL 包装器。清除方法使用 glClear. glClear does nothing else than copy the current clear color (which is set by glClearColor ) 颜色平面(当前帧缓冲区)的所有像素。此操作未混合任何内容。

您必须在整个 window 上混合一个矩形,而不是使用 clear:

# pyglet.gl.glClearColor(0, 0, 0, 0.01)  # <--- delete
# self.clear()                           # <--- delete
cover_vertices = [0, 0, 400, 0, 400, 400, 0, 400]
cover_colors = [0, 0, 0, 2] * (len(cover_vertices)//2)
pyglet.graphics.vertex_list(len(cover_vertices)//2, ("v2f", cover_vertices), ("c4B", cover_colors)).draw(pyglet.gl.GL_POLYGON)


Is there a way to make this look better? I had already tried this but I was not happy with the result. I would expect the dark red trajectory to slowly fade into black again

在这种情况下,必须更改混合模式。绘制一个覆盖屏幕并从目标颜色中减去的矩形。这可以通过将 glBlendEquation 参数设置为 GL_FUNC_REVERSE_SUBTRACT.
来实现 最后在顶部绘制红色矩形,禁用混合模式:

# subtract [1, 1, 1, 0] from all pixels in the frambuffer
        pyglet.gl.glEnable(pyglet.gl.GL_BLEND)
        pyglet.gl.glBlendEquation(pyglet.gl.GL_FUNC_REVERSE_SUBTRACT)
        pyglet.gl.glBlendFunc(pyglet.gl.GL_ONE, pyglet.gl.GL_ONE)
        cover_vertices = [0, 0, 400, 0, 400, 400, 0, 400]
        cover_colors = [1, 1, 1, 0] * (len(cover_vertices)//2)
        pyglet.graphics.vertex_list(len(cover_vertices)//2, ("v2f", cover_vertices), ("c4B", cover_colors)).draw(pyglet.gl.GL_POLYGON)

        # draw the object on top of the frambuffer
        pyglet.gl.glDisable(pyglet.gl.GL_BLEND)
        relative_vertices = [[20, 20], [20, -20], [-20, -20], [-20, 20]]
        vertices = []
        for v in relative_vertices:
            vertices.append(self.px + v[0])
            vertices.append(self.py + v[1])
        colors = [255, 0, 0] * len(relative_vertices)
        pyglet.graphics.vertex_list(len(relative_vertices), ("v2f", vertices), ("c3B", colors)).draw(pyglet.gl.GL_POLYGON)