Working With FrameBuffer Objects#

Start with a simple window:

Starting template#
 1import arcade
 2
 3SCREEN_WIDTH = 800
 4SCREEN_HEIGHT = 600
 5SCREEN_TITLE = "Frame Buffer Object Demo"
 6
 7
 8class MyGame(arcade.Window):
 9
10    def __init__(self, width, height, title):
11        super().__init__(width, height, title)
12
13        self.background_color = arcade.color.ALMOND
14
15    def setup(self):
16        pass
17
18    def on_draw(self):
19        self.clear()
20
21
22def main():
23    """ Main function """
24    window = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
25    window.setup()
26    arcade.run()
27
28
29if __name__ == "__main__":
30    main()

Then create a simple program with a frame buffer:

Pass-through frame buffer#
 1import arcade
 2from arcade.experimental.texture_render_target import RenderTargetTexture
 3
 4SCREEN_WIDTH = 800
 5SCREEN_HEIGHT = 600
 6SCREEN_TITLE = "Starting Template Simple"
 7
 8
 9class RandomFilter(RenderTargetTexture):
10    def __init__(self, width, height):
11        super().__init__(width, height)
12        self.program = self.ctx.program(
13            vertex_shader="""
14            #version 330
15
16            in vec2 in_vert;
17            in vec2 in_uv;
18            out vec2 uv;
19
20            void main() {
21                gl_Position = vec4(in_vert, 0.0, 1.0);
22                uv = in_uv;
23            }
24            """,
25            fragment_shader="""
26            #version 330
27
28            uniform sampler2D texture0;
29
30            in vec2 uv;
31            out vec4 fragColor;
32
33            void main() {
34                vec4 color = texture(texture0, uv);
35                fragColor = color;
36            }
37            """,
38        )
39
40    def use(self):
41        self._fbo.use()
42
43    def draw(self):
44        self.texture.use(0)
45        self._quad_fs.render(self.program)
46
47
48class MyGame(arcade.Window):
49
50    def __init__(self, width, height, title):
51        super().__init__(width, height, title)
52        self.filter = RandomFilter(width, height)
53
54    def on_draw(self):
55        self.clear()
56        self.filter.clear()
57        self.filter.use()
58        arcade.draw_circle_filled(self.width / 2, self.height / 2, 100, arcade.color.RED)
59        arcade.draw_circle_filled(400, 300, 100, arcade.color.GREEN)
60
61        self.use()
62        self.filter.draw()
63
64
65def main():
66    """ Main function """
67    MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
68    arcade.run()
69
70
71if __name__ == "__main__":
72    main()
73

Now, color everything that doesn’t have an alpha of zero as green:

Pass-through frame buffer#
 1import arcade
 2from arcade.experimental.texture_render_target import RenderTargetTexture
 3
 4SCREEN_WIDTH = 800
 5SCREEN_HEIGHT = 600
 6SCREEN_TITLE = "Starting Template Simple"
 7
 8
 9class RandomFilter(RenderTargetTexture):
10    def __init__(self, width, height):
11        super().__init__(width, height)
12        self.program = self.ctx.program(
13            vertex_shader="""
14            #version 330
15
16            in vec2 in_vert;
17            in vec2 in_uv;
18            out vec2 uv;
19
20            void main() {
21                gl_Position = vec4(in_vert, 0.0, 1.0);
22                uv = in_uv;
23            }
24            """,
25            fragment_shader="""
26            #version 330
27
28            uniform sampler2D texture0;
29
30            in vec2 uv;
31            out vec4 fragColor;
32
33            void main() {
34                vec4 color = texture(texture0, uv);
35
36                if (color.a > 0)
37                    fragColor = vec4(0, 1, 0, 1.0);
38                else
39                    fragColor = vec4(0, 0, 0, 0);
40            }
41            """,
42        )
43
44    def use(self):
45        self._fbo.use()
46
47    def draw(self):
48        self.texture.use(0)
49        self._quad_fs.render(self.program)
50
51
52class MyGame(arcade.Window):
53
54    def __init__(self, width, height, title):
55        super().__init__(width, height, title)
56        self.filter = RandomFilter(width, height)
57
58    def on_draw(self):
59        self.clear()
60        self.filter.clear()
61        self.filter.use()
62        arcade.draw_circle_filled(self.width / 2, self.height / 2, 100, arcade.color.RED)
63
64        self.use()
65        self.filter.draw()
66
67
68def main():
69    """ Main function """
70    MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
71    arcade.run()
72
73
74if __name__ == "__main__":
75    main()

Something about passing uniform data to the shader:

Pass-through frame buffer#
 1import arcade
 2from arcade.experimental.texture_render_target import RenderTargetTexture
 3
 4SCREEN_WIDTH = 800
 5SCREEN_HEIGHT = 600
 6SCREEN_TITLE = "Starting Template Simple"
 7
 8
 9class RandomFilter(RenderTargetTexture):
10    def __init__(self, width, height):
11        super().__init__(width, height)
12        self.program = self.ctx.program(
13            vertex_shader="""
14            #version 330
15
16            in vec2 in_vert;
17            in vec2 in_uv;
18            out vec2 uv;
19
20            void main() {
21                gl_Position = vec4(in_vert, 0.0, 1.0);
22                uv = in_uv;
23            }
24            """,
25            fragment_shader="""
26            #version 330
27
28            uniform sampler2D texture0;
29
30            in vec2 uv;
31            uniform vec4 my_color;
32            out vec4 fragColor;
33
34            void main() {
35                vec4 color = texture(texture0, uv);
36
37                if (color.a > 0)
38                    fragColor = my_color;
39                else
40                    fragColor = vec4(0, 0, 0, 0);
41            }
42            """,
43        )
44        self.program["my_color"] = 1, 0, 1, 1
45
46    def use(self):
47        self._fbo.use()
48
49    def draw(self):
50        self.texture.use(0)
51        self._quad_fs.render(self.program)
52
53
54class MyGame(arcade.Window):
55
56    def __init__(self, width, height, title):
57        super().__init__(width, height, title)
58        self.filter = RandomFilter(width, height)
59
60    def on_draw(self):
61        self.clear()
62        self.filter.clear()
63        self.filter.use()
64        arcade.draw_circle_filled(self.width / 2, self.height / 2, 100, arcade.color.RED)
65
66        self.use()
67        self.filter.draw()
68
69
70def main():
71    """ Main function """
72    MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
73    arcade.run()
74
75
76if __name__ == "__main__":
77    main()