File: shadow_mapping_example.py

package info (click to toggle)
python-moderngl 5.12.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 4,700 kB
  • sloc: python: 15,758; cpp: 14,665; makefile: 14
file content (355 lines) | stat: -rw-r--r-- 9,742 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
# Example of shadow rendering (shadow mapping)
# Description: This example shows how you can draw a shadow
#              from a single light source.
# Note: The left-hand rule coordinate system is used.

import moderngl as gl
import moderngl_window as glw
import struct
import math


# Create window
window_cls = glw.get_local_window_cls('pyglet')
window = window_cls(
    size=(1024, 512), fullscreen=False,
    title='Shadow rendering (shadow mapping) example',
    resizable=False, vsync=True, gl_version=(3, 3)
)
ctx = window.ctx
glw.activate_context(window, ctx=ctx)
window.clear()
window.swap_buffers()

# Window program
window_program = ctx.program(
    vertex_shader='''
    #version 330

    in vec3 in_vert;
    in vec4 in_color;

    uniform float camera_aspect_ratio;
    uniform vec3 camera_position;
    uniform mat3 camera_rotation_matrix;
    uniform mat4 camera_projection_matrix;

    out vec3 o_vert;
    out vec4 o_color;

    void main() {
        vec3 new_position = camera_rotation_matrix*(in_vert-camera_position);
        gl_Position = camera_projection_matrix*vec4(
            new_position.x, new_position.y*camera_aspect_ratio,
            new_position.z, 1.0
        );

        o_vert = new_position;
        o_color = in_color;
    }
    ''',
    fragment_shader='''
    #version 330

    in vec3 o_vert;
    in vec4 o_color;

    uniform float light_aspect_ratio;
    uniform vec2 light_clip_space;
    uniform float light_perspective;
    uniform vec3 light_position;
    uniform mat3 light_rotation_matrix;

    uniform vec3 camera_position;
    uniform mat3 camera_revers_rotation_matrix;

    uniform float shadow_bias;

    uniform sampler2D light_depthI;

    out vec4 f_color;

    void main() {
        f_color = o_color;

        // Camera pixels position //
        vec3 pixel_coords = o_vert;
        ////////////

        // ROTATION 3D PROG moving prog //
        // Convert to light pixel coords //
        pixel_coords = camera_revers_rotation_matrix*
              pixel_coords;
        pixel_coords += camera_position;
        vec3 pixel_world_coords = pixel_coords;  // Pixel world coordinates
        pixel_coords -= light_position;
        pixel_coords = light_rotation_matrix*
              pixel_coords;
        /////////////////

        // Convert .xy to 2D coords //
        float distance = length(pixel_coords);
        // ^^ From light to pixel 3D distance ^^

        pixel_coords.y*=light_aspect_ratio;
        pixel_coords.xy = vec2(pixel_coords.xy/
                (pixel_coords.z+(1.0-pixel_coords.z)*(1.0-light_perspective))+1.0)/2.0;
        ///////////


        // Get depth
        float depth = texture(light_depthI, pixel_coords.xy).x;
        depth = 2.0 * depth - 1.0;

        vec2 lcs = light_clip_space;

        depth = (
            (2.0*lcs.x*lcs.y/(lcs.y+lcs.x-depth*(lcs.y-lcs.x)))*
                light_perspective+
            ((depth+(lcs.y+lcs.x)/(lcs.y-lcs.x))*(lcs.y - lcs.x))/2*
                (1.0-light_perspective)
        );

        // Compare depth
        if (!(
             pixel_coords.x > 0.0 && pixel_coords.y > 0.0
            && pixel_coords.x < 1.0 && pixel_coords.y < 1.0
            && pixel_coords.z > 0.0
            && pixel_coords.z < depth+shadow_bias
        )) {
            f_color.xyz = f_color.xyz*0.3;
        }
    }
    '''
)

# Light program
light_program = ctx.program(
    vertex_shader='''
    #version 330

    in vec3 in_vert;
    in vec4 in_color;

    uniform float light_aspect_ratio;
    uniform vec3 light_position;
    uniform mat3 light_rotation_matrix;
    uniform mat4 light_projection_matrix;

    out vec4 o_color;

    void main() {
        vec3 new_position = light_rotation_matrix*(in_vert-light_position);
        gl_Position = light_projection_matrix*vec4(
            new_position.x, new_position.y*light_aspect_ratio,
            new_position.z, 1.0
        );

        o_color = in_color;
    }
    ''',
    fragment_shader='''
    #version 330

    in vec4 o_color;
    out vec4 f_color;

    void main() {
        f_color = o_color;
    }
    '''
)

# Preparing scene
vertex = [
    -.5, -.1, -.5,
    -.5, -.1, .5,
    .5, -.1, .5,

    .5, -.1, -.5,
    .5, -.1, .5,
    -.5, -.1, -.5,

    .2, -.1, -.2,
    .2, -.1, .2,
    -.2, .05, .2,
]
vertex_buf = ctx.buffer(struct.pack(f'{len(vertex)}f', *vertex))

colors = [
    .5, .5, .5, 1,
    .5, .5, .5, 1,
    .5, .5, .5, 1,

    .5, .5, .5, 1,
    .5, .5, .5, 1,
    .5, .5, .5, 1,

    1, 0, 0, 1,
    0, 1, 0, 1,
    0, 0, 1, 1,
]
colors_buf = ctx.buffer(struct.pack(f'{len(colors)}f', *colors))

# Prepair render
window_vao = ctx.vertex_array(window_program, [
    (vertex_buf, '3f', 'in_vert'),
    (colors_buf, '4f', 'in_color')
])
window_fbo = ctx.framebuffer(
    color_attachments=[ctx.texture(window.size, 4)],
    depth_attachment=ctx.depth_texture(window.size)
)
window_fbo.color_attachments[0].repeat_x = False
window_fbo.color_attachments[0].repeat_y = False
window_fbo.depth_attachment.repeat_x = False
window_fbo.depth_attachment.repeat_y = False
window_fbo.depth_attachment.compare_func = ''


light_vao = ctx.vertex_array(light_program, [
    (vertex_buf, '3f', 'in_vert'),
    (colors_buf, '4f', 'in_color')
])
light_fbo = ctx.framebuffer(
    color_attachments=[ctx.texture(window.size, 4)],
    depth_attachment=ctx.depth_texture(window.size)
)
light_fbo.color_attachments[0].repeat_x = False
light_fbo.color_attachments[0].repeat_y = False
light_fbo.depth_attachment.repeat_x = False
light_fbo.depth_attachment.repeat_y = False
light_fbo.depth_attachment.filter = gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR
light_fbo.depth_attachment.compare_func = ''


# Define matrix methods
def get_projection_matrix(near, far, perspective=1):
    perspective_projection = (
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, (far+near)/(far-near), 1,
        0, 0, -(2*far*near)/(far-near), 0
    )
    orthographic_projection = (
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 2/(far-near), 0,
        0, 0, -(far+near)/(far-near), 1
    )

    result_projection = []
    for x, y in zip(perspective_projection, orthographic_projection):
        result_projection.append(x*perspective+y*(1-perspective))

    return result_projection


def get_rotation_matrix(rx, ry, rz, revers=True):
    rx *= (math.pi / 180)
    ry *= (math.pi / 180)
    rz *= (math.pi / 180)
    if not revers:
        srx, crx = math.sin(rx), math.cos(rx)
        sry, cry = math.sin(-ry), math.cos(-ry)
        srz, crz = math.sin(-rz), math.cos(-rz)
        rotation_matrix = (
            cry * crz - sry * srx * srz,
            cry * srz + sry * srx * crz,
            -sry * crx,

            -crx * srz,
            crx * crz,
            srx,

            sry * crz + cry * srx * srz,
            sry * srz - cry * srx * crz,
            cry * crx
        )
    else:
        srx, crx = math.sin(-rx), math.cos(-rx)
        sry, cry = math.sin(ry), math.cos(ry)
        srz, crz = math.sin(rz), math.cos(rz)
        rotation_matrix = (
            crz * cry + srz * srx * sry,
            srz * crx,
            srz * srx * cry - crz * sry,

            crz * srx * sry - srz * cry,
            crz * crx,
            srz * sry + crz * srx * cry,

            crx * sry,
            -srx,
            crx * cry
        )

    return rotation_matrix


#####################################################################
# Define constants
far = 10  # Camera projection far
near = 0.001  # Camera projection near
perpective = 1  # Select perpective/orthographic projection
position = [0., 0.1, -0.4]  # Camera position
rotate = [-20, 0, 0]  # Camera angle

light_far = 10  # Light projection far
light_near = 0.001  # Light projection near
light_perspective = 0  # Select perpective/orthographic projection
light_position = [0, 0.4, 0]  # Light position
light_rotate = [-90, 0, 0]  # Light angle

shadow_bias = 0.001  # Allowed shadow offset

view_as_light = False  # Show what the light sees
#####################################################################

# Filling uniforms
window_vao.program['camera_aspect_ratio'] = \
    window_fbo.size[0]/window_fbo.size[1]
window_vao.program['camera_position'] = position
window_vao.program['camera_projection_matrix'] = \
    get_projection_matrix(near, far, perpective)
window_vao.program['camera_rotation_matrix'] = \
    get_rotation_matrix(*rotate, revers=False)
window_vao.program['light_aspect_ratio'] = light_fbo.size[0]/light_fbo.size[1]
window_vao.program['light_clip_space'] = light_near, light_far
window_vao.program['light_perspective'] = light_perspective
window_vao.program['light_position'] = light_position
window_vao.program['light_rotation_matrix'] = \
    get_rotation_matrix(*light_rotate, revers=False)
window_vao.program['camera_revers_rotation_matrix'] = \
    get_rotation_matrix(*rotate, revers=True)

light_vao.program['light_aspect_ratio'] = light_fbo.size[0]/light_fbo.size[1]
light_vao.program['light_position'] = light_position
light_vao.program['light_projection_matrix'] = \
    get_projection_matrix(light_near, light_far, light_perspective)
light_vao.program['light_rotation_matrix'] = \
    get_rotation_matrix(*light_rotate, revers=False)

window_vao.program['shadow_bias'] = shadow_bias


# Render loop
ctx.enable(gl.DEPTH_TEST)
ctx.depth_func = '<'

while not window.is_closing:
    light_fbo.use()
    light_fbo.clear()
    light_vao.render(vertices=vertex_buf.size // 4 // 3)

    window_fbo.use()
    window_fbo.clear()
    light_fbo.depth_attachment.use(location=0)
    window_vao.render(vertices=vertex_buf.size // 4 // 3)

    ctx.copy_framebuffer(
        window.fbo,
        light_fbo if view_as_light else window_fbo
    )

    window.swap_buffers()