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
|
"""
Shadow mapping example from:
https://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/
"""
import math
from pathlib import Path
from pyrr import Matrix44, matrix44, Vector3
import moderngl
import moderngl_window
from moderngl_window import geometry
from base import CameraWindow
class ShadowMapping(CameraWindow):
title = "Shadow Mapping"
resource_dir = (Path(__file__) / '../../resources').resolve()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.camera.projection.update(near=1, far=200)
self.wnd.mouse_exclusivity = True
# Offscreen buffer
offscreen_size = 1024, 1024
self.offscreen_depth = self.ctx.depth_texture(offscreen_size)
self.offscreen_depth.compare_func = ''
self.offscreen_depth.repeat_x = False
self.offscreen_depth.repeat_y = False
# Less ugly by default with linear. May need to be NEAREST for some techniques
self.offscreen_depth.filter = moderngl.LINEAR, moderngl.LINEAR
self.offscreen = self.ctx.framebuffer(
depth_attachment=self.offscreen_depth,
)
# Scene geometry
self.floor = geometry.cube(size=(25.0, 1.0, 25.0))
self.wall = geometry.cube(size=(1.0, 5, 25), center=(-12.5, 2, 0))
self.sphere = geometry.sphere(radius=5.0, sectors=64, rings=32)
self.sun = geometry.sphere(radius=1.0)
# Debug geometry
self.offscreen_quad = geometry.quad_2d(size=(0.5, 0.5), pos=(0.75, 0.75))
self.offscreen_quad2 = geometry.quad_2d(size=(0.5, 0.5), pos=(0.25, 0.75))
# Programs
self.raw_depth_prog = self.load_program('programs/shadow_mapping/raw_depth.glsl')
self.basic_light = self.load_program('programs/shadow_mapping/directional_light.glsl')
self.basic_light['shadowMap'].value = 0
self.basic_light['color'].value = 1.0, 1.0, 1.0, 1.0
self.shadowmap_program = self.load_program('programs/shadow_mapping/shadowmap.glsl')
self.texture_prog = self.load_program('programs/texture.glsl')
self.texture_prog['texture0'].value = 0
self.sun_prog = self.load_program('programs/cube_simple.glsl')
self.sun_prog['color'].value = 1, 1, 0, 1
self.lightpos = 0, 0, 0
def render(self, time, frametime):
self.ctx.enable_only(moderngl.DEPTH_TEST | moderngl.CULL_FACE)
self.lightpos = Vector3((math.sin(time) * 20, 5, math.cos(time) * 20), dtype='f4')
scene_pos = Vector3((0, -5, -32), dtype='f4')
# --- PASS 1: Render shadow map
self.offscreen.clear()
self.offscreen.use()
depth_projection = Matrix44.orthogonal_projection(-20, 20, -20, 20, -20, 40, dtype='f4')
depth_view = Matrix44.look_at(self.lightpos, (0, 0, 0), (0, 1, 0), dtype='f4')
depth_mvp = depth_projection * depth_view
self.shadowmap_program['mvp'].write(depth_mvp)
self.floor.render(self.shadowmap_program)
self.wall.render(self.shadowmap_program)
self.sphere.render(self.shadowmap_program)
# --- PASS 2: Render scene to screen
self.wnd.use()
self.basic_light['m_proj'].write(self.camera.projection.matrix)
self.basic_light['m_camera'].write(self.camera.matrix)
self.basic_light['m_model'].write(Matrix44.from_translation(scene_pos, dtype='f4'))
bias_matrix = Matrix44(
[[0.5, 0.0, 0.0, 0.0],
[0.0, 0.5, 0.0, 0.0],
[0.0, 0.0, 0.5, 0.0],
[0.5, 0.5, 0.5, 1.0]],
dtype='f4',
)
self.basic_light['m_shadow_bias'].write(matrix44.multiply(depth_mvp, bias_matrix))
self.basic_light['lightDir'].write(self.lightpos)
self.offscreen_depth.use(location=0)
self.floor.render(self.basic_light)
self.wall.render(self.basic_light)
self.sphere.render(self.basic_light)
# Render the sun position
self.sun_prog['m_proj'].write(self.camera.projection.matrix)
self.sun_prog['m_camera'].write(self.camera.matrix)
self.sun_prog['m_model'].write(Matrix44.from_translation(self.lightpos + scene_pos, dtype='f4'))
self.sun.render(self.sun_prog)
# --- PASS 3: Debug ---
# self.ctx.enable_only(moderngl.NOTHING)
self.offscreen_depth.use(location=0)
self.offscreen_quad.render(self.raw_depth_prog)
# self.offscreen_color.use(location=0)
# self.offscreen_quad2.render(self.texture_prog)
if __name__ == '__main__':
moderngl_window.run_window_config(ShadowMapping)
|