透视#

perspective.py#
1# flake8: noqa
2"""
3Perspective example using the lower level rendering API.
4
5This is definitely in the advanced section, but it can be
6a useful tool to learn. Sometimes we want perspective
7projection for things like backgrounds. This can be
8done very efficiently with shaders.
9
10In this example we render content into a framebuffer /
11virtual screen and map that on a texture we can rotate
12in 3D.
13
14If Python and Arcade are installed, this example can be run from the command line with:
15python -m arcade.examples.perspective
16"""
17
18from array import array
19
20import arcade
21from pyglet.math import Mat4
22from arcade.gl import BufferDescription
23
24
25class Perspective(arcade.Window):
26
27 def __init__(self):
28 super().__init__(800, 600, "Perspective", resizable=True)
29 # Simple texture shader for the plane.
30 # It support projection and model matrix
31 # and a scroll value for texture coordinates
32 self.program = self.ctx.program(
33 vertex_shader="""
34 #version 330
35
36 uniform mat4 projection;
37 uniform mat4 model;
38
39 in vec3 in_pos;
40 in vec2 in_uv;
41
42 out vec2 uv;
43
44 void main() {
45 gl_Position = projection * model * vec4(in_pos, 1.0);
46 uv = in_uv;
47 }
48 """,
49 fragment_shader="""
50 #version 330
51
52 uniform sampler2D layer;
53 uniform vec2 scroll;
54
55 in vec2 uv;
56 out vec4 fragColor;
57
58 void main() {
59 fragColor = texture(layer, uv + scroll);
60 }
61 """,
62 )
63
64 # # Matrix for perspective projection
65 self.proj = Mat4.perspective_projection(self.aspect_ratio, 0.1, 100, fov=75)
66 # # Configure the projection in the shader
67 self.program["projection"] = self.proj
68
69 # Framebuffer / virtual screen to render the contents into
70 self.fbo = self.ctx.framebuffer(
71 color_attachments=self.ctx.texture(size=(1024, 1024))
72 )
73
74 # Set up the geometry buffer for the plane.
75 # This is four points with texture coordinates
76 # creating a rectangle
77 buffer = self.ctx.buffer(
78 data=array(
79 'f',
80 [
81 # x y z u v
82 -1, 1, 0, 0, 1, # Top Left
83 -1, -1, 0, 0, 0, # Bottom Left
84 1, 1, 0, 1, 1, # Top Right
85 1, -1, 0, 1, 0, # Bottom right
86 ]
87 )
88 )
89 # Make this into a geometry object we can draw-
90 # Here we describe the contents of the buffer so the shader can understand it
91 self.geometry = self.ctx.geometry(
92 content=[BufferDescription(buffer, "3f 2f", ("in_pos", "in_uv"))],
93 mode=self.ctx.TRIANGLE_STRIP,
94 )
95
96 # Create some sprites
97 self.spritelist = arcade.SpriteList()
98 for y in range(8):
99 for x in range(8):
100 self.spritelist.append(
101 arcade.Sprite(
102 ":resources:images/tiles/boxCrate_double.png",
103 center_x=64 + x * 128,
104 center_y=64 + y * 128,
105 )
106 )
107 self.time = 0
108
109 self.offscreen_cam = arcade.camera.Camera2D.from_raw_data(
110 position=(0.0, 0.0),
111 viewport=(0, 0, self.fbo.width, self.fbo.height),
112 projection=(0, self.fbo.width, 0, self.fbo.height)
113 )
114
115 def on_draw(self):
116 # Every frame we can update the offscreen texture if needed
117 self.draw_offscreen()
118 # Clear the window
119 self.clear()
120
121 # Bind the texture containing the offscreen data to channel 0
122 self.fbo.color_attachments[0].use(unit=0)
123
124 # Move the plane into camera view and rotate it
125 translate = Mat4.from_translation((0, 0, -2))
126 rotate = Mat4.from_rotation(self.time / 2, (1, 0, 0))
127 self.program["model"] = translate @ rotate
128
129 # Scroll the texture coordinates
130 self.program["scroll"] = 0, -self.time / 5
131
132 # Draw the plane
133 self.geometry.render(self.program)
134
135 def on_update(self, delta_time: float):
136 self.time += delta_time
137
138 def draw_offscreen(self):
139 """Render into the texture mapped """
140 # Activate the offscreen framebuffer and draw the sprites into it
141 with self.fbo.activate() as fbo:
142 fbo.clear()
143 self.offscreen_cam.use()
144 self.spritelist.draw()
145
146 def on_resize(self, width: int, height: int):
147 super().on_resize(width, height)
148 self.program["projection"] = Mat4.perspective_projection(self.aspect_ratio, 0.1, 100, fov=75)
149
150
151def main():
152 Perspective().run()
153
154
155if __name__ == "__main__":
156 main()