def __init__(self, width, height, gbuffer=None, lightbuffer=None): self.ctx = context.ctx() self.width = width self.height = height self.size = (width, height) self.depth_sampler = samplers.create(texture_compare_mode=False, min_filter=moderngl.LINEAR, mag_filter=moderngl.LINEAR) # FBOs self.gbuffer = gbuffer self.lightbuffer = lightbuffer # Light Info self.point_lights = [] # Create geometry buffer if not supplied depth_buffer = DepthTexture(self.size) if not self.gbuffer: self.gbuffer = FBO.create_from_textures( [ Texture2D.create(self.size, 4, dtype='f1'), Texture2D.create(self.size, 3, dtype='f2'), ], depth_buffer=depth_buffer, ) if not self.lightbuffer: self.lightbuffer = FBO.create_from_textures( [Texture2D.create(self.size, 4)], # depth_buffer=depth_buffer, ) # Unit cube for point lights (cube with radius 1.0) self.unit_cube = geometry.cube(width=2, height=2, depth=2) self.point_light_shader = resources.shaders.get( "deferred/light_point.glsl", create=True) # Debug draw lights self.debug_shader = resources.shaders.get("deferred/debug.glsl", create=True) # Combine shader self.combine_shader = resources.shaders.get("deferred/combine.glsl", create=True) self.quad = geometry.quad_fs()
def __init__(self): super().__init__() if not glfw.init(): raise ValueError("Failed to initialize glfw") self.check_glfw_version() glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, self.gl_version.major) glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, self.gl_version.minor) glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE) glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, True) glfw.window_hint(glfw.RESIZABLE, self.resizable) glfw.window_hint(glfw.DOUBLEBUFFER, True) glfw.window_hint(glfw.DEPTH_BITS, 24) monitor = None if self.fullscreen: # Use the primary monitors current resolution monitor = glfw.get_primary_monitor() mode = glfw.get_video_mode(monitor) self.width, self.height = mode.size.width, mode.size.height print("picked fullscreen mode:", mode) print("Window size:", self.width, self.height) self.window = glfw.create_window(self.width, self.height, self.title, monitor, None) if not self.window: glfw.terminate() raise ValueError("Failed to create window") if not self.cursor: glfw.set_input_mode(self.window, glfw.CURSOR, glfw.CURSOR_DISABLED) # Get the actual buffer size of the window # This is important for some displays like Apple's Retina as reported window sizes are virtual self.buffer_width, self.buffer_height = glfw.get_framebuffer_size(self.window) print("Frame buffer size:", self.buffer_width, self.buffer_height) print("Actual window size:", glfw.get_window_size(self.window)) glfw.make_context_current(self.window) # The number of screen updates to wait from the time glfwSwapBuffers # was called before swapping the buffers and returning if self.vsync: glfw.swap_interval(1) glfw.set_key_callback(self.window, self.key_event_callback) glfw.set_cursor_pos_callback(self.window, self.mouse_event_callback) glfw.set_window_size_callback(self.window, self.window_resize_callback) # Create mederngl context from existing context self.ctx = moderngl.create_context() self.fbo = FBO() self.fbo.ctx = self.ctx self.fbo.fbo = self.ctx.screen self.fbo.default_framebuffer = True context.WINDOW = self self.set_default_viewport()
def test_create_from_textures(self): fbo = FBO.create_from_textures( [ Texture2D.create((256, 256)), Texture2D.create((256, 256)), ], DepthTexture.create(((256, 256))), )
def test_read(self): fbo = FBO.create((256, 256), depth=True, layers=1) data = fbo.read(components=4) assert len(data) == 256 * 256 * 4 buffer = bytearray([0] * 256 * 256 * 4) fbo.read_into(buffer)
def _post_load(self): super()._post_load() self._texture_width = int( round( self._meta.char_aspect_wh * self._texture_height * self.area[0] / self.area[1], 0)) self.aspect_ratio = self._texture_width / self._texture_height self._fbo = FBO.create((self._texture_width, self._texture_height))
def __init__(self): self.cube_shader1 = self.get_shader('geocubes/cube_multi_fade.glsl') self.cube_shader2 = self.get_shader('geocubes/cube_texture_light.glsl') self.quad_shader = self.get_shader('geocubes/quad_fs_uvscale.glsl') self.texture1 = self.get_texture('geocubes/texture.png') self.texture2 = self.get_texture('geocubes/GreenFabric.png') self.cube = geometry.cube(4.0, 4.0, 4.0) v = 100.0 r = (-v, v) self.points = geometry.points_random_3d(50_000, range_x=r, range_y=r, range_z=r, seed=7656456) self.quad = geometry.quad_fs() self.fbo = FBO.create((512, 512), depth=True)
def test_create(self): fbo = FBO.create((256, 256), depth=True, layers=1) self.assertGeneral(fbo)
def test_stuff(self): FBO.create((10, 10), depth=True)
def __init__(self, width, height, gbuffer=None, lightbuffer=None): self.width = width self.height = height # FBOs self.gbuffer = gbuffer self.lightbuffer = lightbuffer # Light Info self.point_lights = [] # FIXME: We might want double buffering here as well # Create geometry buffer if not supplied if not self.gbuffer: self.gbuffer = FBO() # RGBA color attachment self.gbuffer.add_color_attachment( Texture.create_2d(width=width, height=height, internal_format=GL.GL_RGBA8, format=GL.GL_RGBA, min_filter=GL.GL_NEAREST, mag_filter=GL.GL_NEAREST, wrap_s=GL.GL_CLAMP_TO_EDGE, wrap_t=GL.GL_CLAMP_TO_EDGE)) # 16 bit RGB float buffer for normals self.gbuffer.add_color_attachment( Texture.create_2d(width=width, height=height, format=GL.GL_RGB, internal_format=GL.GL_RGB16F, type=GL.GL_FLOAT, min_filter=GL.GL_NEAREST, mag_filter=GL.GL_NEAREST, wrap_s=GL.GL_CLAMP_TO_EDGE, wrap_t=GL.GL_CLAMP_TO_EDGE)) # 24 bit depth, 8 bit stencil self.gbuffer.set_depth_attachment( Texture.create_2d(width=width, height=height, internal_format=GL.GL_DEPTH24_STENCIL8, format=GL.GL_DEPTH_COMPONENT, min_filter=GL.GL_NEAREST, mag_filter=GL.GL_NEAREST, wrap_s=GL.GL_CLAMP_TO_EDGE, wrap_t=GL.GL_CLAMP_TO_EDGE, wrap_r=GL.GL_CLAMP_TO_EDGE)) if not self.lightbuffer: self.lightbuffer = FBO() # 8 bit light accumulation buffer self.lightbuffer.add_color_attachment( # Texture.create_2d(width=width, height=height, # internal_format=GL.GL_RED, format=GL.GL_RED, type=GL.GL_UNSIGNED_BYTE, # min_filter=GL.GL_NEAREST, mag_filter=GL.GL_NEAREST, # wrap_s=GL.GL_CLAMP_TO_EDGE, wrap_t=GL.GL_CLAMP_TO_EDGE) Texture.create_2d(width=width, height=height, internal_format=GL.GL_RGBA8, format=GL.GL_RGBA, min_filter=GL.GL_NEAREST, mag_filter=GL.GL_NEAREST, wrap_s=GL.GL_CLAMP_TO_EDGE, wrap_t=GL.GL_CLAMP_TO_EDGE)) # Attach the same depth buffer as the geometry buffer self.lightbuffer.set_depth_attachment(self.gbuffer.depth_buffer) # Unit cube for point lights (cube with radius 1.0) self.unit_cube = geometry.cube(width=2, height=2, depth=2) self.point_light_shader = resources.shaders.get( "deferred/light_point.glsl", create=True) # Debug draw lights self.debug_shader = resources.shaders.get("deferred/debug.glsl", create=True) # Combine shader self.combine_shader = resources.shaders.get("deferred/combine.glsl", create=True) self.quad = geometry.quad_fs()
class DeferredRenderer: def __init__(self, width, height, gbuffer=None, lightbuffer=None): self.width = width self.height = height # FBOs self.gbuffer = gbuffer self.lightbuffer = lightbuffer # Light Info self.point_lights = [] # FIXME: We might want double buffering here as well # Create geometry buffer if not supplied if not self.gbuffer: self.gbuffer = FBO() # RGBA color attachment self.gbuffer.add_color_attachment( Texture.create_2d(width=width, height=height, internal_format=GL.GL_RGBA8, format=GL.GL_RGBA, min_filter=GL.GL_NEAREST, mag_filter=GL.GL_NEAREST, wrap_s=GL.GL_CLAMP_TO_EDGE, wrap_t=GL.GL_CLAMP_TO_EDGE)) # 16 bit RGB float buffer for normals self.gbuffer.add_color_attachment( Texture.create_2d(width=width, height=height, format=GL.GL_RGB, internal_format=GL.GL_RGB16F, type=GL.GL_FLOAT, min_filter=GL.GL_NEAREST, mag_filter=GL.GL_NEAREST, wrap_s=GL.GL_CLAMP_TO_EDGE, wrap_t=GL.GL_CLAMP_TO_EDGE)) # 24 bit depth, 8 bit stencil self.gbuffer.set_depth_attachment( Texture.create_2d(width=width, height=height, internal_format=GL.GL_DEPTH24_STENCIL8, format=GL.GL_DEPTH_COMPONENT, min_filter=GL.GL_NEAREST, mag_filter=GL.GL_NEAREST, wrap_s=GL.GL_CLAMP_TO_EDGE, wrap_t=GL.GL_CLAMP_TO_EDGE, wrap_r=GL.GL_CLAMP_TO_EDGE)) if not self.lightbuffer: self.lightbuffer = FBO() # 8 bit light accumulation buffer self.lightbuffer.add_color_attachment( # Texture.create_2d(width=width, height=height, # internal_format=GL.GL_RED, format=GL.GL_RED, type=GL.GL_UNSIGNED_BYTE, # min_filter=GL.GL_NEAREST, mag_filter=GL.GL_NEAREST, # wrap_s=GL.GL_CLAMP_TO_EDGE, wrap_t=GL.GL_CLAMP_TO_EDGE) Texture.create_2d(width=width, height=height, internal_format=GL.GL_RGBA8, format=GL.GL_RGBA, min_filter=GL.GL_NEAREST, mag_filter=GL.GL_NEAREST, wrap_s=GL.GL_CLAMP_TO_EDGE, wrap_t=GL.GL_CLAMP_TO_EDGE)) # Attach the same depth buffer as the geometry buffer self.lightbuffer.set_depth_attachment(self.gbuffer.depth_buffer) # Unit cube for point lights (cube with radius 1.0) self.unit_cube = geometry.cube(width=2, height=2, depth=2) self.point_light_shader = resources.shaders.get( "deferred/light_point.glsl", create=True) # Debug draw lights self.debug_shader = resources.shaders.get("deferred/debug.glsl", create=True) # Combine shader self.combine_shader = resources.shaders.get("deferred/combine.glsl", create=True) self.quad = geometry.quad_fs() def draw_buffers(self, near, far): """ Draw framebuffers for debug purposes. We need to supply near and far plane so the depth buffer can be linearized when visualizing. :param near: Projection near value :param far: Projection far value """ self.gbuffer.draw_color_layer(layer=0, pos=(0.0, 0.0), scale=(0.25, 0.25)) self.gbuffer.draw_color_layer(layer=1, pos=(0.5, 0.0), scale=(0.25, 0.25)) self.gbuffer.draw_depth(near, far, pos=(1.0, 0.0), scale=(0.25, 0.25)) self.lightbuffer.draw_color_layer(layer=0, pos=(1.5, 0.0), scale=(0.25, 0.25)) def add_point_light(self, position, radius): """Add point light""" self.point_lights.append(PointLight(position, radius)) def render_lights(self, camera_matrix, projection): """Render light volumes""" # Disable culling so lights can be rendered when inside volumes GL.glEnable(GL.GL_CULL_FACE) GL.glFrontFace(GL.GL_CW) # No depth testing GL.glDisable(GL.GL_DEPTH_TEST) # Enable additive blending GL.glEnable(GL.GL_BLEND) GL.glBlendFunc(GL.GL_ONE, GL.GL_ONE) with self.lightbuffer: for light in self.point_lights: # Calc light properties light_size = light.radius m_light = matrix44.multiply(light.matrix, camera_matrix) # Draw the light volume with self.unit_cube.bind(self.point_light_shader) as s: s.uniform_mat4("m_proj", projection.matrix) s.uniform_mat4("m_light", m_light) s.uniform_sampler_2d(0, "g_normal", self.gbuffer.color_buffers[1]) s.uniform_sampler_2d(1, "g_depth", self.gbuffer.depth_buffer) s.uniform_2f("screensize", self.width, self.height) s.uniform_2f("proj_const", *projection.projection_constants) s.uniform_1f("radius", light_size) self.unit_cube.draw() GL.glDisable(GL.GL_BLEND) GL.glDisable(GL.GL_CULL_FACE) def render_lights_debug(self, camera_matrix, projection): """Render outlines of light volumes""" GL.glEnable(GL.GL_BLEND) GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA) for light in self.point_lights: m_mv = matrix44.multiply(light.matrix, camera_matrix) light_size = light.radius with self.unit_cube.bind(self.debug_shader) as s: s.uniform_mat4("m_proj", projection.matrix) s.uniform_mat4("m_mv", m_mv) s.uniform_1f("size", light_size) self.unit_cube.draw(GL.GL_LINE_STRIP) GL.glDisable(GL.GL_BLEND) def render_geometry(self, cam_matrix, projection): raise NotImplementedError("render_geometry() not implemented") def combine(self): """Combine diffuse and light buffer""" with self.quad.bind(self.combine_shader) as s: s.uniform_sampler_2d(0, "diffuse_buffer", self.gbuffer.color_buffers[0]) s.uniform_sampler_2d(1, "light_buffer", self.lightbuffer.color_buffers[0]) self.quad.draw() def clear(self): """clear all buffers""" self.gbuffer.clear() self.lightbuffer.clear()