class Viewer: def __init__(self): self.width = 1024 self.height = 768 self.window = GLFWViewer(self.width, self.height, (0.2, 0.2, 0.2, 1.0)) self.matcap_img = assets.imread("matcap/jeepster_skinmat2.jpg").astype( np.float32) / 255 # self.matcap_img = assets.imread("container2_axis.png").astype(np.float32)[...,[0,1,2]]/255 self.matcap_img = np.flip(self.matcap_img, 0) def setup(self): glEnable(GL_DEPTH_TEST) glEnable(GL_CULL_FACE) glEnable(GL_PROGRAM_POINT_SIZE) self.prog = program.create(*glsl.read("matcap")) self.matcap_tex = texture.create(self.matcap_img, 0, GL_RGB) def resize(self): with self.window: pass def draw(self): GLFWViewer.poll_events() glViewport(0, 0, self.width, self.height) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) with program.use(self.prog) as prog: program.set_uniform(prog, 'projectionMatrix', self.window.projection_matrix) program.set_uniform(prog, 'viewMatrix', self.window.view_matrix) program.set_uniform(prog, 'modelMatrix', np.eye(4)) glBindTexture(GL_TEXTURE_2D, self.matcap_tex) program.set_uniform(prog, 'matCap', 0) imdraw.torusknot(prog) self.window.swap_buffers() def start(self): with self.window: self.setup() while not self.window.should_close(): self.draw()
import glm import logging logging.basicConfig( filename=None, level=logging.DEBUG, format='%(levelname)s:%(module)s.%(funcName)s: %(message)s') width, height = 1024, 768 model_matrix = np.identity(4) window = GLFWViewer(width, height, (0.6, 0.7, 0.7, 1.0)) with window: glEnable(GL_DEPTH_TEST) glEnable(GL_CULL_FACE) while not window.should_close(): glViewport(0, 0, window.width, window.height) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) with program.use(skybox_program): program.set_uniform(skybox_program, 'projection', window.projection_matrix) sky_view = glm.mat4(glm.mat3(window.view_matrix)) program.set_uniform(skybox_program, 'view', sky_view) camera_pos = glm.transpose( glm.transpose(glm.inverse(window.view_matrix)))[3].xyz program.set_uniform(skybox_program, 'cameraPos', camera_pos) program.set_uniform(skybox_program, 'skybox', 0) program.set_uniform(skybox_program, 'groundProjection', True) glActiveTexture(GL_TEXTURE0 + 0) glBindTexture(GL_TEXTURE_CUBE_MAP, env_cubemap) imdraw.cube(skybox_program, flip=True)
class Viewer: def __init__(self, scene): # window self.width = 1024 self.height = 768 self.window = GLFWViewer(self.width, self.height, (0.2, 0.2, 0.2, 1.0)) self.camera = PerspectiveCamera(glm.inverse(self.window.view_matrix), glm.radians(60), self.width / self.height, 1, 30) self.scene = scene # assets self.environment_image = assets.imread('hdri/Tropical_Beach_3k.hdr') self.environment_image = assets.to_linear(self.environment_image) # render passes self.environment_pass = EnvironmentPass(512, 512) self.irradiance_pass = IrradiancePass(32, 32) self.prefilter_pass = PrefilterPass() self.brdf_pass = BRDFPass(512, 512) self.tonemapping_pass = TonemappingPass(self.width, self.height) self.geometry_pass = GeometryPass(self.width, self.height, self.draw_scene_for_geometry) dirlight.shadowpass = DepthPass(1024, 1024, GL_FRONT, self.draw_scene_for_shadows) spotlight.shadowpass = DepthPass(1024, 1024, GL_FRONT, self.draw_scene_for_shadows) pointlight.shadowpass = CubeDepthPass( 512, 512, GL_FRONT, near=1, far=15, draw_scene=self.draw_scene_for_shadows) self.lighting_pass = LightingPass( self.width, self.height, lights=[dirlight, spotlight, pointlight]) def get_geometry_buffer(self, mesh): try: geometry_buffers = self._geometry_buffers except AttributeError: geometry_buffers = dict() self._geometry_buffers = geometry_buffers try: geo_buffer = geometry_buffers[mesh] except KeyError: logging.debug("create goemetry buffer for {}".format(mesh)) positions = mesh.geometry.positions normals = mesh.geometry.normals uvs = mesh.geometry.uvs indices = mesh.geometry.indices # create vertex buffers pos_vbo, uv_vbo, normal_vbo = glGenBuffers(3) glBindBuffer(GL_ARRAY_BUFFER, pos_vbo) glBufferData(GL_ARRAY_BUFFER, positions.nbytes, positions, GL_STATIC_DRAW) glBindBuffer(GL_ARRAY_BUFFER, uv_vbo) glBufferData(GL_ARRAY_BUFFER, uvs.nbytes, uvs, GL_STATIC_DRAW) glBindBuffer(GL_ARRAY_BUFFER, normal_vbo) glBufferData(GL_ARRAY_BUFFER, normals.nbytes, normals, GL_STATIC_DRAW) glBindBuffer(GL_ARRAY_BUFFER, 0) # create vertex array vao = glGenVertexArrays(1) # create element buffer ebo = glGenBuffers(1) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo) glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.nbytes, indices, GL_STATIC_DRAW) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) # link buffer to geometry geo_buffer = (vao, ebo, pos_vbo, uv_vbo, normal_vbo, indices.size) geometry_buffers[mesh] = geo_buffer return geo_buffer def draw_scene_for_geometry(self, prog): import ctypes for child in self.scene.children: # set uniforms program.set_uniform(prog, 'model', child.transform) program.set_uniform(prog, 'albedo', child.material.albedo) program.set_uniform(prog, 'roughness', child.material.roughness) program.set_uniform(prog, 'metallic', child.material.metallic) # set attributes vao, ebo, pos_vbo, uv_vbo, normal_vbo, count = self.get_geometry_buffer( child) glBindVertexArray(vao) position_location = glGetAttribLocation(prog, 'position') glBindBuffer(GL_ARRAY_BUFFER, pos_vbo) glVertexAttribPointer(position_location, 3, GL_FLOAT, False, 0, ctypes.c_void_p(0)) glEnableVertexAttribArray(position_location) uv_location = glGetAttribLocation(prog, 'uv') if uv_location is not -1: glBindBuffer(GL_ARRAY_BUFFER, uv_vbo) glVertexAttribPointer(uv_location, 2, GL_FLOAT, False, 0, ctypes.c_void_p(0)) glEnableVertexAttribArray(uv_location) normal_location = glGetAttribLocation(prog, 'normal') if normal_location is not -1: glBindBuffer(GL_ARRAY_BUFFER, normal_vbo) glVertexAttribPointer(normal_location, 3, GL_FLOAT, False, 0, ctypes.c_void_p(0)) glEnableVertexAttribArray(normal_location) # draw geometry glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo) glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, None) # cleanup bindings glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) glBindBuffer(GL_ARRAY_BUFFER, 0) glBindVertexArray(0) def draw_scene_for_shadows(self, prog): for child in self.scene.children: # set uniforms program.set_uniform(prog, 'model', child.transform) # set attributes vao, ebo, pos_vbo, uv_vbo, normal_vbo, count = self.get_geometry_buffer( child) glBindVertexArray(vao) position_location = glGetAttribLocation(prog, 'position') glBindBuffer(GL_ARRAY_BUFFER, pos_vbo) glVertexAttribPointer(position_location, 3, GL_FLOAT, False, 0, ctypes.c_void_p(0)) glEnableVertexAttribArray(position_location) # draw geometry glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo) glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, None) # cleanup bindings glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) glBindBuffer(GL_ARRAY_BUFFER, 0) glBindVertexArray(0) def setup(self): logging.debug("setup viewer") # Render passes # ------------- # geometry self.geometry_pass.setup() # environment self.environment_tex = texture.create(self.environment_image, 0, format=GL_RGB, wrap_s=GL_CLAMP_TO_EDGE, wrap_t=GL_CLAMP_TO_EDGE, min_filter=GL_LINEAR, mag_filter=GL_LINEAR) self.environment_pass.texture = self.environment_tex self.environment_pass.setup() self.environment_pass.render() # ibl self.prefilter_pass.setup() self.prefilter_pass.environment = self.environment_pass.cubemap self.prefilter_pass.render() self.irradiance_pass.setup() self.irradiance_pass.environment = self.environment_pass.cubemap self.irradiance_pass.render() self.brdf_pass.setup() self.brdf_pass.render() # shadows dirlight.shadowpass.setup() spotlight.shadowpass.setup() pointlight.shadowpass.setup() # lighting self.lighting_pass.setup() self.lighting_pass.irradiance = self.irradiance_pass.irradiance self.lighting_pass.prefilter = self.prefilter_pass.prefilter self.lighting_pass.brdf = self.brdf_pass.brdflut # setup forward rendering self.forward_fbo = glGenFramebuffers(1) with fbo.bind(self.forward_fbo): glDrawBuffers(1, [GL_COLOR_ATTACHMENT0 + 0]) self.forward_texture = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, self.forward_texture) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, self.width, self.height, 0, GL_RGBA, GL_FLOAT, None) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self.forward_texture, 0) glBindTexture(GL_TEXTURE_2D, 0) # create depth+stencil buffer rbo = glGenRenderbuffers(1) glBindRenderbuffer(GL_RENDERBUFFER, rbo) glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, self.width, self.height) glBindRenderbuffer(GL_RENDERBUFFER, 0) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo) assert glCheckFramebufferStatus( GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE self.skybox_program = program.create(*glsl.read('skybox')) # tonemapping self.tonemapping_pass.setup() def resize(self): with self.window: pass def render(self): # Animate # ------- import math, time spotlight.position = glm.vec3(math.cos(time.time() * 3) * 4, 0.3, -4) spotlight.direction = -spotlight.position pointlight.position = glm.vec3( math.cos(time.time()) * 4, 4, math.sin(time.time()) * 4) self.camera.transform = glm.inverse(self.window.view_matrix) # Render passes # ------------- ## Geometry self.geometry_pass.camera = self.camera # window.projection_matrix, window.view_matrix self.geometry_pass.render() ## Shadowmaps dirlight.shadowpass.camera = dirlight.camera dirlight.shadowpass.render() spotlight.shadowpass.camera = spotlight.camera spotlight.shadowpass.render() pointlight.shadowpass.position = pointlight.position pointlight.shadowpass.render() ## Lighting self.lighting_pass.cameraPos = self.camera.position self.lighting_pass.gPosition = self.geometry_pass.gPosition self.lighting_pass.gNormal = self.geometry_pass.gNormal self.lighting_pass.gAlbedoSpecular = self.geometry_pass.gAlbedoSpecular self.lighting_pass.gRoughness = self.geometry_pass.gRoughness self.lighting_pass.gMetallic = self.geometry_pass.gMetallic self.lighting_pass.gEmissive = self.geometry_pass.gEmissive self.lighting_pass.render() ## Forward rendering # - Copy depth from geometry pass glBindFramebuffer(GL_READ_FRAMEBUFFER, self.geometry_pass.gBuffer) glBindFramebuffer(GL_DRAW_FRAMEBUFFER, self.forward_fbo) # write to default framebuffer glBlitFramebuffer(0, 0, self.width, self.height, 0, 0, self.width, self.height, GL_DEPTH_BUFFER_BIT, GL_NEAREST) glBindFramebuffer(GL_READ_FRAMEBUFFER, self.lighting_pass.fbo) glBlitFramebuffer(0, 0, self.width, self.height, 0, 0, self.width, self.height, GL_COLOR_BUFFER_BIT, GL_NEAREST) glBindFramebuffer(GL_FRAMEBUFFER, 0) glEnable(GL_DEPTH_TEST) glDepthFunc(GL_LEQUAL) glDepthMask(GL_FALSE) with fbo.bind(self.forward_fbo): # draw skybox glViewport(0, 0, self.width, self.height) with program.use(self.skybox_program) as prog: program.set_uniform(prog, 'projection', self.camera.projection) sky_view = glm.mat4(glm.mat3(self.camera.view)) program.set_uniform(prog, 'view', sky_view) program.set_uniform(prog, 'cameraPos', self.camera.position) program.set_uniform(prog, 'skybox', 0) program.set_uniform(prog, 'groundProjection', True) glActiveTexture(GL_TEXTURE0 + 0) glBindTexture(GL_TEXTURE_CUBE_MAP, self.environment_pass.cubemap) imdraw.cube(prog, flip=True) glBindTexture(GL_TEXTURE_CUBE_MAP, 0) glDepthMask(GL_TRUE) ## Tonemapping self.tonemapping_pass.hdrimage = self.lighting_pass.beauty self.tonemapping_pass.exposure = 0.0 self.tonemapping_pass.gamma = 2.2 self.tonemapping_pass.render() # Render to screen # ---------------- glViewport(0, 0, self.width, self.height) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glDisable(GL_DEPTH_TEST) # draw to screen imdraw.texture(self.tonemapping_pass.ldrimage, (0, 0, self.width, self.height)) # debug imdraw.texture(self.geometry_pass.gPosition, (0, 0, 90, 90)) imdraw.texture(self.geometry_pass.gNormal, (100, 0, 90, 90)) imdraw.texture(self.geometry_pass.gAlbedoSpecular, (200, 0, 90, 90), shuffle=(0, 1, 2, -1)) imdraw.texture(self.geometry_pass.gAlbedoSpecular, (300, 0, 90, 90), shuffle=(3, 3, 3, -1)) imdraw.texture(self.geometry_pass.gRoughness, (400, 0, 90, 90), shuffle=(0, 0, 0, -1)) imdraw.texture(self.geometry_pass.gMetallic, (500, 0, 90, 90), shuffle=(0, 0, 0, -1)) imdraw.texture(dirlight.shadowpass.texture, (0, 100, 90, 90), shuffle=(0, 0, 0, -1)) imdraw.texture(spotlight.shadowpass.texture, (100, 100, 90, 90), shuffle=(0, 0, 0, -1)) imdraw.cubemap(pointlight.shadowpass.cubemap, (200, 100, 90, 90), self.window.projection_matrix, self.window.view_matrix) imdraw.texture(self.environment_tex, (0, 200, 90, 90)) imdraw.cubemap(self.environment_pass.cubemap, (100, 200, 90, 90), self.window.projection_matrix, self.window.view_matrix) imdraw.cubemap(self.irradiance_pass.irradiance, (200, 200, 90, 90), self.window.projection_matrix, self.window.view_matrix) imdraw.cubemap(self.prefilter_pass.prefilter, (300, 200, 90, 90), self.window.projection_matrix, self.window.view_matrix) imdraw.texture(self.brdf_pass.brdflut, (400, 200, 90, 90)) imdraw.texture(self.lighting_pass.beauty, (0, 300, 90, 90)) # swap buffers # ------------ self.window.swap_buffers() GLFWViewer.poll_events() def start(self): with self.window: self.setup() while not self.window.should_close(): self.render()