pengc02's picture
all
ec9a6bc
import ctypes
import glfw
from OpenGL.GL import *
from OpenGL.GL import shaders
import numpy as np
import cv2 as cv
import trimesh
null = ctypes.c_void_p(0)
''' render vertex attributes, e.g., colors or normals '''
vs_vertex_attribute = '''
#version 330 core
uniform mat4 mvp;
layout (location = 0) in vec3 vertices;
layout (location = 1) in vec3 attributes;
out vec4 vertex_attributes;
void main(){
gl_Position = mvp * vec4(vertices, 1.f);
vertex_attributes = vec4(attributes, 1.f);
}
'''
fs_vertex_attribute = '''
#version 330 core
in vec4 vertex_attributes;
out vec4 frag_color;
void main(){
frag_color = vertex_attributes;
}
'''
''' render vertex positions '''
vs_position = '''
#version 330 core
uniform mat4 mvp;
layout (location = 0) in vec3 vertices;
out vec4 positions;
void main(){
gl_Position = mvp * vec4(vertices, 1.f);
positions = vec4(vertices, 1.f);
}
'''
fs_position = '''
#version 330 core
in vec4 positions;
out vec4 frag_color;
void main(){
frag_color = positions;
}
'''
''' Render phong geometry '''
vs_phong_geometry = '''
#version 330 core
uniform mat4 mvp;
uniform mat4 mv;
layout (location = 0) in vec3 vertices;
layout (location = 1) in vec3 normals;
out VS_OUT
{
vec3 v;
vec3 fn;
vec3 bn;
} vs_out;
void main(){
gl_Position = mvp * vec4(vertices, 1.f);
vec4 v_cam = mv * vec4(vertices, 1.f);
vs_out.v = v_cam.xyz;
mat3 R = mat3(mv);
vec3 front_normal = normalize(R * normals.xyz);
vec3 back_normal = -front_normal;
vs_out.fn = front_normal;
vs_out.bn = back_normal;
}
'''
fs_phong_geometry = '''
#version 330 core
struct LightAttrib
{
vec3 la;
vec3 ld;
vec3 ls;
vec3 ldir;
};
struct MaterialAttrib
{
vec3 ma;
vec3 md;
vec3 ms;
float ss;
};
in VS_OUT
{
vec3 v;
vec3 fn;
vec3 bn;
} fs_in;
out vec4 frag_color;
void main(){
/* init lighting, front material and back material */
LightAttrib light = LightAttrib(
vec3(0.3, 0.3, 0.3),
vec3(0.7, 0.7, 0.7),
vec3(1.0, 1.0, 1.0),
vec3(0.0, 0.0, 1.0)
);
// gray
MaterialAttrib fmat = MaterialAttrib(
vec3(0.85f, 0.85f, 0.85f),
vec3(0.85f, 0.85f, 0.85f),
vec3(0.1, 0.1, 0.1),
10.0
);
MaterialAttrib bmat = MaterialAttrib(
vec3(0.85, 0.85, 0.85),
vec3(0.85, 0.85, 0.85),
vec3(0.6, 0.6, 0.6),
100.0
);
/*Calculate light, view, front-facing and back-facing normals*/
vec3 ldir = normalize(light.ldir);
vec3 fn = normalize(fs_in.fn);
vec3 bn = normalize(fs_in.bn);
vec3 vdir = normalize(-fs_in.v);
vec3 frdir = reflect(-ldir, fn);
vec3 brdir = reflect(-ldir, bn);
/*discard this fragment if normal is NAN*/
if (any(isnan(fn)) || any(isnan(bn))) discard;
/*render double faces*/
if (gl_FrontFacing) {
/*calculate radiance*/
vec3 ka = light.la * fmat.ma;
vec3 kd = light.ld * fmat.md;
vec3 ks = light.ls * fmat.ms;
/*calculate Phong lighting of front-facing fragment*/
vec3 fca = ka;
vec3 fcd = kd * max(dot(fn, ldir), 0.0);
vec3 fcs = ks * pow(max(dot(vdir, frdir), 0.0), fmat.ss);
vec3 fc = clamp(fca + fcd + fcs, 0.0, 1.0);
frag_color = vec4(fc, 1.0);
}
else {
/*calculate radiance*/
vec3 ka = light.la * bmat.ma;
vec3 kd = light.ld * bmat.md;
vec3 ks = light.ls * bmat.ms;
/*calculate Phong lighting of back-facing fragment*/
vec3 bca = ka;
vec3 bcd = kd * max(dot(bn, ldir), 0.0);
vec3 bcs = ks * pow(max(dot(vdir, brdir), 0.0), bmat.ss);
vec3 bc = clamp(bca + bcd + bcs, 0.0, 1.0);
frag_color = vec4(bc, 1.0);
}
}
'''
''' Render phong color '''
vs_phong_color = '''
#version 330 core
uniform mat4 mvp;
uniform mat4 mv;
layout (location = 0) in vec3 vertices;
layout (location = 1) in vec3 normals;
layout (location = 2) in vec3 colors;
out VS_OUT
{
vec3 v;
vec3 fn;
vec3 bn;
vec3 c;
} vs_out;
void main(){
gl_Position = mvp * vec4(vertices, 1.f);
vec4 v_cam = mv * vec4(vertices, 1.f);
vs_out.v = v_cam.xyz;
mat3 R = mat3(mv);
vec3 front_normal = normalize(R * normals.xyz);
vec3 back_normal = -front_normal;
vs_out.fn = front_normal;
vs_out.bn = back_normal;
vs_out.c = colors;
}
'''
fs_phong_color = '''
#version 330 core
struct LightAttrib
{
vec3 la;
vec3 ld;
vec3 ls;
vec3 ldir;
};
struct MaterialAttrib
{
vec3 ma;
vec3 md;
vec3 ms;
float ss;
};
in VS_OUT
{
vec3 v;
vec3 fn;
vec3 bn;
vec3 c;
} fs_in;
out vec4 frag_color;
void main(){
/* init lighting, front material and back material */
LightAttrib light = LightAttrib(
vec3(0.3, 0.3, 0.3),
vec3(0.7, 0.7, 0.7),
vec3(1.0, 1.0, 1.0),
vec3(0.0, 0.0, 1.0)
);
// gray
MaterialAttrib fmat = MaterialAttrib(
vec3(0.85f, 0.85f, 0.85f),
vec3(0.85f, 0.85f, 0.85f),
vec3(0.1, 0.1, 0.1),
10.0
);
MaterialAttrib bmat = MaterialAttrib(
vec3(0.85, 0.85, 0.85),
vec3(0.85, 0.85, 0.85),
vec3(0.6, 0.6, 0.6),
100.0
);
/*Calculate light, view, front-facing and back-facing normals*/
vec3 ldir = normalize(light.ldir);
vec3 fn = normalize(fs_in.fn);
vec3 bn = normalize(fs_in.bn);
vec3 vdir = normalize(-fs_in.v);
vec3 frdir = reflect(-ldir, fn);
vec3 brdir = reflect(-ldir, bn);
/*discard this fragment if normal is NAN*/
if (any(isnan(fn)) || any(isnan(bn))) discard;
/*render double faces*/
if (gl_FrontFacing) {
/*calculate radiance*/
vec3 ka = light.la * fmat.ma;
vec3 kd = light.ld * fmat.md;
vec3 ks = light.ls * fmat.ms;
/*calculate Phong lighting of front-facing fragment*/
vec3 fca = ka;
vec3 fcd = kd * max(dot(fn, ldir), 0.0);
vec3 fcs = ks * pow(max(dot(vdir, frdir), 0.0), fmat.ss);
vec3 fc = clamp(fca + fcd + fcs, 0.0, 1.0);
frag_color = vec4(fc, 1.0) * vec4(fs_in.c, 1.0);
}
else {
/*calculate radiance*/
vec3 ka = light.la * bmat.ma;
vec3 kd = light.ld * bmat.md;
vec3 ks = light.ls * bmat.ms;
/*calculate Phong lighting of back-facing fragment*/
vec3 bca = ka;
vec3 bcd = kd * max(dot(bn, ldir), 0.0);
vec3 bcs = ks * pow(max(dot(vdir, brdir), 0.0), bmat.ss);
vec3 bc = clamp(bca + bcd + bcs, 0.0, 1.0);
frag_color = vec4(bc, 1.0) * vec4(fs_in.c, 1.0);
}
}
'''
''' Render color from texture map'''
vs_texture_color = '''
#version 330 core
uniform mat4 mvp;
in vec3 vertices;
in vec2 texture_coords;
out vec2 texCoords;
void main() {
texCoords = texture_coords;
gl_Position = mvp * vec4(vertices.xyz, 1.0);
}
'''
fs_texture_color = '''
#version 330
uniform sampler2D tex0;
in vec2 texCoords;
out vec4 frag_color;
void main() {
frag_color = texture(tex0, texCoords);
}
'''
''' render vertex attributes to a UV map '''
vs_vertex_attribute_to_uv = '''
#version 330 core
uniform mat4 mvp;
layout (location = 0) in vec2 vt;
layout (location = 1) in vec3 attributes;
out vec4 vertex_attributes;
void main(){
gl_Position = vec4(vt.x, vt.y, 0.f, 1.f);
vertex_attributes = vec4(attributes, 1.f);
}
'''
fs_vertex_attribute_to_uv = '''
#version 330 core
in vec4 vertex_attributes;
out vec4 frag_color;
void main(){
frag_color = vertex_attributes;
}
'''
# Note that the model is in the usual camera space by default (not opengl)
def gl_perspective_projection_matrix(fx, fy, cx, cy, img_w, img_h, far = 100.0, near = 0.1, gl_space = False):
proj_mat = np.zeros((4, 4), dtype = np.float32)
proj_mat[0, 0] = 2 * fx / img_w
proj_mat[0, 2] = (2 * cx - img_w) / img_w
proj_mat[1, 1] = -2 * fy / img_h
proj_mat[1, 2] = (img_h - 2 * cy) / img_h
proj_mat[2, 2] = (far + near) / (far - near)
proj_mat[2, 3] = 2 * near * far / (near - far)
proj_mat[3, 2] = 1.
if gl_space:
real2gl = np.identity(4, dtype = np.float32)
real2gl[1, 1] = -1
real2gl[2, 2] = -1
proj_mat = np.dot(proj_mat, real2gl)
return proj_mat
# Note that the model is in the usual camera space by default (not opengl)
def gl_orthographic_projection_matrix(far = -100.0, near = -0.1, gl_space = False):
proj_mat = np.zeros((4, 4), dtype = np.float32)
proj_mat[0, 0] = 1.
proj_mat[1, 1] = 1.
proj_mat[2, 2] = 2 / (far - near)
proj_mat[2, 3] = -(far + near) / (far - near)
proj_mat[3, 3] = 1.
if not gl_space:
gl2real = np.identity(4, dtype = np.float32)
gl2real[1, 1] = -1
gl2real[2, 2] = -1
proj_mat = np.dot(proj_mat, gl2real)
return proj_mat
class Renderer:
def __init__(self, img_w: int, img_h: int, mvp: np.ndarray = np.identity(4, dtype = np.float32), shader_name = 'vertex_attribute', bg_color = (0, 0, 0), win_name = ''):
glfw.init()
self.img_w = img_w
self.img_h = img_h
self.bg_color = bg_color
self.window = glfw.create_window(img_w, img_h, win_name, None, None)
glfw.set_window_pos(self.window, 500, 500)
glfw.hide_window(self.window)
glfw.make_context_current(self.window)
# init shader
self.shader_name = shader_name
if shader_name == 'vertex_attribute':
vertex_shader = shaders.compileShader(vs_vertex_attribute, GL_VERTEX_SHADER)
fragment_shader = shaders.compileShader(fs_vertex_attribute, GL_FRAGMENT_SHADER)
elif shader_name == 'position':
vertex_shader = shaders.compileShader(vs_position, GL_VERTEX_SHADER)
fragment_shader = shaders.compileShader(fs_position, GL_FRAGMENT_SHADER)
elif shader_name == 'phong_geometry':
vertex_shader = shaders.compileShader(vs_phong_geometry, GL_VERTEX_SHADER)
fragment_shader = shaders.compileShader(fs_phong_geometry, GL_FRAGMENT_SHADER)
elif shader_name == 'phong_color':
vertex_shader = shaders.compileShader(vs_phong_color, GL_VERTEX_SHADER)
fragment_shader = shaders.compileShader(fs_phong_color, GL_FRAGMENT_SHADER)
elif shader_name == 'texture_color':
vertex_shader = shaders.compileShader(vs_texture_color, GL_VERTEX_SHADER)
fragment_shader = shaders.compileShader(fs_texture_color, GL_FRAGMENT_SHADER)
elif shader_name == 'vertex_attribute_to_uv':
vertex_shader = shaders.compileShader(vs_vertex_attribute_to_uv, GL_VERTEX_SHADER)
fragment_shader = shaders.compileShader(fs_vertex_attribute_to_uv, GL_FRAGMENT_SHADER)
else:
raise ValueError('Invalid shader name!')
self.shader = shaders.compileProgram(vertex_shader, fragment_shader)
glUseProgram(self.shader)
# init model, view, projection matrix
self.uniform_locations = {'mvp': glGetUniformLocation(self.shader, 'mvp')}
glUniformMatrix4fv(self.uniform_locations['mvp'], 1, GL_TRUE, mvp)
# vertex and corresponding attribute location
# self.attribute_locations = {'vertices': glGetAttribLocation(self.shader, 'vertices'),
# 'attributes': glGetAttribLocation(self.shader, 'attributes')}
self.vao = glGenVertexArrays(1)
self.vertices_vbo = glGenBuffers(1)
self.attributes_vbo = glGenBuffers(1)
self.attributes_vbo_2 = glGenBuffers(1)
''' frame buffer object '''
# frame buffer
self.fbo = glGenFramebuffers(1)
glBindFramebuffer(GL_FRAMEBUFFER, self.fbo)
# texture
texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, img_w, img_h, 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, texture, 0)
# render buffer
rbo = glGenRenderbuffers(1)
glBindRenderbuffer(GL_RENDERBUFFER, rbo)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, img_w, img_h)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo)
if glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE:
print('frame buffer is not complete!')
glBindFramebuffer(GL_FRAMEBUFFER, 0) # switch to default frame buffer
# texture map (if used)
if shader_name == 'texture_color':
self.texture_map_id = glGenTextures(1)
else:
self.texture_map_id = None
def set_mvp_mat(self, mvp: np.ndarray):
glfw.make_context_current(self.window)
glUseProgram(self.shader)
glUniformMatrix4fv(self.uniform_locations['mvp'], 1, GL_TRUE, mvp)
def set_mv_mat(self, mv: np.ndarray):
glfw.make_context_current(self.window)
glUseProgram(self.shader)
mv_location = glGetUniformLocation(self.shader, 'mv')
glUniformMatrix4fv(mv_location, 1, GL_TRUE, mv)
def set_camera(self, extr, intr = None):
if intr is None: # assume orthographic projection
proj_mat = gl_orthographic_projection_matrix()
else:
proj_mat = gl_perspective_projection_matrix(
intr[0, 0], intr[1, 1],
intr[0, 2], intr[1, 2],
self.img_w, self.img_h
)
mvp = proj_mat @ extr
self.set_mvp_mat(mvp)
if self.shader_name.startswith('phong'):
real2gl = np.identity(4, np.float32)
real2gl[1, 1] = -1
real2gl[2, 2] = -1
self.set_mv_mat(real2gl @ extr)
def set_model(self, vertices, vertex_attributes = None, vertex_attributes_2 = None):
"""
the order of vertex attributes:
1. normal
2. color
"""
vertices = vertices.astype(np.float32)
glfw.make_context_current(self.window)
glBindVertexArray(self.vao)
# vertices
glBindBuffer(GL_ARRAY_BUFFER, self.vertices_vbo)
glBufferData(GL_ARRAY_BUFFER, vertices.size * ctypes.sizeof(GLfloat), vertices, GL_STREAM_DRAW)
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, vertices.shape[1], GL_FLOAT, False, 0, null)
if vertex_attributes is not None:
vertex_attributes = vertex_attributes.astype(np.float32)
# attributes
glBindBuffer(GL_ARRAY_BUFFER, self.attributes_vbo)
glBufferData(GL_ARRAY_BUFFER, vertex_attributes.size * ctypes.sizeof(GLfloat), vertex_attributes, GL_STREAM_DRAW)
glEnableVertexAttribArray(1)
glVertexAttribPointer(1, vertex_attributes.shape[1], GL_FLOAT, False, 0, null)
if vertex_attributes_2 is not None:
vertex_attributes_2 = vertex_attributes_2.astype(np.float32)
# attributes
glBindBuffer(GL_ARRAY_BUFFER, self.attributes_vbo_2)
glBufferData(GL_ARRAY_BUFFER, vertex_attributes_2.size * ctypes.sizeof(GLfloat), vertex_attributes_2, GL_STREAM_DRAW)
glEnableVertexAttribArray(2)
glVertexAttribPointer(2, vertex_attributes_2.shape[1], GL_FLOAT, False, 0, null)
glBindVertexArray(0)
self.vnum = vertices.shape[0]
def set_texture(self, tex_map):
height, width = tex_map.shape[:2]
glBindTexture(GL_TEXTURE_2D, self.texture_map_id)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, tex_map)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glGenerateMipmap(GL_TEXTURE_2D)
def render(self, cull_face = True):
glfw.make_context_current(self.window)
glfw.poll_events()
# defined frame buffer object
glBindFramebuffer(GL_FRAMEBUFFER, self.fbo)
glClearColor(self.bg_color[0], self.bg_color[1], self.bg_color[2], 0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glEnable(GL_DEPTH_TEST)
if cull_face:
glEnable(GL_CULL_FACE)
else:
glDisable(GL_CULL_FACE)
if self.texture_map_id is not None:
glBindTexture(GL_TEXTURE_2D, self.texture_map_id)
glBindVertexArray(self.vao)
glDrawArrays(GL_TRIANGLES, 0, self.vnum)
data = glReadPixels(0, 0, self.img_w, self.img_h, GL_RGBA, GL_FLOAT)
data = np.frombuffer(data, np.float32)
data.shape = self.img_h, self.img_w, 4
data = data[::-1, :]
glBindVertexArray(0)
return data