Using PyOpenGL, how do you apply instancing on an .obj file? - python

I am trying to apply instancing on a object (.obj) file by following the example on Learn Opengl - Instancing.
My question is actually twofold:
1) How to apply instancing on .obj files? (I think I'm almost there)
2) As I understand, to use instancing it is necessary to store the .obj file in a vertex array buffer (VAO), and therefore need help setting the materials and textures for the .obj file. That is if my assumption is true? (For now I just assigned the object to a solid white colour.)
What I have done so far:
I use Pywavefront to load the .obj file but I'm not stuck on it if another obj loader is needed.
Setting locations of where to place the object:
#Locations
vertices = [[-33.64413, 0.0, 19.03057], [-33.64818, 0.0, 19.0219],
[-33.64921, 0.0, 19.011],[-33.64913, 0.0, 19.03027], [-33.69828, 0.0, 19.0219],
[-33.64991, 0.0, 19.021]]
self.vertices = numpy.array(vertices, dtype='f')
Loading OBJ and collecting vertices and faces (indices):
obj_file = pywavefront.Wavefront('OBJ_PINS/Mcdonalds.obj', collect_faces=True)
obj_vertices = numpy.array(obj_file.vertices, dtype='f')
faces_list = []
for mesh in obj_file.mesh_list:
for face in mesh.faces:
faces_list.append(face)
obj_ind = numpy.array(faces_list, dtype=numpy.int32)
I then create a Vertex Array Object (VAO) with the necessary vertex buffer object (VBO):
VAOs = glGenVertexArrays(1)
glBindVertexArray(VAOs)
# Vertex Buffer Object
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, obj_vertices.nbytes, obj_vertices, GL_STATIC_DRAW)
# Element Buffer Object
EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, obj_ind.nbytes, obj_ind, GL_STATIC_DRAW)
# vertices
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, obj_vertices.itemsize * 3, ctypes.c_void_p(0))
Transforming and storing the object for instancing:
# instance locations
amount = len(self.vertices)
#List storing the same object in different places
modelMatrices = []
#Storring the objects in a list
for i in range(0, amount):
model_o = glm.mat4(1.0)
model_o = glm.translate(model_o, glm.vec3(self.vertices[i][0], self.vertices[i][1], self.vertices[i][2]))
model_o = glm.scale(model_o, glm.vec3(0.0001, 0.0001, 0.0001))
modelMatrices.append(model_o)
The interesting part, instancing the object to different places:
#Create VBO for instancing
instanceVBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO)
glBufferData(GL_ARRAY_BUFFER, glm.sizeof(glm.mat4), glm.value_ptr(modelMatrices[0]), GL_STATIC_DRAW)
#Bind each vertex attrib array of matrices (4 vectors in Matrix)
glEnableVertexAttribArray(3)
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p(0))
glEnableVertexAttribArray(4)
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p(glm.sizeof(glm.vec4)))
glEnableVertexAttribArray(5)
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p((2 * glm.sizeof(glm.vec4))))
glEnableVertexAttribArray(6)
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p((3 * glm.sizeof(glm.vec4))))
#Set instancing
glVertexAttribDivisor(3, 1)
glVertexAttribDivisor(4, 1)
glVertexAttribDivisor(5, 1)
glVertexAttribDivisor(6, 1)
#End VAO
glBindVertexArray(0)
The shaders:
self.vertex_obj = """
# version 330
in layout(location = 0) vec3 aPos;
in layout (location = 3) mat4 instanceMatrix;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * instanceMatrix * vec4(aPos, 1.0);
}
"""
self.fragment_obj = """
#version 330
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0,1.0,1.0, 1.0);
}
"""
When I draw the object in the drawing loop I only see one object and not 6:
glBindVertexArray(VAOs)
glDrawElementsInstanced(GL_TRIANGLES, len(obj_ind)*3, GL_UNSIGNED_INT, None, amount)
glBindVertexArray(0)
What I think is wrong:
I need to call the whole list of matrices to be drawn in the instanced glBufferData but don't know how to do that.
Should it change to something like:
glBufferData(GL_ARRAY_BUFFER, amount * glm.sizeof(glm.mat4), glm.value_ptr(modelMatrices[?]), GL_STATIC_DRAW)
FULL CODE
import numpy
import glfw
from OpenGL.GL import *
from OpenGL.GL.shaders import compileProgram, compileShader
import pyrr
import glm
from pyrr import matrix44
from math import sin, cos, radians
from OBJ_Loader import*
import pywavefront
class threeD_viewer():
def __init__(self):
vertices = [[-33.64413, 0.0, 19.03057], [-33.64818, 0.0, 19.0219],
[-33.64921, 0.0, 19.011],[-33.64913, 0.0, 19.03027], [-33.69828, 0.0, 19.0219],
[-33.64991, 0.0, 19.021]]
self.vertices = numpy.array(vertices, dtype='f')
self.cam = Camera(self.vertices[0][0], self.vertices[0][2])
self.WIDTH, self.HEIGHT = 1280, 720
self.lastX, self.LastY = self.WIDTH / 2, self.HEIGHT / 2
self.first_mouse = True
self.forward, self.backward, self.right, self.left, self.up, self.down = False, False, False, False, False, False
self.x_start, self.y_start = self.vertices[0][0], self.vertices[0][2]
self.vertex_obj = """
# version 330
in layout(location = 0) vec3 aPos;
in layout (location = 3) mat4 instanceMatrix;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * instanceMatrix * vec4(aPos, 1.0);
}
"""
self.fragment_obj = """
#version 330
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0,1.0,1.0, 1.0);
}
"""
self.main()
# the keyboard input callback
def key_input_clb(self, window, key, scancode, action, mode):
if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
glfw.set_window_should_close(window, True)
if key == glfw.KEY_W and action == glfw.PRESS:
self.forward = True
elif key == glfw.KEY_W and action == glfw.RELEASE:
self.forward = False
if key == glfw.KEY_S and action == glfw.PRESS:
self.backward = True
elif key == glfw.KEY_S and action == glfw.RELEASE:
self.backward = False
if key == glfw.KEY_A and action == glfw.PRESS:
self.left = True
elif key == glfw.KEY_A and action == glfw.RELEASE:
self.left = False
if key == glfw.KEY_D and action == glfw.PRESS:
self.right = True
elif key == glfw.KEY_D and action == glfw.RELEASE:
self.right = False
if key == glfw.KEY_Q and action == glfw.PRESS:
self.up = True
elif key == glfw.KEY_Q and action == glfw.RELEASE:
self.up = False
if key == glfw.KEY_E and action == glfw.PRESS:
self.down = True
elif key == glfw.KEY_E and action == glfw.RELEASE:
self.down = False
def do_movement(self):
if self.forward:
self.cam.process_keyboard("FORWARD", 0.000008)
if self.backward:
self.cam.process_keyboard("BACKWARD", 0.000008)
if self.right:
self.cam.process_keyboard("RIGHT", 0.000008)
if self.left:
self.cam.process_keyboard("LEFT", 0.000008)
if self.up:
self.cam.process_keyboard("UP", 0.000008)
if self.down:
self.cam.process_keyboard("DOWN", 0.000008)
def mmouse_look_clb(self, window, xpos, ypos):
if self.first_mouse:
self.lastX = xpos
self.lastY = ypos
self.first_mouse = False
xoffset = xpos - self.lastX
yoffset = self.lastY - ypos
self.lastX = xpos
self.lastY = ypos
self.cam.process_mouse_movement(xoffset, yoffset)
def window_resize(self, window, width, height):
glViewport(0, 0, width, height)
projection = pyrr.matrix44.create_perspective_projection_matrix(45, width / height, 0.0001, 100)
proj_loc = glGetUniformLocation(self.shader_obj, "projection")
glUniformMatrix4fv(proj_loc, 1, GL_FALSE, projection)
def main(self):
# initializing glfw library
if not glfw.init():
raise Exception("glfw can not be initialized!")
# creating the window
window = glfw.create_window(self.WIDTH, self.HEIGHT, "My OpenGL window", None, None)
# check if window was created
if not window:
glfw.terminate()
raise Exception("glfw window can not be created!")
# set window's position
glfw.set_window_pos(window, 400, 200)
# set the callback function for window resize
glfw.set_window_size_callback(window, self.window_resize)
glfw.set_cursor_pos_callback(window, self.mmouse_look_clb)
glfw.set_input_mode(window, glfw.CURSOR, glfw.CURSOR_DISABLED)
glfw.set_key_callback(window, self.key_input_clb)
# make the context current
glfw.make_context_current(window)
self.shader_obj = compileProgram(compileShader(self.vertex_obj, GL_VERTEX_SHADER),
compileShader(self.fragment_obj, GL_FRAGMENT_SHADER))
obj_file = pywavefront.Wavefront('OBJ_PINS/Mcdonalds.obj', collect_faces=True)
obj_vertices = numpy.array(obj_file.vertices, dtype='f')
faces_list = []
for mesh in obj_file.mesh_list:
for face in mesh.faces:
faces_list.append(face)
obj_ind = numpy.array(faces_list, dtype=numpy.int32)
VAOs = glGenVertexArrays(1)
glBindVertexArray(VAOs)
# Vertex Buffer Object
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, obj_vertices.nbytes, obj_vertices, GL_STATIC_DRAW)
# Element Buffer Object
EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, obj_ind.nbytes, obj_ind, GL_STATIC_DRAW)
# vertices
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, obj_vertices.itemsize * 3, ctypes.c_void_p(0))
# instance locations
amount = len(self.vertices)
#List storing the same object in different places
modelMatrices = []
#Storring the objects in a list
for i in range(0, amount):
model_o = glm.mat4(1.0)
model_o = glm.translate(model_o, glm.vec3(self.vertices[i][0], self.vertices[i][1], self.vertices[i][2]))
model_o = glm.scale(model_o, glm.vec3(0.0001, 0.0001, 0.0001))
modelMatrices.append(model_o)
#Create VBO for instancing
instanceVBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO)
glBufferData(GL_ARRAY_BUFFER, glm.sizeof(glm.mat4), glm.value_ptr(modelMatrices[0]), GL_STATIC_DRAW)
#Bind each vertex attrib array of matrices (4 vectors in Matrix)
glEnableVertexAttribArray(3)
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p(0))
glEnableVertexAttribArray(4)
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p(glm.sizeof(glm.vec4)))
glEnableVertexAttribArray(5)
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p((2 * glm.sizeof(glm.vec4))))
glEnableVertexAttribArray(6)
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p((3 * glm.sizeof(glm.vec4))))
#Set instancing
glVertexAttribDivisor(3, 1)
glVertexAttribDivisor(4, 1)
glVertexAttribDivisor(5, 1)
glVertexAttribDivisor(6, 1)
#End VAO
glBindVertexArray(0)
glClearColor(0, 0.1, 0.1, 1)
glEnable(GL_DEPTH_TEST)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
#projection = pyrr.matrix44.create_perspective_projection_matrix(45, self.WIDTH / self.HEIGHT, 0.0001, 1000)
projection = glm.perspective(glm.radians(45.0), self.WIDTH / self.HEIGHT, 0.0001, 1000)
glUseProgram(self.shader_obj)
view_loc_obj = glGetUniformLocation(self.shader_obj, "view")
proj_loc_obj = glGetUniformLocation(self.shader_obj, "projection")
model_loc_obj = glGetUniformLocation(self.shader_obj, "model")
glUniformMatrix4fv(proj_loc_obj, 1, GL_FALSE, glm.value_ptr(projection))
glUseProgram(0)
# the main application loop
while not glfw.window_should_close(window):
glfw.poll_events()
self.do_movement()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glUseProgram(self.shader_obj)
view = self.cam.get_view_matrix()
glUniformMatrix4fv(proj_loc_obj, 1, GL_FALSE, glm.value_ptr(projection))
glUniformMatrix4fv(view_loc_obj, 1, GL_FALSE, view)
glBindVertexArray(VAOs)
glDrawElementsInstanced(GL_TRIANGLES, len(obj_ind)*3, GL_UNSIGNED_INT, None, amount)
glBindVertexArray(0)
glUseProgram(0)
glfw.swap_buffers(window)
# terminate glfw, free up allocated resources
glfw.terminate()
class Camera:
def __init__(self, xstart, ystart):
self.camera_pos = glm.vec3(xstart+0.005, 0, ystart+0.01)
#self.camera_pos = glm.vec3(0, 0, 0)
print(xstart+0.005)
print(ystart+0.01)
self.camera_front = glm.vec3(0.0, 0.0, -200.0)
self.camera_up = glm.vec3(0.0, 1.0, 0.0)
self.camera_right = glm.vec3(1.0, 0.0, 0.0)
self.mouse_sensitivity = 0.25
self.jaw = -90.0
self.pitch = 0.0
def get_view_matrix(self):
return matrix44.create_look_at(self.camera_pos, self.camera_pos + self.camera_front, self.camera_up)
def process_mouse_movement(self, xoffset, yoffset, constrain_pitch=True):
xoffset *= self.mouse_sensitivity
yoffset *= self.mouse_sensitivity
self.jaw += xoffset
self.pitch += yoffset
if constrain_pitch:
if self.pitch > 45:
self.pitch = 45
if self.pitch < -45:
self.pitch = -45
self.update_camera_vectors()
def update_camera_vectors(self):
front = glm.vec3(0.0, 0.0, 0.0)
front.x = cos(radians(self.jaw)) * cos(radians(self.pitch))
front.y = sin(radians(self.pitch))
front.z = sin(radians(self.jaw)) * cos(radians(self.pitch))
self.camera_front = glm.normalize(front)
self.camera_right = glm.normalize(glm.cross(self.camera_front, glm.vec3(0.0, 1.0, 0.0)))
#self.camera_up = glm.normalize(glm.cross(self.camera_right, self.camera_front))
# Camera method for the WASD movement
def process_keyboard(self, direction, velocity):
if direction == "FORWARD":
self.camera_pos += self.camera_front * velocity
if direction == "BACKWARD":
self.camera_pos -= self.camera_front * velocity
if direction == "LEFT":
self.camera_pos -= self.camera_right * velocity
if direction == "RIGHT":
self.camera_pos += self.camera_right * velocity
if direction == "UP":
self.camera_pos += self.camera_up * velocity
if direction == "DOWN":
self.camera_pos -= self.camera_up * velocity
threeD_viewer()
Update
Instancing works now. The solution was to store a list of the vectors rather than the glm.mat4 objects:
# instance locations
amount = len(self.vertices)
#List storing the same object in different places
modelMatrices = []
#Storring the objects in a list
for i in range(0, amount):
mod_list = []
model_o = glm.mat4(1.0)
model_o = glm.translate(model_o, glm.vec3(self.vertices[i][0], self.vertices[i][1], self.vertices[i][2]))
for i in range(4):
for j in range(4):
mod_list.append(model_o[i][j])
modelMatrices.append(mod_list)
modelMatrices = numpy.array(modelMatrices, dtype ="f")
print(modelMatrices.shape)
#Create VBO for instancing
instanceVBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO)
glBufferData(GL_ARRAY_BUFFER, amount* glm.sizeof(glm.mat4), modelMatrices, GL_STATIC_DRAW)

Related

Porting C++ OpenGL to PyOpenGL not rendering properly

I've been facing an issue for the past couple of days, and I still haven't been able to figure it out.. I'm trying to port a previous C++ Opengl project to PyOpengl, but I'm not able to get the object to render as it should. I'm simply trying to render a 3D grid model, which works in the original C++ code, but not in Python PyOpenGL.
What it should look like: (C++ OpenGL)
What it looks like (Python PyOpenGL)
This is the code I end up with in PyOpenGL :
import glfw
import glm
import numpy as np
from OpenGL.GL import *
import Shader
# Settings
SCR_WIDTH = 800
SCR_HEIGHT = 600
def framebuffer_size_callback(window, width, height):
if width != 0 and height != 0:
width = width
height = height
glViewport(0, 0, width, height)
def create_v_array():
vertexArray = []
indexArray = []
for x in range(-100, 102, 2):
# To draw lines across x axis from z = -1 to z = 1
vertexArray.append(glm.vec3(x / 100.0, 0.0, -1.0)) # Vertex position 1
vertexArray.append(glm.vec3(1.0, 1.0, 1.0)) # color for v1
vertexArray.append(glm.vec3(x / 100.0, 0.0, 1.0)) # Vertex position 2
vertexArray.append(glm.vec3(1.0, 1.0, 1.0)) # color for v2
for z in range(-100, 102, 2):
# To draw lines across z axis from x = -1 to x = 1
vertexArray.append(glm.vec3(-1.0, 0.0, z / 100.0)) # Vertex position 1
vertexArray.append(glm.vec3(1.0, 1.0, 1.0)) # color for v1
vertexArray.append(glm.vec3(1.0, 0.0, z / 100.0)) # Vertex position 2
vertexArray.append(glm.vec3(1.0, 1.0, 1.0)) # color for v2
for i in range(10000):
indexArray.append(i)
vao = GLuint()
vbo = GLuint()
ebo = GLuint()
vertexArray = np.array(vertexArray, dtype=np.float32)
indexArray = np.array(indexArray, dtype=np.float32)
# Bind vao
glGenVertexArrays(1, vao)
glBindVertexArray(vao)
# Upload Vertex Buffer (VBO) to the GPU, keep a reference to it (vertexBufferObject)
glGenBuffers(1, vbo)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, vertexArray.nbytes, vertexArray, GL_STATIC_DRAW)
# Upload Index Buffer (EBO) to the GPU, keep a reference to it (elementBufferObject)
glGenBuffers(1, ebo)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexArray.nbytes, indexArray, GL_STATIC_DRAW)
# Position
glVertexAttribPointer(0,
3,
GL_FLOAT,
GL_FALSE,
24,
None
)
glEnableVertexAttribArray(0)
# Color
glVertexAttribPointer(1,
3,
GL_FLOAT,
GL_FALSE,
24,
ctypes.c_void_p(12)
)
glEnableVertexAttribArray(1)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
return vao
def main():
# Initialize the library
#glfw.glewExperimental = GL_TRUE
if not glfw.init():
return
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 2)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, GL_TRUE)
# Create a windowed mode window and its OpenGL context
window = glfw.create_window(SCR_WIDTH, SCR_HEIGHT, "OpenGL", None, None)
if not window:
glfw.terminate()
return
# Make the window's context current
glfw.make_context_current(window)
glfw.set_framebuffer_size_callback(window, framebuffer_size_callback)
#glEnable(GL_DEPTH_TEST)
#glDepthFunc(GL_LESS)
shader = Shader.Shader("VertexShader.vsh", "FragmentShader.fsh")
vao = create_v_array()
glUseProgram(shader.ID)
# Loop until the user closes the window
while not glfw.window_should_close(window):
glClearColor(0.2, 0.3, 0.3, 1.0)
glClear(GL_COLOR_BUFFER_BIT)
glUseProgram(shader.ID)
glBindVertexArray(vao)
model = glm.mat4(1)
view = glm.mat4(1)
model = glm.rotate(model, glm.radians(15.0), glm.vec3(1.0, 0.0, 0.0))
view = glm.translate(view, glm.vec3(0.0, 0.0, -1.0))
projection = glm.perspective(glm.radians(45.0), (SCR_WIDTH/SCR_HEIGHT), 0.1, 100.0)
shader.set_mat4("model", model)
shader.set_mat4("view", view)
shader.set_mat4("projection", projection)
glBindVertexArray(vao)
glDrawElements(GL_LINES, 20000, GL_UNSIGNED_INT, None)
glfw.swap_buffers(window)
glfw.poll_events()
glfw.terminate()
if __name__ == "__main__":
main()
Custom Shader class:
class Shader:
def __init__(self, vertex_path, fragment_path):
vertex_code = get_file_content(vertex_path)
fragment_code = get_file_content(fragment_path)
vertex = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertex, vertex_code)
glCompileShader(vertex)
result = glGetShaderiv(vertex, GL_COMPILE_STATUS)
if not (result):
print("Vertex shader ERROR!")
raise RuntimeError(glGetShaderInfoLog(vertex))
fragment = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragment, fragment_code)
glCompileShader(fragment)
result2 = glGetShaderiv(fragment, GL_COMPILE_STATUS)
if not (result2):
print("Fragment shader ERROR!")
raise RuntimeError(glGetShaderInfoLog(vertex))
self.ID = glCreateProgram()
glAttachShader(self.ID, vertex)
glAttachShader(self.ID, fragment)
glLinkProgram(self.ID)
success = glGetProgramiv(self.ID, GL_LINK_STATUS)
if not success:
print("gad darn")
infolog = glGetProgramInfoLog(self.ID)
print("ERROR::SHADER::PROGRAM::LINKING_FAILED\n", infolog)
glDeleteShader(vertex)
glDeleteShader(fragment)
def set_mat4(self, name, matrix):
glUniformMatrix4fv(glGetUniformLocation(self.ID, name), 1, GL_FALSE, glm.value_ptr(matrix))
def use(self):
glUseProgram(self.ID)
# Helper Function
def get_file_content(file):
with open(file) as f:
content = f.read()
return content
glsl files:
Vertex Shader:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
out vec3 vertexColor;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
vertexColor = aColor;
}
Fragment Shader:
#version 330 core
in vec3 vertexColor;
out vec4 FragColor;
void main()
{
FragColor = vec4(vertexColor.r, vertexColor.g, vertexColor.b, 1.0f);
}
The type of the indices must be integral instead of the floating point:
indexArray = np.array(indexArray, dtype=np.float32)
indexArray = np.array(indexArray, dtype=np.uint32)

How to solve Python OpenGL Memory Leakage problem?

I am trying to draw some traingles and render some texts in screen. But I've observed that memory(RAM) is gradually increasing just only for 6 draw calls. I've 8 GB RAM. When I run the program memory usage goes from 4.2 to 6 within 1 minute. Here is the full code.
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GL import shaders
from shader import *
import glfw
import freetype
import glm
import numpy as np
from PIL import Image
import math
class CharacterSlot:
def __init__(self, texture, glyph):
self.texture = texture
self.textureSize = (glyph.bitmap.width, glyph.bitmap.rows)
if isinstance(glyph, freetype.GlyphSlot):
self.bearing = (glyph.bitmap_left, glyph.bitmap_top)
self.advance = glyph.advance.x
elif isinstance(glyph, freetype.BitmapGlyph):
self.bearing = (glyph.left, glyph.top)
self.advance = None
else:
raise RuntimeError('unknown glyph type')
def _get_rendering_buffer(xpos, ypos, w, h, zfix=0.0):
return np.asarray([
xpos, ypos + h, 0, 0,
xpos, ypos, 0, 1,
xpos + w, ypos, 1, 1,
xpos, ypos + h, 0, 0,
xpos + w, ypos, 1, 1,
xpos + w, ypos + h, 1, 0
], np.float32)
def init_chars(shaderProgram,window_height,window_width,font_size=24,fontfile = "Vera.ttf"):
glUseProgram(shaderProgram)
#get projection
shader_projection = glGetUniformLocation(shaderProgram, "projection")
W = window_width
H = window_height
projection = glm.ortho(-W/2, W/2, -H/2, H/2)
glUniformMatrix4fv(shader_projection, 1, GL_FALSE, glm.value_ptr(projection))
#disable byte-alignment restriction
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
face = freetype.Face(fontfile)
face.set_char_size(font_size*64 )
#load first 128 characters of ASCII set
Characters = dict()
for i in range(0,128):
face.load_char(chr(i))
glyph = face.glyph
#generate texture
texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, glyph.bitmap.width, glyph.bitmap.rows, 0,
GL_RED, GL_UNSIGNED_BYTE, glyph.bitmap.buffer)
#texture options
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_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
#now store character for later use
Characters[chr(i)] = CharacterSlot(texture,glyph)
glBindTexture(GL_TEXTURE_2D, 0)
glUseProgram(0)
return Characters
def render_text(window,shaderProgram,text,x,y,scale,Characters,color=(170,250,255)):
r,g,b = color
glUseProgram(shaderProgram)
#configure VAO/VBO for texture quads
VAO = glGenVertexArrays(1)
glBindVertexArray(VAO)
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, 6 * 4 * 4, None, GL_STATIC_DRAW)
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
glUniform3f(glGetUniformLocation(shaderProgram, "textColor"),r/255,g/255,b/255)
glActiveTexture(GL_TEXTURE0)
glBindVertexArray(VAO)
for c in text:
ch = Characters[c]
w, h = ch.textureSize
w = w*scale
h = h*scale
vertices = _get_rendering_buffer(x,y,w,h)
#render glyph texture over quad
glBindTexture(GL_TEXTURE_2D, ch.texture)
#update content of VBO memory
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.nbytes, vertices)
glBindBuffer(GL_ARRAY_BUFFER, 0)
#render quad
glDrawArrays(GL_TRIANGLES, 0, 6)
#now advance cursors for next glyph (note that advance is number of 1/64 pixels)
x += (ch.advance>>6)*scale
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0)
#UNBIND and DELETE VAO/VBO
glBindVertexArray(0)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glDeleteBuffers(1, id(VBO))
glDeleteBuffers(1, id(VAO))
def triangle(shaderProgram,window,x=0,y=0):
vertices = [-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
0.0, 0.5, 0.0]
vertices = np.array(vertices, dtype=np.float32)
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(0)
#use shader program
glUseProgram(shaderProgram)
#accessing ourColor variable from shaderProgram
vertexColorLoc = glGetUniformLocation(shaderProgram, "ourColor")
glUniform4f(vertexColorLoc, 255, 28/255.0, 20/255.0, 0.7);
#transform matrix
transform = glm.mat4(1)
transform = glm.translate(transform,glm.vec3(x,y,0))
MVP = glGetUniformLocation(shaderProgram, "MVP")
glUniformMatrix4fv(MVP, 1, GL_FALSE, glm.value_ptr(transform))
#drawing trangle
glLineWidth(3)
glDrawArrays(GL_TRIANGLES, 0, 3)
glUseProgram(0)
#UNBIND and DELETE VAO/VBO
glBindVertexArray(0)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glDeleteBuffers(1, id(VBO))
def main():
glfw.init()
window = glfw.create_window(640, 640,"EXAMPLE PROGRAM",None,None)
glfw.make_context_current(window)
#initliaze shader programs
shaderProgram = get_shaderProgram()
text_shaderProgram = get_text_shaderProgram()
#load characters and VAO/VBO for text rendering
Characters = init_chars(text_shaderProgram,640,640)
#window loop
while not glfw.window_should_close(window):
glfw.poll_events()
#screen
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
#draw functions
render_text(window,text_shaderProgram,"TRIANGLE",-50,-200,1,Characters)
render_text(window,text_shaderProgram,"A",0,180,1,Characters)
render_text(window,text_shaderProgram,"B",-160,-180,1,Characters)
render_text(window,text_shaderProgram,"C",150,-180,1,Characters)
triangle(shaderProgram,window)
triangle(shaderProgram,window,x=0.5,y=0.5)
#swap buffers
glfw.swap_buffers(window)
glfw.swap_interval(1)
glfw.terminate()
if __name__ == '__main__':
main()
The shader program is here. But I think the problems is in buffer object. I've tried to unbind VAO/VBO and delete buffers by following code. But I see no change.
#UNBIND and DELETE VAO/VBO
glBindVertexArray(0)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glDeleteBuffers(1, id(VBO))
glDeleteBuffers(1, id(VAO))
Here is the related problem where accepted answer suggested that glGenBuffers causes memory leak. The alternate function glCreateBuffers is not available in pyopengl. How can I solve this issue?
I can't see any good reason for recreating the Vertex Array Object and Array Buffer Object every time when render_text respectively triangle is called. The vertex specification and the number of vertices doesn't change, so it would be sufficient to update the content of the buffer.
Create the Vertex Array Object and the Array Buffer Object once at initialization:
def init_buffers():
global text_VAO, text_VBO, triangle_VAO, triangle_VBO
text_VAO = glGenVertexArrays(1)
glBindVertexArray(text_VAO)
text_VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, text_VBO)
glBufferData(GL_ARRAY_BUFFER, 6 * 4 * 4, None, GL_STATIC_DRAW)
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
vertices = [-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
0.0, 0.5, 0.0]
vertices = np.array(vertices, dtype=np.float32)
triangle_VAO = glGenVertexArrays(1)
glBindVertexArray(triangle_VAO)
triangle_VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, triangle_VBO)
glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(0)
Use then in the functions render_text and triangle:
def render_text(window,shaderProgram,text,x,y,scale,Characters,color=(170,250,255)):
# [...]
glBindVertexArray(text_VAO)
for c in text:
# [...]
glBindBuffer(GL_ARRAY_BUFFER, text_VBO)
glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.nbytes, vertices)
# glDeleteBuffers(1, id(VBO)) <--- DELETE
# glDeleteBuffers(1, id(VAO)) <--- DELETE
def triangle(shaderProgram,window,x=0,y=0):
glBindVertexArray(triangle_VAO)
# [...]
# glDeleteBuffers(1, id(VBO)) <--- DELETE
Invoke init_buffers before the application loop:
def main():
# [...]
init_buffers()
while not glfw.window_should_close(window):
# [...]

How to render text with PyOpenGL?

I'm learning modern openGL, and at this moment I'm facing trouble with rendering text. I'm following this tutorial which is in C++, but I'm trying to implement in python.
Here is my code:
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GL import shaders
import glfw
import freetype
import glm
import numpy as np
from PIL import Image
import math
import time
class CharacterSlot:
def __init__(self, texture, glyph):
self.texture = texture
self.textureSize = (glyph.bitmap.width, glyph.bitmap.rows)
if isinstance(glyph, freetype.GlyphSlot):
self.bearing = (glyph.bitmap_left, glyph.bitmap_top)
self.advance = glyph.advance.x
elif isinstance(glyph, freetype.BitmapGlyph):
self.bearing = (glyph.left, glyph.top)
self.advance = None
else:
raise RuntimeError('unknown glyph type')
def _get_rendering_buffer(xpos, ypos, w, h, zfix=0.0):
return np.asarray([
xpos, ypos - h, zfix, 0.0, 1.0,
xpos, ypos, zfix, 0.0, 0.0,
xpos + w, ypos, zfix, 1.0, 0.0,
xpos, ypos - h, zfix, 0.0, 1.0,
xpos + w, ypos, zfix, 1.0, 0.0,
xpos + w, ypos - h, zfix, 1.0, 1.0
], np.float32)
VERTEX_SHADER = """
#version 330 core
layout (location = 0) in vec4 vertex; // <vec2 pos, vec2 tex>
out vec2 TexCoords;
uniform mat4 projection;
void main()
{
gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);
TexCoords = vertex.zw;
}
"""
FRAGMENT_SHADER = """
#version 330 core
in vec2 TexCoords;
out vec4 color;
uniform sampler2D text;
uniform vec3 textColor;
void main()
{
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
color = vec4(textColor, 1.0) * sampled;
}
"""
shaderProgram = None
Characters = dict()
VBO = None
VAO = None
def initliaze():
global VERTEXT_SHADER
global FRAGMENT_SHADER
global shaderProgram
global Characters
global VBO
global VAO
#compiling shaders
vertexshader = shaders.compileShader(VERTEX_SHADER, GL_VERTEX_SHADER)
fragmentshader = shaders.compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER)
#creating shaderProgram
shaderProgram = shaders.compileProgram(vertexshader, fragmentshader)
#get projection
#problem
shader_projection = glGetUniformLocation(shaderProgram, "projection")
projection = glm.ortho(0.0,640,0.0,640)
glUniformMatrix4fv(shader_projection, 1, GL_FALSE, glm.value_ptr(projection));
#disable byte-alignment restriction
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
face = freetype.Face("Vera.ttf")
face.set_char_size( 48*64 )
#load first 128 characters of ASCII set
for i in range(0,128):
face.load_char(chr(i))
glyph = face.glyph
#generate texture
texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture)
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGB,
glyph.bitmap.width, glyph.bitmap.rows,
0,
GL_RGB,
GL_UNSIGNED_BYTE,
glyph.bitmap.buffer)
#texture options
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_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#now store character for later use
Characters[chr(i)] = CharacterSlot(texture,glyph)
glBindTexture(GL_TEXTURE_2D, 0);
#configure VAO/VBO for texture quads
VAO = glGenVertexArrays(1)
glBindVertexArray(VAO)
VBO = glGenBuffers(1);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, 6 * 4, None, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
def render_text(window,text,x,y,scale,color):
global shaderProgram
global Characters
global VBO
global VAO
face = freetype.Face("Vera.ttf")
face.set_char_size(48*64)
glUniform3f(glGetUniformLocation(shaderProgram, "textColor"),
color[0]/255,color[1]/255,color[2]/255)
glActiveTexture(GL_TEXTURE0);
for c in text:
ch = Characters[c]
w,h = ch.textureSize
w = w*scale
h = w*scale
vertices = _get_rendering_buffer(x,y,w,h)
#render glyph texture over quad
glBindTexture(GL_TEXTURE_2D, ch.texture);
#update content of VBO memory
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, len(vertices), vertices)
glBindBuffer(GL_ARRAY_BUFFER, 0);
#render quad
glDrawArrays(GL_TRIANGLES, 0, 6);
#now advance cursors for next glyph (note that advance is number of 1/64 pixels)
x += (ch.advance+6)*scale;
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
glfwSwapBuffers(window);
glfwPollEvents();
def main():
glfw.init()
window = glfw.create_window(640, 640,"EXAMPLE PROGRAM",None,None)
glfw.make_context_current(window)
initliaze()
while not glfw.window_should_close(window):
glfw.poll_events()
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT);
render_text(window,'hello',1,1,1,(100,100,100))
glfw.terminate()
if __name__ == '__main__':
main()
I'm facing trouble in two portion so far I can understand. The first problem in initliaze(), error raised for the following portion.
shader_projection = glGetUniformLocation(shaderProgram, "projection")
projection = glm.ortho(0.0,640,0.0,640)
glUniformMatrix4fv(shader_projection, 1, GL_FALSE, glm.value_ptr(projection));
I've commented out the above portion to ignore. The second problem is in render_text() function, error raised for the following portion.
glUniform3f(glGetUniformLocation(shaderProgram, "textColor"),
color[0]/255,color[1]/255,color[2]/255)
There could be problems in many more places. I don't understand that why text rendering will be so difficult. What am I missing here?
You missed to install the shader program by glUseProgram:
shaderProgram = shaders.compileProgram(vertexshader, fragmentshader)
glUseProgram(shaderProgram) # <---
The 2nd argument to glBufferData and the 3rd argument of glBufferSubData is the size in bytes:
glBufferData(GL_ARRAY_BUFFER, 6 * 4, None, GL_DYNAMIC_DRAW)
glBufferData(GL_ARRAY_BUFFER, 6 * 4 * 4, None, GL_DYNAMIC_DRAW)
glBufferSubData(GL_ARRAY_BUFFER, 0, len(vertices), vertices)
glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.nbytes, vertices)
The vertex attribute consist of a 2 dimension vertex coordinate (x, y) and a 2 dimensional texture coordinate. Remove the wird zfix from the array of vertex attribute data. Furthermore you have to flip the 2nd component of the texture coordinates (otherwise the text is upside down)
def _get_rendering_buffer(xpos, ypos, w, h, zfix=0.0):
return np.asarray([
xpos, ypos - h, 0, 0,
xpos, ypos, 0, 1,
xpos + w, ypos, 1, 1,
xpos, ypos - h, 0, 0,
xpos + w, ypos, 1, 1,
xpos + w, ypos - h, 1, 0
], np.float32)
The stride argument of glVertexAttribIPointer has to be specified in bytes. If stride is 0, the generic vertex attributes are understood to be tightly packed in the array. Hence in your case stride has to be 16 or 0:
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4, 0)
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
face.load_char(chr(i)) generates a image with on color channel (1 byte per pixel). Use the internal format and format GL_RED rather than GL_RGB for generating the 2 dimensional texture image:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, glyph.bitmap.width, glyph.bitmap.rows, 0,
GL_RED, GL_UNSIGNED_BYTE, glyph.bitmap.buffer)
You have to bind the vertex array, before drawing the text:
glBindVertexArray(VAO)
for c in text:
# [...]
glDrawArrays(GL_TRIANGLES, 0, 6)
There is typo when you increment x, you have to use the >>-operator rather than the +-operator:
x += (ch.advance+6)*scale
x += (ch.advance>>6)*scale
and another typo when you compute h:
h = w*scale
h = h*scale
You have to enable alpha blending:
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
In NDC (normalized device coordinates) the left bottom is (-1, -1) and the right top is (1, 1). Set the orthographic projection in that way, that the top left of the window is at (0, 0):
projection = glm.ortho(0.0,640,0.0,640)
projection = glm.ortho(0, 640, 640, 0)
The reference point of the text is at the bottom. Hence you have to set a x coordinate greater than the text height:
render_text(window,'hello',1,1,1,(100,100,100))
render_text(window,'hello', 20, 50, 1, (255, 100, 100))
See the complete example (I've used a different font):
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GL import shaders
import glfw
import freetype
import glm
import numpy as np
from PIL import Image
import math
import time
fontfile = "Vera.ttf"
#fontfile = r'C:\source\resource\fonts\gnu-freefont_freesans\freesans.ttf'
class CharacterSlot:
def __init__(self, texture, glyph):
self.texture = texture
self.textureSize = (glyph.bitmap.width, glyph.bitmap.rows)
if isinstance(glyph, freetype.GlyphSlot):
self.bearing = (glyph.bitmap_left, glyph.bitmap_top)
self.advance = glyph.advance.x
elif isinstance(glyph, freetype.BitmapGlyph):
self.bearing = (glyph.left, glyph.top)
self.advance = None
else:
raise RuntimeError('unknown glyph type')
def _get_rendering_buffer(xpos, ypos, w, h, zfix=0.0):
return np.asarray([
xpos, ypos - h, 0, 0,
xpos, ypos, 0, 1,
xpos + w, ypos, 1, 1,
xpos, ypos - h, 0, 0,
xpos + w, ypos, 1, 1,
xpos + w, ypos - h, 1, 0
], np.float32)
VERTEX_SHADER = """
#version 330 core
layout (location = 0) in vec4 vertex; // <vec2 pos, vec2 tex>
out vec2 TexCoords;
uniform mat4 projection;
void main()
{
gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);
TexCoords = vertex.zw;
}
"""
FRAGMENT_SHADER = """
#version 330 core
in vec2 TexCoords;
out vec4 color;
uniform sampler2D text;
uniform vec3 textColor;
void main()
{
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
color = vec4(textColor, 1.0) * sampled;
}
"""
shaderProgram = None
Characters = dict()
VBO = None
VAO = None
def initliaze():
global VERTEXT_SHADER
global FRAGMENT_SHADER
global shaderProgram
global Characters
global VBO
global VAO
#compiling shaders
vertexshader = shaders.compileShader(VERTEX_SHADER, GL_VERTEX_SHADER)
fragmentshader = shaders.compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER)
#creating shaderProgram
shaderProgram = shaders.compileProgram(vertexshader, fragmentshader)
glUseProgram(shaderProgram)
#get projection
#problem
shader_projection = glGetUniformLocation(shaderProgram, "projection")
projection = glm.ortho(0, 640, 640, 0)
glUniformMatrix4fv(shader_projection, 1, GL_FALSE, glm.value_ptr(projection))
#disable byte-alignment restriction
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
face = freetype.Face(fontfile)
face.set_char_size( 48*64 )
#load first 128 characters of ASCII set
for i in range(0,128):
face.load_char(chr(i))
glyph = face.glyph
#generate texture
texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, glyph.bitmap.width, glyph.bitmap.rows, 0,
GL_RED, GL_UNSIGNED_BYTE, glyph.bitmap.buffer)
#texture options
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_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
#now store character for later use
Characters[chr(i)] = CharacterSlot(texture,glyph)
glBindTexture(GL_TEXTURE_2D, 0)
#configure VAO/VBO for texture quads
VAO = glGenVertexArrays(1)
glBindVertexArray(VAO)
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, 6 * 4 * 4, None, GL_DYNAMIC_DRAW)
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
def render_text(window,text,x,y,scale,color):
global shaderProgram
global Characters
global VBO
global VAO
face = freetype.Face(fontfile)
face.set_char_size(48*64)
glUniform3f(glGetUniformLocation(shaderProgram, "textColor"),
color[0]/255,color[1]/255,color[2]/255)
glActiveTexture(GL_TEXTURE0)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glBindVertexArray(VAO)
for c in text:
ch = Characters[c]
w, h = ch.textureSize
w = w*scale
h = h*scale
vertices = _get_rendering_buffer(x,y,w,h)
#render glyph texture over quad
glBindTexture(GL_TEXTURE_2D, ch.texture)
#update content of VBO memory
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.nbytes, vertices)
glBindBuffer(GL_ARRAY_BUFFER, 0)
#render quad
glDrawArrays(GL_TRIANGLES, 0, 6)
#now advance cursors for next glyph (note that advance is number of 1/64 pixels)
x += (ch.advance>>6)*scale
glBindVertexArray(0)
glBindTexture(GL_TEXTURE_2D, 0)
glfw.swap_buffers(window)
glfw.poll_events()
def main():
glfw.init()
window = glfw.create_window(640, 640,"EXAMPLE PROGRAM",None,None)
glfw.make_context_current(window)
initliaze()
while not glfw.window_should_close(window):
glfw.poll_events()
glClearColor(0,0,0,1)
glClear(GL_COLOR_BUFFER_BIT)
render_text(window,'hello', 20, 50, 1, (255, 100, 100))
glfw.terminate()
if __name__ == '__main__':
main()
See also FreeType / OpenGL text rendering

Updating Shader Uniform in OpenGL doesn't work

At the moment I am stuck trying to update uniform values of the vertex and fragment shader. Unfortunately as soon as I call glDrawElements I can't access these values anymore. So I am only able to set them at initialization and then never again. It looks a bit like in this thread, but reinstalling PyOpenGl did not work (MODERN OPENGL can't change uniform value beetween glDrawArrays calls). I would be greatful for any help.
def Video_setupGL():
#Set ViewPort
glViewport(0, 0, 640, 480)
####################
vertices2 = [
0.0, 0.5, -0.5,
0.5, -0.5, -0.5,
-0.5, -0.5, -0.5
]
#####################
vertexShaderSource2 = textwrap.dedent("""#version 330
in vec3 position;
uniform mat4 transform;
void main() { gl_Position = transform * vec4(position, 1.0f); }\
""")
vertexShader = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertexShader, vertexShaderSource2)
glCompileShader(vertexShader)
#check if compilation was successful
ret = glGetShaderiv(vertexShader, GL_COMPILE_STATUS)
if not ret:
raise Exception("failed to compile vertex shader")
fragmentSource2 = textwrap.dedent("""#version 330
uniform vec4 ourColor;
void main()
{
##gl_FragColor = vec4(1.0f, 1.0f, 1.0f, 1.0f);
gl_FragColor = ourColor;
}\
""")
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragmentShader, fragmentSource2)
glCompileShader(fragmentShader)
ret = glGetShaderiv(fragmentShader, GL_COMPILE_STATUS)
if not ret:
raise Exception("failed to compile fragment shader. Code = " + str(ret))
shaderProgram = glCreateProgram()
glAttachShader(shaderProgram, vertexShader)
glAttachShader(shaderProgram, fragmentShader)
glLinkProgram(shaderProgram)
ret = glGetProgramiv(shaderProgram, GL_LINK_STATUS)
if not ret:
raise Exception("failed to link shader program. Code = " + str(ret))
#################################
#1. Init VBO
vertexBuffer = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer)
glBufferData(GL_ARRAY_BUFFER, np.array(vertices, dtype="float32"), GL_STATIC_DRAW)
#1.2 Init EBO
indices = [0, 1, 3, 1, 2, 3]
ebo = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, np.array(indices, dtype="int32"), GL_STATIC_DRAW)
#################################
#2. Init VAO
vertexArrayObject = glGenVertexArrays(1)
glBindVertexArray(vertexArrayObject)
#################################
position = glGetAttribLocation(shaderProgram, 'position')
glEnableVertexAttribArray(position)
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 3*sizeof(c_float), ctypes.c_void_p(0)) # stride is maybe wrongly set
glEnableVertexAttribArray(0)
glBindVertexArray(0)
glDisableVertexAttribArray(position)
glBindBuffer(GL_ARRAY_BUFFER, 0)
########################
state = [shaderProgram, vertexArrayObject, ebo]
return state
def Video_paintGL(state, posMatrix, i, color):
######posMatrix, i and color are changed everytime Video_paintGL ist executed
shaderProgram, vertexArrayObject, myEbo = state
##############
glClearColor(0.2, 0.2, 0.2, 1.0)
glClear(GL_COLOR_BUFFER_BIT)
glLoadIdentity()
glUseProgram(shaderProgram)
################
transformLoc = glGetUniformLocation(shaderProgram, "transform")
if transformLoc == -1:
raise Exception("Transform Uniform could not be found")
print("Error: " + str(glGetError()))
posMatrix = getRotMatrix(np.array([0,0,1], dtype="float32"), np.pi/4 * (i+1)) #get a rotated identiy matrix around the z axis
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, posMatrix) ###this function makes me sick
colorLoc = glGetUniformLocation(shaderProgram, "ourColor")
print(color)
glUniform4f(colorLoc, color[0], color[1], color[2], 1.0) #this as well
glBindVertexArray(vertexArrayObject)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, myEbo)
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
glBindVertexArray(0)
glUseProgram(0)

OpenGL Shadow Mapping using GLSL

I am trying to get shadow mapping working using GLSL. Unfortunately my depth render results are unusable even I have a pretty decent depth buffer precision. It is rendering like wireframe, following image may be a better description.
I am also including a test case(single file including shader), only dependency is pyopengl.
# shadow mapping test
# utkualtinkaya at gmail
# shader is from http://www.fabiensanglard.net/shadowmapping/index.php
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from OpenGL.GL.shaders import *
from OpenGL.GL.framebufferobjects import *
import math
class Camera:
def __init__(self):
self.rotx, self.roty = math.pi/4, math.pi/4
self.distance = 100
self.moving = False
self.ex, self.ey = 0, 0
self.size = (800, 600)
def load_matrices(self):
glViewport(0, 0, *self.size)
y = math.cos(self.roty) * self.distance
x = math.sin(self.roty) * math.cos(self.rotx) * self.distance
z = math.sin(self.roty) * math.sin(self.rotx) * self.distance
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45.0, self.size[0]/float(self.size[1]), 1, 1000)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluLookAt(x,y,z, 0,0,0, 0,1,0)
def on_mouse_button (self, b, s, x, y):
self.moving = not s
self.ex, self.ey = x, y
if b in [3, 4]:
dz = (1 if b == 3 else -1)
self.distance += self.distance/15.0 * dz;
def on_mouse_move(self, x, y, z = 0):
if self.moving:
self.rotx += (x-self.ex) / 300.0
self.roty += -(y-self.ey) / 300.0
self.ex, self.ey = x, y
def set_size(self, w, h):
self.size = w, h
class Shader():
def __init__(self):
self.is_built = False
self.uniforms = {}
def build(self):
self.program = compileProgram(
compileShader('''
uniform mat4 camMatrix;
uniform mat4 shadowMatrix;
varying vec4 depthProjection;
uniform bool useShadow;
void main() {
gl_Position = camMatrix * gl_ModelViewMatrix * gl_Vertex;
depthProjection = shadowMatrix * gl_ModelViewMatrix * gl_Vertex;
gl_FrontColor = gl_Color;
}
''',GL_VERTEX_SHADER),
compileShader('''
varying vec4 depthProjection;
uniform sampler2D shadowMap;
uniform bool useShadow;
void main () {
float shadow = 1.0;
if (useShadow) {
vec4 shadowCoord = depthProjection / depthProjection.w ;
// shadowCoord.z -= 0.0003;
float distanceFromLight = texture2D(shadowMap, shadowCoord.st).z;
if (depthProjection .w > 0.0)
shadow = distanceFromLight < shadowCoord.z ? 0.5 : 1.0 ;
}
gl_FragColor = shadow * gl_Color;
}
''',GL_FRAGMENT_SHADER),)
self.is_built = True
self.uniforms['camMatrix'] = glGetUniformLocation(self.program, 'camMatrix')
self.uniforms['shadowMatrix'] = glGetUniformLocation(self.program, 'shadowMatrix')
self.uniforms['shadowMap'] = glGetUniformLocation(self.program, 'shadowMap')
self.uniforms['useShadow'] = glGetUniformLocation(self.program, 'useShadow')
print self.uniforms
def use(self):
if not self.is_built:
self.build()
glUseProgram(self.program)
class Test:
def __init__(self):
glutInit(sys.argv)
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
glutInitWindowSize(800, 600)
glutInitWindowPosition(1120/2, 100)
self.window = glutCreateWindow("Shadow Test")
self.cam = Camera()
self.light = Camera()
self.cam.set_size(800, 600)
self.light.set_size(2048, 2048)
self.light.distance = 100
self.shader = Shader()
self.initialized = False
def setup(self):
self.initialized = True
glClearColor(0,0,0,1.0);
glDepthFunc(GL_LESS)
glEnable(GL_DEPTH_TEST)
self.fbo = glGenFramebuffers(1);
self.shadowTexture = glGenTextures(1)
glBindFramebuffer(GL_FRAMEBUFFER, self.fbo)
w, h = self.light.size
glActiveTexture(GL_TEXTURE5)
glBindTexture(GL_TEXTURE_2D, self.shadowTexture)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, w, h, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, None)
glDrawBuffer(GL_NONE)
glReadBuffer(GL_NONE)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, self.fbo, 0)
FBOstatus = glCheckFramebufferStatus(GL_FRAMEBUFFER)
if FBOstatus != GL_FRAMEBUFFER_COMPLETE:
print ("GL_FRAMEBUFFER_COMPLETE_EXT failed, CANNOT use FBO\n");
glBindFramebuffer(GL_FRAMEBUFFER, 0)
#glActiveTexture(GL_TEXTURE0)
def draw(self):
glPushMatrix()
glTranslate(0, 10 ,0)
glColor4f(0, 1, 1, 1)
glutSolidCube(5)
glPopMatrix()
glPushMatrix()
glColor4f(0.5, 0.5, .5, 1)
glScale(100, 1, 100)
glutSolidCube(1)
glPopMatrix()
def apply_camera(self, cam):
cam.load_matrices()
model_view = glGetDoublev(GL_MODELVIEW_MATRIX);
projection = glGetDoublev(GL_PROJECTION_MATRIX);
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glMultMatrixd(projection)
glMultMatrixd(model_view)
glUniformMatrix4fv(self.shader.uniforms['camMatrix'], 1, False, glGetFloatv(GL_MODELVIEW_MATRIX))
glLoadIdentity()
def shadow_pass(self):
glUniform1i(self.shader.uniforms['useShadow'], 0)
glBindFramebuffer(GL_FRAMEBUFFER, self.fbo)
glClear(GL_DEPTH_BUFFER_BIT)
glCullFace(GL_FRONT)
self.apply_camera(self.light)
self.draw()
glBindFramebuffer(GL_FRAMEBUFFER, 0)
def final_pass(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
self.light.load_matrices()
model_view = glGetDoublev(GL_MODELVIEW_MATRIX);
projection = glGetDoublev(GL_PROJECTION_MATRIX);
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
bias = [ 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]
glLoadMatrixd(bias)
glMultMatrixd(projection)
glMultMatrixd(model_view)
glUniformMatrix4fv(self.shader.uniforms['shadowMatrix'], 1, False, glGetFloatv(GL_MODELVIEW_MATRIX))
glActiveTexture(GL_TEXTURE5)
glBindTexture(GL_TEXTURE_2D, self.shadowTexture)
glUniform1i(self.shader.uniforms['shadowMap'], 5)
glUniform1i(self.shader.uniforms['useShadow'], 1);
self.apply_camera(self.cam)
glLoadIdentity()
glCullFace(GL_BACK)
self.draw()
def render(self):
if not self.initialized: self.setup()
self.shader.use()
self.shadow_pass()
self.final_pass()
glutSwapBuffers()
def mouse_move(self, *args):
self.cam.on_mouse_move(*args)
self.light.on_mouse_move(*args)
def mouse_button(self, b, *args):
if b==0:
self.light.on_mouse_button(b, *args)
else:
self.cam.on_mouse_button(b, *args)
def main(self):
glutDisplayFunc(self.render)
glutIdleFunc(self.render)
glutMouseFunc(self.mouse_button)
glutMotionFunc(self.mouse_move)
glutReshapeFunc(self.cam.set_size)
#self.setup()
glutMainLoop()
if __name__ == '__main__':
test = Test()
test.main()
solved it, it is the binding issue, in shadow pass fragment shader I simply checked a boolean value to disable reading the texture, but that was not enough. I should have unbind the texture before shadow pass, that is mentioned in documentation as:
Quote from OpenGL Refence:
Special precautions need to be taken to avoid attaching a texture image to the currently bound framebuffer while the texture object is currently bound and potentially sampled by the current vertex or fragment shader.
NVIDIA ignores this, ATI behaves pretty much "undefined" as the documentation says.
// shadowCoord.z -= 0.0003;
Your commented out shadow bias is the solution to your z-fighting problem. Change it to some value e.g. += 0.0005 and you should be good to go.

Categories