I am new to pyglet, and I am trying to draw an object that moves around. I want to show the trajectory of this object, but make the older positions fade gradually. Therefore I am trying to clear the screen in every draw with a semitransparent color.
But, for some reason, the window clear method is opaque, even when I set the color to be almost transparent. The code works if I omit the clear and draw a semitransparent square as big as the window, but I don't understand why the clear method produces a solid color.
This is my code:
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()
For some reason, I can draw transparent objects, but the background when clear is used is opaque. Why is this not working?
Also, I would like to know if there is a way to make all background pixels more transparent maintaining their color, instead of having to draw a semitransparent black layer on top of them.
PyGlet is a OpenGL wrapper. The clear method use glClear
. glClear
does nothing else than copy the current clear color (which is set by glClearColor
) to all pixels of the color plane (of the current framebuffer). Nothing is blended with this operations.
You've to blend a rectangle on the entire window, instead of using 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
In this case the blend mode has to be changed. Draw a rectangle which covers the screen and is subtracted from the destination color. This can be achieved by setting the glBlendEquation
parameter to GL_FUNC_REVERSE_SUBTRACT
.
Finally draw the red rectangle on top, with blending mode disabled:
# 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)