from ctypes import * import numpy as np from .framework import * GLUT = None # NOTE: Render class assumes GL context is created already. class Render: def __init__(self, width=1600, height=1200, name='GL Renderer', program_files=['simple.fs', 'simple.vs'], color_size=1, ms_rate=1, egl=False): self.width = width self.height = height self.name = name self.use_inverse_depth = False self.egl = egl glEnable(GL_DEPTH_TEST) glClampColor(GL_CLAMP_READ_COLOR, GL_FALSE) glClampColor(GL_CLAMP_FRAGMENT_COLOR, GL_FALSE) glClampColor(GL_CLAMP_VERTEX_COLOR, GL_FALSE) # init program shader_list = [] for program_file in program_files: _, ext = os.path.splitext(program_file) if ext == '.vs': shader_list.append(loadShader(GL_VERTEX_SHADER, program_file)) elif ext == '.fs': shader_list.append(loadShader(GL_FRAGMENT_SHADER, program_file)) elif ext == '.gs': shader_list.append(loadShader(GL_GEOMETRY_SHADER, program_file)) self.program = createProgram(shader_list) for shader in shader_list: glDeleteShader(shader) # Init uniform variables self.model_mat_unif = glGetUniformLocation(self.program, 'ModelMat') self.persp_mat_unif = glGetUniformLocation(self.program, 'PerspMat') self.vertex_buffer = glGenBuffers(1) # Init screen quad program and buffer self.quad_program, self.quad_buffer = self.init_quad_program() # Configure frame buffer self.frame_buffer = glGenFramebuffers(1) glBindFramebuffer(GL_FRAMEBUFFER, self.frame_buffer) self.intermediate_fbo = None if ms_rate > 1: # Configure texture buffer to render to self.color_buffer = [] for i in range(color_size): color_buffer = glGenTextures(1) multi_sample_rate = ms_rate glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, color_buffer) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, multi_sample_rate, GL_RGBA32F, self.width, self.height, GL_TRUE) glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D_MULTISAMPLE, color_buffer, 0) self.color_buffer.append(color_buffer) self.render_buffer = glGenRenderbuffers(1) glBindRenderbuffer(GL_RENDERBUFFER, self.render_buffer) glRenderbufferStorageMultisample(GL_RENDERBUFFER, multi_sample_rate, GL_DEPTH24_STENCIL8, self.width, self.height) glBindRenderbuffer(GL_RENDERBUFFER, 0) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, self.render_buffer) attachments = [] for i in range(color_size): attachments.append(GL_COLOR_ATTACHMENT0 + i) glDrawBuffers(color_size, attachments) glBindFramebuffer(GL_FRAMEBUFFER, 0) self.intermediate_fbo = glGenFramebuffers(1) glBindFramebuffer(GL_FRAMEBUFFER, self.intermediate_fbo) self.screen_texture = [] for i in range(color_size): screen_texture = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, screen_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 + i, GL_TEXTURE_2D, screen_texture, 0) self.screen_texture.append(screen_texture) glDrawBuffers(color_size, attachments) glBindFramebuffer(GL_FRAMEBUFFER, 0) else: self.color_buffer = [] for i in range(color_size): color_buffer = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, color_buffer) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, self.width, self.height, 0, GL_RGBA, GL_FLOAT, None) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, color_buffer, 0) self.color_buffer.append(color_buffer) # Configure depth texture map to render to self.depth_buffer = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, self.depth_buffer) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL) glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, self.width, self.height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, None) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, self.depth_buffer, 0) attachments = [] for i in range(color_size): attachments.append(GL_COLOR_ATTACHMENT0 + i) glDrawBuffers(color_size, attachments) self.screen_texture = self.color_buffer glBindFramebuffer(GL_FRAMEBUFFER, 0) # Configure texture buffer if needed self.render_texture = None # NOTE: original render_texture only support one input # this is tentative member of this issue self.render_texture_v2 = {} # Inner storage for buffer data self.vertex_data = None self.vertex_dim = None self.n_vertices = None self.model_view_matrix = None self.projection_matrix = None if not egl: global GLUT import OpenGL.GLUT as GLUT GLUT.glutDisplayFunc(self.display) def init_quad_program(self): shader_list = [] shader_list.append(loadShader(GL_VERTEX_SHADER, "quad.vs")) shader_list.append(loadShader(GL_FRAGMENT_SHADER, "quad.fs")) the_program = createProgram(shader_list) for shader in shader_list: glDeleteShader(shader) # vertex attributes for a quad that fills the entire screen in Normalized Device Coordinates. # positions # texCoords quad_vertices = np.array( [-1.0, 1.0, 0.0, 1.0, -1.0, -1.0, 0.0, 0.0, 1.0, -1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, 1.0, -1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0] ) quad_buffer = glGenBuffers(1) glBindBuffer(GL_ARRAY_BUFFER, quad_buffer) glBufferData(GL_ARRAY_BUFFER, quad_vertices, GL_STATIC_DRAW) glBindBuffer(GL_ARRAY_BUFFER, 0) return the_program, quad_buffer def set_mesh(self, vertices, faces): self.vertex_data = vertices[faces.reshape([-1])] self.vertex_dim = self.vertex_data.shape[1] self.n_vertices = self.vertex_data.shape[0] glBindBuffer(GL_ARRAY_BUFFER, self.vertex_buffer) glBufferData(GL_ARRAY_BUFFER, self.vertex_data, GL_STATIC_DRAW) glBindBuffer(GL_ARRAY_BUFFER, 0) def set_viewpoint(self, projection, model_view): self.projection_matrix = projection self.model_view_matrix = model_view def draw_init(self): glBindFramebuffer(GL_FRAMEBUFFER, self.frame_buffer) glEnable(GL_DEPTH_TEST) glClearColor(0.0, 0.0, 0.0, 0.0) if self.use_inverse_depth: glDepthFunc(GL_GREATER) glClearDepth(0.0) else: glDepthFunc(GL_LESS) glClearDepth(1.0) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) def draw_end(self): if self.intermediate_fbo is not None: for i in range(len(self.color_buffer)): glBindFramebuffer(GL_READ_FRAMEBUFFER, self.frame_buffer) glReadBuffer(GL_COLOR_ATTACHMENT0 + i) glBindFramebuffer(GL_DRAW_FRAMEBUFFER, self.intermediate_fbo) glDrawBuffer(GL_COLOR_ATTACHMENT0 + i) glBlitFramebuffer(0, 0, self.width, self.height, 0, 0, self.width, self.height, GL_COLOR_BUFFER_BIT, GL_NEAREST) glBindFramebuffer(GL_FRAMEBUFFER, 0) glDepthFunc(GL_LESS) glClearDepth(1.0) def draw(self): self.draw_init() glUseProgram(self.program) glUniformMatrix4fv(self.model_mat_unif, 1, GL_FALSE, self.model_view_matrix.transpose()) glUniformMatrix4fv(self.persp_mat_unif, 1, GL_FALSE, self.projection_matrix.transpose()) glBindBuffer(GL_ARRAY_BUFFER, self.vertex_buffer) glEnableVertexAttribArray(0) glVertexAttribPointer(0, self.vertex_dim, GL_DOUBLE, GL_FALSE, 0, None) glDrawArrays(GL_TRIANGLES, 0, self.n_vertices) glDisableVertexAttribArray(0) glBindBuffer(GL_ARRAY_BUFFER, 0) glUseProgram(0) self.draw_end() def get_color(self, color_id=0): glBindFramebuffer(GL_FRAMEBUFFER, self.intermediate_fbo if self.intermediate_fbo is not None else self.frame_buffer) glReadBuffer(GL_COLOR_ATTACHMENT0 + color_id) data = glReadPixels(0, 0, self.width, self.height, GL_RGBA, GL_FLOAT, outputType=None) glBindFramebuffer(GL_FRAMEBUFFER, 0) rgb = data.reshape(self.height, self.width, -1) rgb = np.flip(rgb, 0) return rgb def get_z_value(self): glBindFramebuffer(GL_FRAMEBUFFER, self.frame_buffer) data = glReadPixels(0, 0, self.width, self.height, GL_DEPTH_COMPONENT, GL_FLOAT, outputType=None) glBindFramebuffer(GL_FRAMEBUFFER, 0) z = data.reshape(self.height, self.width) z = np.flip(z, 0) return z def display(self): self.draw() if not self.egl: # First we draw a scene. # Notice the result is stored in the texture buffer. # Then we return to the default frame buffer since we will display on the screen. glBindFramebuffer(GL_FRAMEBUFFER, 0) # Do the clean-up. glClearColor(0.0, 0.0, 0.0, 0.0) glClear(GL_COLOR_BUFFER_BIT) # We draw a rectangle which covers the whole screen. glUseProgram(self.quad_program) glBindBuffer(GL_ARRAY_BUFFER, self.quad_buffer) size_of_double = 8 glEnableVertexAttribArray(0) glVertexAttribPointer(0, 2, GL_DOUBLE, GL_FALSE, 4 * size_of_double, None) glEnableVertexAttribArray(1) glVertexAttribPointer(1, 2, GL_DOUBLE, GL_FALSE, 4 * size_of_double, c_void_p(2 * size_of_double)) glDisable(GL_DEPTH_TEST) # The stored texture is then mapped to this rectangle. # properly assing color buffer texture glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D, self.screen_texture[0]) glUniform1i(glGetUniformLocation(self.quad_program, 'screenTexture'), 0) glDrawArrays(GL_TRIANGLES, 0, 6) glDisableVertexAttribArray(1) glDisableVertexAttribArray(0) glEnable(GL_DEPTH_TEST) glBindBuffer(GL_ARRAY_BUFFER, 0) glUseProgram(0) GLUT.glutSwapBuffers() GLUT.glutPostRedisplay() def show(self): if not self.egl: GLUT.glutMainLoop()