I am trying to make block collision in Python PyOpenGL but I dont know how. I tried to do
for blocks in blockPositions:
#make camera go back up again
in my loop but it makes my game slow. Im also new in PyOpenGL and was following some tutorials but learned nothing with collisions and stuff.
I googled alot about making collision in PyOpenGL but found nothing. I only found unrelated results like stuff with other languages and other modules.
my code:
import glfw
from OpenGL.GL import *
from OpenGL.GL.shaders import compileProgram, compileShader
import pyrr
from TextureLoader import load_texture
import numpy as np
from camera import Camera
cam = Camera()
WIDTH, HEIGHT = 1280, 720
lastX, lastY = WIDTH / 2, HEIGHT / 2
first_mouse = True
left, right, forward, backward = False, False, False, False
def key_input_clb(window, key, scancode, action, mode):
global left, right, forward, backward
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:
forward = True
elif key == glfw.KEY_W and action == glfw.RELEASE:
forward = False
if key == glfw.KEY_S and action == glfw.PRESS:
backward = True
elif key == glfw.KEY_S and action == glfw.RELEASE:
backward = False
if key == glfw.KEY_A and action == glfw.PRESS:
left = True
elif key == glfw.KEY_A and action == glfw.RELEASE:
left = False
if key == glfw.KEY_D and action == glfw.PRESS:
right = True
elif key == glfw.KEY_D and action == glfw.RELEASE:
right = False
def do_movement():
if left:
cam.process_keyboard("LEFT", 0.05)
if right:
cam.process_keyboard("RIGHT", 0.05)
if forward:
cam.process_keyboard("FORWARD", 0.05)
if backward:
cam.process_keyboard("BACKWARD", 0.05)
def mouse_look_clb(window, xpos, ypos):
global first_mouse, lastX, lastY
if first_mouse:
lastX = xpos
lastY = ypos
first_mouse = False
xoffset = xpos - lastX
yoffset = lastY - ypos
lastX = xpos
lastY = ypos
cam.process_mouse_movement(xoffset, yoffset)
vertex_src = """
# version 330
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec2 a_texture;
layout(location = 2) in vec3 a_offset;
uniform mat4 model;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 move;
out vec2 v_texture;
void main()
{
vec3 final_pos = a_position + a_offset;
gl_Position = projection * view * move * model * vec4(final_pos, 1.0f);
v_texture = a_texture;
}
"""
fragment_src = """
# version 330
in vec2 v_texture;
out vec4 out_color;
uniform sampler2D s_texture;
void main()
{
out_color = texture(s_texture, v_texture);
}
"""
def window_resize_clb(window, width, height):
glViewport(0, 0, width, height)
projection = pyrr.matrix44.create_perspective_projection_matrix(45, width / height, 0.1, 100)
glUniformMatrix4fv(proj_loc, 1, GL_FALSE, projection)
if not glfw.init():
raise Exception("glfw can not be initialized!")
window = glfw.create_window(WIDTH, HEIGHT, "My OpenGL window", None, None)
if not window:
glfw.terminate()
raise Exception("glfw window can not be created!")
glfw.set_window_pos(window, 400, 200)
glfw.set_window_size_callback(window, window_resize_clb)
glfw.set_cursor_pos_callback(window, mouse_look_clb)
glfw.set_key_callback(window, key_input_clb)
glfw.set_input_mode(window, glfw.CURSOR, glfw.CURSOR_DISABLED)
glfw.make_context_current(window)
cube_buffer = [-0.5, -0.5, 0.5, 0.0, 0.0,
0.5, -0.5, 0.5, 1.0, 0.0,
0.5, 0.5, 0.5, 1.0, 1.0,
-0.5, 0.5, 0.5, 0.0, 1.0,
-0.5, -0.5, -0.5, 0.0, 0.0,
0.5, -0.5, -0.5, 1.0, 0.0,
0.5, 0.5, -0.5, 1.0, 1.0,
-0.5, 0.5, -0.5, 0.0, 1.0,
0.5, -0.5, -0.5, 0.0, 0.0,
0.5, 0.5, -0.5, 1.0, 0.0,
0.5, 0.5, 0.5, 1.0, 1.0,
0.5, -0.5, 0.5, 0.0, 1.0,
-0.5, 0.5, -0.5, 0.0, 0.0,
-0.5, -0.5, -0.5, 1.0, 0.0,
-0.5, -0.5, 0.5, 1.0, 1.0,
-0.5, 0.5, 0.5, 0.0, 1.0,
-0.5, -0.5, -0.5, 0.0, 0.0,
0.5, -0.5, -0.5, 1.0, 0.0,
0.5, -0.5, 0.5, 1.0, 1.0,
-0.5, -0.5, 0.5, 0.0, 1.0,
0.5, 0.5, -0.5, 0.0, 0.0,
-0.5, 0.5, -0.5, 1.0, 0.0,
-0.5, 0.5, 0.5, 1.0, 1.0,
0.5, 0.5, 0.5, 0.0, 1.0]
cube_buffer = np.array(cube_buffer, dtype=np.float32)
cube_indices = [ 0, 1, 2, 2, 3, 0,
4, 5, 6, 6, 7, 4,
8, 9, 10, 10, 11, 8,
12, 13, 14, 14, 15, 12,
16, 17, 18, 18, 19, 16,
20, 21, 22, 22, 23, 20]
cube_indices = np.array(cube_indices, dtype=np.uint32)
shader = compileProgram(compileShader(vertex_src, GL_VERTEX_SHADER), compileShader(fragment_src, GL_FRAGMENT_SHADER))
VAO = glGenVertexArrays(1)
VBO = glGenBuffers(1)
EBO = glGenBuffers(1)
glBindVertexArray(VAO)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, cube_buffer.nbytes, cube_buffer, GL_STATIC_DRAW)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, cube_indices.nbytes, cube_indices, GL_STATIC_DRAW)
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, cube_buffer.itemsize * 5, ctypes.c_void_p(0))
glEnableVertexAttribArray(1)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, cube_buffer.itemsize * 5, ctypes.c_void_p(12))
textures = glGenTextures(1)
load_texture("src/grass.png", textures)
instance_array = []
offset = 1
for z in range(0, 100, 2):
for x in range(0, 100, 2):
translation = pyrr.Vector3([0.0, 0.0, 0.0])
translation.x = x + offset
translation.y = y + offset
translation.z = z + offset
instance_array.append(translation)
len_of_instance_array = len(instance_array)
instance_array = np.array(instance_array, np.float32).flatten()
instanceVBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO)
glBufferData(GL_ARRAY_BUFFER, instance_array.nbytes, instance_array, GL_STATIC_DRAW)
glEnableVertexAttribArray(2)
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
glVertexAttribDivisor(2, 1)
glUseProgram(shader)
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, WIDTH / HEIGHT, 0.1, 100)
cube_pos = pyrr.matrix44.create_from_translation(pyrr.Vector3([-50.0, -50.0, -200.0]))
model_loc = glGetUniformLocation(shader, "model")
proj_loc = glGetUniformLocation(shader, "projection")
view_loc = glGetUniformLocation(shader, "view")
move_loc = glGetUniformLocation(shader, "move")
glUniformMatrix4fv(proj_loc, 1, GL_FALSE, projection)
glUniformMatrix4fv(model_loc, 1, GL_FALSE, cube_pos)
while not glfw.window_should_close(window):
glfw.poll_events()
do_movement()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
move = pyrr.matrix44.create_from_translation(pyrr.Vector3([0, 0, glfw.get_time()*8]))
glUniformMatrix4fv(move_loc, 1, GL_FALSE, move)
view = cam.get_view_matrix()
glUniformMatrix4fv(view_loc, 1, GL_FALSE, view)
glDrawElementsInstanced(GL_TRIANGLES, len(cube_indices), GL_UNSIGNED_INT, None, len_of_instance_array)
glfw.swap_buffers(window)
glfw.terminate()
About my project: I am working with OpenGL3.2 on Linux and I have built a basic application using GTK3 with a GLArea widget. The program is written in Python using the PyCharm IDE. The project interpreter is set to Python 3.8 and I have loaded up the following packages: Pillow 7.1.2, PyGObject 3.36.1, PyOpenGL 3.1.5, numpy 1.18, pyrr 0.10.3 and glfw 1.11.2
(see image at bottom)
My Problem:
I have a program that runs correctly using GLFW but will not run correctly using GTK3's GLArea. I am trying to render to a texture using a custom framebuffer object The GTK3 based program does not successfully render to the custom framebuffer. However, the GLFW based program renders just fine. There is no difference in the OpenGL code. I am only changing the windowing code. Is there something I need to enable with GTK3 in order to use custom framebuffers? The documentation for GTK3 (here) only indicates that special flags need to be set to enable the depth buffer and the stencil buffer (I have enabled both of these) but nothing related to custom texture buffers.
Any and all insights greatly appreciated.
Here is the problematic GTK3 GLArea program:
import sys
import gi, pyrr
import numpy
gi.require_version('Gtk', '3.0')
from pyrr import matrix44, Vector3
from gi.repository import Gtk
from OpenGL.GL import *
from OpenGL.GL.shaders import compileProgram
from PIL import Image
class GLCanvas(Gtk.GLArea):
def __init__(self):
Gtk.GLArea.__init__(self)
self.set_required_version(3, 2) # Sets the version of OpenGL required by this OpenGL program
self.connect("realize", self.on_initialize) # This signal is used to initialize the OpenGL state
self.connect("render", self.on_render) # This signal is emitted for each frame that is rendered
self.add_tick_callback(self.tick) # This is a frame time clock that is called each time a frame is rendered
self.set_start_time = False # Boolean to track whether the clock has been initialized
self.set_has_depth_buffer(True)
self.set_has_stencil_buffer(True)
def tick(self, widget, frame_clock):
self.current_frame_time = frame_clock.get_frame_time() # Gets the current timestamp in microseconds
if self.set_start_time == False: # Initializes the timer at the start of the program
self.starting_time = self.current_frame_time # Stores the timestamp set when the program was initalized
self.set_start_time = True # Prevents the initialization routine from running again in this instance
self.application_clock = (self.current_frame_time - self.starting_time)/1000000 # Calculate the total number of seconds that the program has been running
return True # Returns true to indicate that tick callback should contine to be called
def on_initialize(self, gl_area):
# Prints information about our OpenGL Context
opengl_context = self.get_context() # Retrieves the Gdk.GLContext used by gl_area
opengl_context.make_current() # Makes the Gdk.GLContext current to the drawing surfaced used by Gtk.GLArea
major, minor = opengl_context.get_version() # Gets the version of OpenGL currently used by the opengl_context
print("\033[93m OpenGL context created successfully.\n -- Using OpenGL Version \033[94m" + str(major) + "." + str(minor) + "\033[0m")
# Checks to see if there were errors creating the context
if gl_area.get_error() != None:
print(gl_area.get_error())
# Get information about current GTK GLArea canvas
window = gl_area.get_allocation()
w_width, w_height = window.width, window.height
self.aspect_ratio = w_width / w_height
self.cube_positions = [(1.0, 1.0, 0.0), (0.0, 0.0, 0.0), (2.0, 0.0, 0.0)]
self.plane_position = matrix44.create_from_translation(Vector3([-3.0, 1.0, 0.0]))
cube = [-0.5, -0.5, 0.5, 0.0, 0.0,
0.5, -0.5, 0.5, 1.0, 0.0,
0.5, 0.5, 0.5, 1.0, 1.0,
-0.5, 0.5, 0.5, 0.0, 1.0,
-0.5, -0.5, -0.5, 0.0, 0.0,
0.5, -0.5, -0.5, 1.0, 0.0,
0.5, 0.5, -0.5, 1.0, 1.0,
-0.5, 0.5, -0.5, 0.0, 1.0,
0.5, -0.5, -0.5, 0.0, 0.0,
0.5, 0.5, -0.5, 1.0, 0.0,
0.5, 0.5, 0.5, 1.0, 1.0,
0.5, -0.5, 0.5, 0.0, 1.0,
-0.5, 0.5, -0.5, 0.0, 0.0,
-0.5, -0.5, -0.5, 1.0, 0.0,
-0.5, -0.5, 0.5, 1.0, 1.0,
-0.5, 0.5, 0.5, 0.0, 1.0,
-0.5, -0.5, -0.5, 0.0, 0.0,
0.5, -0.5, -0.5, 1.0, 0.0,
0.5, -0.5, 0.5, 1.0, 1.0,
-0.5, -0.5, 0.5, 0.0, 1.0,
0.5, 0.5, -0.5, 0.0, 0.0,
-0.5, 0.5, -0.5, 1.0, 0.0,
-0.5, 0.5, 0.5, 1.0, 1.0,
0.5, 0.5, 0.5, 0.0, 1.0]
cube = numpy.array(cube, dtype=numpy.float32)
self.cube_indices = [0, 1, 2, 2, 3, 0,
4, 5, 6, 6, 7, 4,
8, 9, 10, 10, 11, 8,
12, 13, 14, 14, 15, 12,
16, 17, 18, 18, 19, 16,
20, 21, 22, 22, 23, 20]
self.cube_indices = numpy.array(self.cube_indices, dtype=numpy.uint32)
plane = [-0.5, -0.5, 0.0, 0.0, 0.0,
2.0, -0.5, 0.0, 1.0, 0.0,
2.0, 1.0, 0.0, 1.0, 1.0,
-0.5, 1.0, 0.0, 0.0, 1.0]
plane = numpy.array(plane, dtype=numpy.float32)
self.plane_indices = [0, 1, 2, 2, 3, 0]
self.plane_indices = numpy.array(self.plane_indices, dtype=numpy.uint32)
vertex_shader = """
#version 330
in layout(location = 0) vec3 position;
in layout(location = 1) vec2 textCoords;
uniform mat4 vp;
uniform mat4 model;
out vec2 outText;
void main()
{
gl_Position = vp * model * vec4(position, 1.0f);
outText = textCoords;
}
"""
fragment_shader = """
#version 330
in vec2 outText;
out vec4 outColor;
uniform sampler2D renderedTexture;
void main()
{
outColor = texture(renderedTexture, outText);
}
"""
shader = OpenGL.GL.shaders.compileProgram(OpenGL.GL.shaders.compileShader(vertex_shader, GL_VERTEX_SHADER),
OpenGL.GL.shaders.compileShader(fragment_shader, GL_FRAGMENT_SHADER))
# cube VAO
self.cube_vao = glGenVertexArrays(1)
glBindVertexArray(self.cube_vao)
cube_VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, cube_VBO)
glBufferData(GL_ARRAY_BUFFER, cube.itemsize * len(cube), cube, GL_STATIC_DRAW)
cube_EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cube_EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.cube_indices.itemsize * len(self.cube_indices), self.cube_indices, GL_STATIC_DRAW)
# position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, cube.itemsize * 5, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
# textures
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, cube.itemsize * 5, ctypes.c_void_p(12))
glEnableVertexAttribArray(1)
glBindVertexArray(0)
# plane VAO
self.plane_vao = glGenVertexArrays(1)
glBindVertexArray(self.plane_vao)
plane_VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, plane_VBO)
glBufferData(GL_ARRAY_BUFFER, plane.itemsize * len(plane), plane, GL_STATIC_DRAW)
plane_EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, plane_EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.plane_indices.itemsize * len(self.plane_indices), self.plane_indices,
GL_STATIC_DRAW)
# position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, plane.itemsize * 5, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
# textures
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, plane.itemsize * 5, ctypes.c_void_p(12))
glEnableVertexAttribArray(1)
glBindVertexArray(0)
###########################################################################################
self.plane_texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, self.plane_texture)
# texture wrapping params
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
# texture filtering params
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w_width, w_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, None)
glBindTexture(GL_TEXTURE_2D, 0)
depth_buff = glGenRenderbuffers(1)
glBindRenderbuffer(GL_RENDERBUFFER, depth_buff)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w_width, w_height)
self.FBO = glGenFramebuffers(1)
glBindFramebuffer(GL_FRAMEBUFFER, self.FBO)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self.plane_texture, 0)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_buff)
glBindFramebuffer(GL_FRAMEBUFFER, 0)
###########################################################################################
self.crate_texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, self.crate_texture)
# Set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
# Set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
# load image
image = Image.open("models/crate.jpg")
img_data = numpy.array(list(image.getdata()), numpy.uint8)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width, image.height, 0, GL_RGB, GL_UNSIGNED_BYTE, img_data)
glBindTexture(GL_TEXTURE_2D, 0)
###########################################################################################
glEnable(GL_DEPTH_TEST)
view = matrix44.create_from_translation(Vector3([0.0, 0.0, -5.0]))
projection = matrix44.create_perspective_projection_matrix(45.0, self.aspect_ratio, 0.1, 100.0)
vp = matrix44.multiply(view, projection)
glUseProgram(shader)
vp_loc = glGetUniformLocation(shader, "vp")
self.model_loc = glGetUniformLocation(shader, "model")
glUniformMatrix4fv(vp_loc, 1, GL_FALSE, vp)
return True
def on_render(self, gl_area, gl_context):
glClearColor(0.2, 0.25, 0.27, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
rot_y = pyrr.Matrix44.from_y_rotation(self.application_clock * 2)
# draw to the default frame buffer
glBindVertexArray(self.cube_vao)
glBindTexture(GL_TEXTURE_2D, self.crate_texture)
for i in range(len(self.cube_positions)):
model = matrix44.create_from_translation(self.cube_positions[i])
if i == 0:
glUniformMatrix4fv(self.model_loc, 1, GL_FALSE, rot_y * model)
elif i == 1:
glUniformMatrix4fv(self.model_loc, 1, GL_FALSE, model)
else:
glUniformMatrix4fv(self.model_loc, 1, GL_FALSE, model)
glDrawElements(GL_TRIANGLES, len(self.cube_indices), GL_UNSIGNED_INT, None)
# draw to the custom frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, self.FBO)
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
for i in range(len(self.cube_positions)):
model = matrix44.create_from_translation(self.cube_positions[i])
if i == 0:
glUniformMatrix4fv(self.model_loc, 1, GL_FALSE, rot_y * model)
elif i == 1:
glUniformMatrix4fv(self.model_loc, 1, GL_FALSE, model)
else:
glUniformMatrix4fv(self.model_loc, 1, GL_FALSE, model)
glDrawElements(GL_TRIANGLES, len(self.cube_indices), GL_UNSIGNED_INT, None)
glBindVertexArray(0)
glBindFramebuffer(GL_FRAMEBUFFER, 0)
# draw the plane
glBindVertexArray(self.plane_vao)
glBindTexture(GL_TEXTURE_2D, self.plane_texture)
glUniformMatrix4fv(self.model_loc, 1, GL_FALSE, self.plane_position)
glDrawElements(GL_TRIANGLES, len(self.plane_indices), GL_UNSIGNED_INT, None)
glBindVertexArray(0)
self.queue_draw() # Schedules a redraw for Gtk.GLArea
class RootWindow(Gtk.Application):
def __init__(self):
Gtk.Application.__init__(self)
def do_activate(self):
window = Gtk.Window(application=self)
window.set_title("Render To Texture")
window.set_default_size(1280, 720)
window.set_position(Gtk.WindowPosition.CENTER)
window.add(GLCanvas())
window.show_all()
win = RootWindow()
exit_status = win.run(sys.argv)
sys.exit(exit_status)
Here is the full working GLFW program (original source):
import glfw
from OpenGL.GL import *
import OpenGL.GL.shaders
import numpy
import pyrr
from pyrr import matrix44, Vector3
from PIL import Image
def window_resize(window, width, height):
glViewport(0, 0, width, height)
cube_positions = [(1.0, 1.0, 0.0), (0.0, 0.0, 0.0), (2.0, 0.0, 0.0)]
plane_position = matrix44.create_from_translation(Vector3([-3.0, 1.0, 0.0]))
def main():
if not glfw.init():
return
w_width, w_height = 1280, 720
aspect_ratio = w_width / w_height
window = glfw.create_window(w_width, w_height, "My OpenGL window", None, None)
if not window:
glfw.terminate()
return
glfw.make_context_current(window)
glfw.set_window_size_callback(window, window_resize)
cube = [-0.5, -0.5, 0.5, 0.0, 0.0,
0.5, -0.5, 0.5, 1.0, 0.0,
0.5, 0.5, 0.5, 1.0, 1.0,
-0.5, 0.5, 0.5, 0.0, 1.0,
-0.5, -0.5, -0.5, 0.0, 0.0,
0.5, -0.5, -0.5, 1.0, 0.0,
0.5, 0.5, -0.5, 1.0, 1.0,
-0.5, 0.5, -0.5, 0.0, 1.0,
0.5, -0.5, -0.5, 0.0, 0.0,
0.5, 0.5, -0.5, 1.0, 0.0,
0.5, 0.5, 0.5, 1.0, 1.0,
0.5, -0.5, 0.5, 0.0, 1.0,
-0.5, 0.5, -0.5, 0.0, 0.0,
-0.5, -0.5, -0.5, 1.0, 0.0,
-0.5, -0.5, 0.5, 1.0, 1.0,
-0.5, 0.5, 0.5, 0.0, 1.0,
-0.5, -0.5, -0.5, 0.0, 0.0,
0.5, -0.5, -0.5, 1.0, 0.0,
0.5, -0.5, 0.5, 1.0, 1.0,
-0.5, -0.5, 0.5, 0.0, 1.0,
0.5, 0.5, -0.5, 0.0, 0.0,
-0.5, 0.5, -0.5, 1.0, 0.0,
-0.5, 0.5, 0.5, 1.0, 1.0,
0.5, 0.5, 0.5, 0.0, 1.0]
cube = numpy.array(cube, dtype=numpy.float32)
cube_indices = [ 0, 1, 2, 2, 3, 0,
4, 5, 6, 6, 7, 4,
8, 9, 10, 10, 11, 8,
12, 13, 14, 14, 15, 12,
16, 17, 18, 18, 19, 16,
20, 21, 22, 22, 23, 20]
cube_indices = numpy.array(cube_indices, dtype=numpy.uint32)
plane = [-0.5, -0.5, 0.0, 0.0, 0.0,
2.0, -0.5, 0.0, 1.0, 0.0,
2.0, 1.0, 0.0, 1.0, 1.0,
-0.5, 1.0, 0.0, 0.0, 1.0]
plane = numpy.array(plane, dtype=numpy.float32)
plane_indices = [0, 1, 2, 2, 3, 0]
plane_indices = numpy.array(plane_indices, dtype=numpy.uint32)
vertex_shader = """
#version 330
in layout(location = 0) vec3 position;
in layout(location = 1) vec2 textCoords;
uniform mat4 vp;
uniform mat4 model;
out vec2 outText;
void main()
{
gl_Position = vp * model * vec4(position, 1.0f);
outText = textCoords;
}
"""
fragment_shader = """
#version 330
in vec2 outText;
out vec4 outColor;
uniform sampler2D renderedTexture;
void main()
{
outColor = texture(renderedTexture, outText);
}
"""
shader = OpenGL.GL.shaders.compileProgram(OpenGL.GL.shaders.compileShader(vertex_shader, GL_VERTEX_SHADER),
OpenGL.GL.shaders.compileShader(fragment_shader, GL_FRAGMENT_SHADER))
# cube VAO
cube_vao = glGenVertexArrays(1)
glBindVertexArray(cube_vao)
cube_VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, cube_VBO)
glBufferData(GL_ARRAY_BUFFER, cube.itemsize * len(cube), cube, GL_STATIC_DRAW)
cube_EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cube_EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, cube_indices.itemsize * len(cube_indices), cube_indices, GL_STATIC_DRAW)
# position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, cube.itemsize * 5, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
# textures
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, cube.itemsize * 5, ctypes.c_void_p(12))
glEnableVertexAttribArray(1)
glBindVertexArray(0)
# plane VAO
plane_vao = glGenVertexArrays(1)
glBindVertexArray(plane_vao)
plane_VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, plane_VBO)
glBufferData(GL_ARRAY_BUFFER, plane.itemsize * len(plane), plane, GL_STATIC_DRAW)
plane_EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, plane_EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, plane_indices.itemsize * len(plane_indices), plane_indices, GL_STATIC_DRAW)
# position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, plane.itemsize * 5, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
# textures
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, plane.itemsize * 5, ctypes.c_void_p(12))
glEnableVertexAttribArray(1)
glBindVertexArray(0)
###########################################################################################
plane_texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, plane_texture)
# texture wrapping params
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
# texture filtering params
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w_width, w_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, None)
glBindTexture(GL_TEXTURE_2D, 0)
depth_buff = glGenRenderbuffers(1)
glBindRenderbuffer(GL_RENDERBUFFER, depth_buff)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w_width, w_height)
FBO = glGenFramebuffers(1)
glBindFramebuffer(GL_FRAMEBUFFER, FBO)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, plane_texture, 0)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_buff)
glBindFramebuffer(GL_FRAMEBUFFER, 0)
###########################################################################################
crate_texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, crate_texture)
# Set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
# Set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
# load image
image = Image.open("res/crate.jpg")
img_data = numpy.array(list(image.getdata()), numpy.uint8)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width, image.height, 0, GL_RGB, GL_UNSIGNED_BYTE, img_data)
glBindTexture(GL_TEXTURE_2D, 0)
###########################################################################################
glEnable(GL_DEPTH_TEST)
view = matrix44.create_from_translation(Vector3([0.0, 0.0, -5.0]))
projection = matrix44.create_perspective_projection_matrix(45.0, aspect_ratio, 0.1, 100.0)
vp = matrix44.multiply(view, projection)
glUseProgram(shader)
vp_loc = glGetUniformLocation(shader, "vp")
model_loc = glGetUniformLocation(shader, "model")
glUniformMatrix4fv(vp_loc, 1, GL_FALSE, vp)
while not glfw.window_should_close(window):
glfw.poll_events()
glClearColor(0.2, 0.25, 0.27, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
rot_y = pyrr.Matrix44.from_y_rotation(glfw.get_time() * 2)
# draw to the default frame buffer
glBindVertexArray(cube_vao)
glBindTexture(GL_TEXTURE_2D, crate_texture)
for i in range(len(cube_positions)):
model = matrix44.create_from_translation(cube_positions[i])
if i == 0:
glUniformMatrix4fv(model_loc, 1, GL_FALSE, rot_y * model)
elif i == 1:
glUniformMatrix4fv(model_loc, 1, GL_FALSE, model)
else:
glUniformMatrix4fv(model_loc, 1, GL_FALSE, model)
glDrawElements(GL_TRIANGLES, len(cube_indices), GL_UNSIGNED_INT, None)
# draw to the custom frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, FBO)
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
for i in range(len(cube_positions)):
model = matrix44.create_from_translation(cube_positions[i])
if i == 0:
glUniformMatrix4fv(model_loc, 1, GL_FALSE, rot_y * model)
elif i == 1:
glUniformMatrix4fv(model_loc, 1, GL_FALSE, model)
else:
glUniformMatrix4fv(model_loc, 1, GL_FALSE, model)
glDrawElements(GL_TRIANGLES, len(cube_indices), GL_UNSIGNED_INT, None)
glBindVertexArray(0)
glBindFramebuffer(GL_FRAMEBUFFER, 0)
# draw the plane
glBindVertexArray(plane_vao)
glBindTexture(GL_TEXTURE_2D, plane_texture)
glUniformMatrix4fv(model_loc, 1, GL_FALSE, plane_position)
glDrawElements(GL_TRIANGLES, len(plane_indices), GL_UNSIGNED_INT, None)
glBindVertexArray(0)
glfw.swap_buffers(window)
glfw.terminate()
if __name__ == "__main__":
main()
Solution
It turns out that GLArea is not using framebuffer 0 as the default. The issue here is resetting the framebuffer back to 0 at the end of the rendering loop with glBindFramebuffer(GL_FRAMEBUFFER, 0). Instead, use default_ID = glGetIntegerv(GL_FRAMEBUFFER_BINDING) at the beginning of the rendering loop to get the current default ID. At the end of the loop, reset to the default framebuffer using glBindFramebuffer(GL_FRAMEBUFFER, default_ID).
import sys
import gi, pyrr
import numpy
gi.require_version('Gtk', '3.0')
from pyrr import matrix44, Vector3
from gi.repository import Gtk
from OpenGL.GL import *
from OpenGL.GL.shaders import compileProgram
from PIL import Image
class GLCanvas(Gtk.GLArea):
def __init__(self):
Gtk.GLArea.__init__(self)
self.set_required_version(3, 2) # Sets the version of OpenGL required by this OpenGL program
self.connect("realize", self.on_initialize) # This signal is used to initialize the OpenGL state
self.connect("render", self.on_render) # This signal is emitted for each frame that is rendered
self.add_tick_callback(self.tick) # This is a frame time clock that is called each time a frame is rendered
self.set_start_time = False # Boolean to track whether the clock has been initialized
self.set_has_depth_buffer(True)
self.set_has_stencil_buffer(True)
def tick(self, widget, frame_clock):
self.current_frame_time = frame_clock.get_frame_time() # Gets the current timestamp in microseconds
if self.set_start_time == False: # Initializes the timer at the start of the program
self.starting_time = self.current_frame_time # Stores the timestamp set when the program was initalized
self.set_start_time = True # Prevents the initialization routine from running again in this instance
self.application_clock = (self.current_frame_time - self.starting_time)/1000000 # Calculate the total number of seconds that the program has been running
return True # Returns true to indicate that tick callback should contine to be called
def on_initialize(self, gl_area):
# Prints information about our OpenGL Context
opengl_context = self.get_context() # Retrieves the Gdk.GLContext used by gl_area
opengl_context.make_current() # Makes the Gdk.GLContext current to the drawing surfaced used by Gtk.GLArea
major, minor = opengl_context.get_version() # Gets the version of OpenGL currently used by the opengl_context
# https://stackoverflow.com/questions/287871/how-to-print-colored-text-in-terminal-in-python
print("\033[93m OpenGL context created successfully.\n -- Using OpenGL Version \033[94m" + str(major) + "." + str(minor) + "\033[0m")
# Checks to see if there were errors creating the context
if gl_area.get_error() != None:
print(gl_area.get_error())
# Get information about current GTK GLArea canvas
window = gl_area.get_allocation()
w_width, w_height = window.width, window.height
self.aspect_ratio = w_width / w_height
self.cube_positions = [(1.0, 1.0, 0.0), (0.0, 0.0, 0.0), (2.0, 0.0, 0.0)]
self.plane_position = matrix44.create_from_translation(Vector3([-3.0, 1.0, 0.0]))
cube = [-0.5, -0.5, 0.5, 0.0, 0.0,
0.5, -0.5, 0.5, 1.0, 0.0,
0.5, 0.5, 0.5, 1.0, 1.0,
-0.5, 0.5, 0.5, 0.0, 1.0,
-0.5, -0.5, -0.5, 0.0, 0.0,
0.5, -0.5, -0.5, 1.0, 0.0,
0.5, 0.5, -0.5, 1.0, 1.0,
-0.5, 0.5, -0.5, 0.0, 1.0,
0.5, -0.5, -0.5, 0.0, 0.0,
0.5, 0.5, -0.5, 1.0, 0.0,
0.5, 0.5, 0.5, 1.0, 1.0,
0.5, -0.5, 0.5, 0.0, 1.0,
-0.5, 0.5, -0.5, 0.0, 0.0,
-0.5, -0.5, -0.5, 1.0, 0.0,
-0.5, -0.5, 0.5, 1.0, 1.0,
-0.5, 0.5, 0.5, 0.0, 1.0,
-0.5, -0.5, -0.5, 0.0, 0.0,
0.5, -0.5, -0.5, 1.0, 0.0,
0.5, -0.5, 0.5, 1.0, 1.0,
-0.5, -0.5, 0.5, 0.0, 1.0,
0.5, 0.5, -0.5, 0.0, 0.0,
-0.5, 0.5, -0.5, 1.0, 0.0,
-0.5, 0.5, 0.5, 1.0, 1.0,
0.5, 0.5, 0.5, 0.0, 1.0]
cube = numpy.array(cube, dtype=numpy.float32)
self.cube_indices = [0, 1, 2, 2, 3, 0,
4, 5, 6, 6, 7, 4,
8, 9, 10, 10, 11, 8,
12, 13, 14, 14, 15, 12,
16, 17, 18, 18, 19, 16,
20, 21, 22, 22, 23, 20]
self.cube_indices = numpy.array(self.cube_indices, dtype=numpy.uint32)
plane = [-0.5, -0.5, 0.0, 0.0, 0.0,
2.0, -0.5, 0.0, 1.0, 0.0,
2.0, 1.0, 0.0, 1.0, 1.0,
-0.5, 1.0, 0.0, 0.0, 1.0]
plane = numpy.array(plane, dtype=numpy.float32)
self.plane_indices = [0, 1, 2, 2, 3, 0]
self.plane_indices = numpy.array(self.plane_indices, dtype=numpy.uint32)
vertex_shader = """
#version 330
in vec3 position;
in vec2 textCoords;
uniform mat4 vp;
uniform mat4 model;
out vec2 outText;
void main()
{
gl_Position = vp * model * vec4(position, 1.0f);
outText = textCoords;
}
"""
fragment_shader = """
#version 330
in vec2 outText;
out vec4 outColor;
uniform sampler2D renderedTexture;
void main()
{
outColor = texture(renderedTexture, outText);
}
"""
shader = OpenGL.GL.shaders.compileProgram(OpenGL.GL.shaders.compileShader(vertex_shader, GL_VERTEX_SHADER),
OpenGL.GL.shaders.compileShader(fragment_shader, GL_FRAGMENT_SHADER))
# cube VAO
self.cube_vao = glGenVertexArrays(1)
glBindVertexArray(self.cube_vao)
cube_VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, cube_VBO)
glBufferData(GL_ARRAY_BUFFER, cube.itemsize * len(cube), cube, GL_STATIC_DRAW)
cube_EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cube_EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.cube_indices.itemsize * len(self.cube_indices), self.cube_indices, GL_STATIC_DRAW)
# position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, cube.itemsize * 5, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
# textures
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, cube.itemsize * 5, ctypes.c_void_p(12))
glEnableVertexAttribArray(1)
glBindVertexArray(0)
# plane VAO
self.plane_vao = glGenVertexArrays(1)
glBindVertexArray(self.plane_vao)
plane_VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, plane_VBO)
glBufferData(GL_ARRAY_BUFFER, plane.itemsize * len(plane), plane, GL_STATIC_DRAW)
plane_EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, plane_EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.plane_indices.itemsize * len(self.plane_indices), self.plane_indices,
GL_STATIC_DRAW)
# position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, plane.itemsize * 5, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
# textures
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, plane.itemsize * 5, ctypes.c_void_p(12))
glEnableVertexAttribArray(1)
glBindVertexArray(0)
###########################################################################################
self.plane_texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, self.plane_texture)
# texture wrapping params
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
# texture filtering params
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w_width, w_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, None)
glBindTexture(GL_TEXTURE_2D, 0)
depth_buff = glGenRenderbuffers(1)
glBindRenderbuffer(GL_RENDERBUFFER, depth_buff)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w_width, w_height)
self.FBO = glGenFramebuffers(1)
glBindFramebuffer(GL_FRAMEBUFFER, self.FBO)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self.plane_texture, 0)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_buff)
glBindFramebuffer(GL_FRAMEBUFFER, 0)
###########################################################################################
self.crate_texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, self.crate_texture)
# Set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
# Set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
# load image
image = Image.open("models/crate.jpg")
img_data = numpy.array(list(image.getdata()), numpy.uint8)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width, image.height, 0, GL_RGB, GL_UNSIGNED_BYTE, img_data)
glBindTexture(GL_TEXTURE_2D, 0)
###########################################################################################
glEnable(GL_DEPTH_TEST)
view = matrix44.create_from_translation(Vector3([0.0, 0.0, -5.0]))
projection = matrix44.create_perspective_projection_matrix(45.0, self.aspect_ratio, 0.1, 100.0)
vp = matrix44.multiply(view, projection)
glUseProgram(shader)
vp_loc = glGetUniformLocation(shader, "vp")
self.model_loc = glGetUniformLocation(shader, "model")
glUniformMatrix4fv(vp_loc, 1, GL_FALSE, vp)
return True
def on_render(self, gl_area, gl_context):
default_ID = glGetIntegerv(GL_FRAMEBUFFER_BINDING)
glClearColor(0.2, 0.25, 0.27, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
rot_y = pyrr.Matrix44.from_y_rotation(self.application_clock * 2)
# draw to the default frame buffer
glBindVertexArray(self.cube_vao)
glBindTexture(GL_TEXTURE_2D, self.crate_texture)
for i in range(len(self.cube_positions)):
model = matrix44.create_from_translation(self.cube_positions[i])
if i == 0:
glUniformMatrix4fv(self.model_loc, 1, GL_FALSE, rot_y * model)
elif i == 1:
glUniformMatrix4fv(self.model_loc, 1, GL_FALSE, model)
else:
glUniformMatrix4fv(self.model_loc, 1, GL_FALSE, model)
glDrawElements(GL_TRIANGLES, len(self.cube_indices), GL_UNSIGNED_INT, None)
# draw to the custom frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, self.FBO)
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
for i in range(len(self.cube_positions)):
model = matrix44.create_from_translation(self.cube_positions[i])
if i == 0:
glUniformMatrix4fv(self.model_loc, 1, GL_FALSE, rot_y * model)
elif i == 1:
glUniformMatrix4fv(self.model_loc, 1, GL_FALSE, model)
else:
glUniformMatrix4fv(self.model_loc, 1, GL_FALSE, model)
glDrawElements(GL_TRIANGLES, len(self.cube_indices), GL_UNSIGNED_INT, None)
glBindFramebuffer(GL_FRAMEBUFFER, default_ID)
glBindVertexArray(0)
# draw the plane
glBindVertexArray(self.plane_vao)
glBindTexture(GL_TEXTURE_2D, self.plane_texture)
glUniformMatrix4fv(self.model_loc, 1, GL_FALSE, self.plane_position)
glDrawElements(GL_TRIANGLES, len(self.plane_indices), GL_UNSIGNED_INT, None)
glBindVertexArray(0)
self.queue_draw() # Schedules a redraw for Gtk.GLArea
class RootWindow(Gtk.Application):
def __init__(self):
Gtk.Application.__init__(self)
def do_activate(self):
window = Gtk.Window(application=self)
window.set_title("Render To Texture")
window.set_default_size(1280, 720)
window.set_position(Gtk.WindowPosition.CENTER)
window.add(GLCanvas())
window.show_all()
win = RootWindow()
exit_status = win.run(sys.argv)
sys.exit(exit_status)
I'm relatively new to PyOpenGL and started learning about transformations and model, view, and projection matrices. Which was fine until I compiled and ran my code, and found that my object suddenly disappeared, to be honest I don't know if OpenGL is clipping my object or simply not showing it because of some camera error, but I believe it has something to do with the projection matrix implemented in my code, because when I'd cut the projection matrix out of my code and would run my program, everything was suddenly working again, except for the fact I wouldn't have perspective projection implemented. Well anyways any would be very appreciated :D
Here's my PyOpenGL code.
import OpenGL, PIL, pygame, numpy, pyrr, math, sys, os
from OpenGL.GL import *
from PIL import Image
from pyrr import Matrix44, Vector4, Vector3, Quaternion
VERT_DATA = numpy.array([0.5, 0.5, 0.0,
0.5, -0.5, 0.0,
-0.5, -0.5, 0.0,
-0.5, 0.5, 0.0],
dtype="float32")
COLOR_DATA = numpy.array([1.0, 0.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
0.0, 1.0, 1.0, 1.0],
dtype="float32")
TEXTURE_COORD_DATA = numpy.array([0.5, 0.5,
0.5, -0.5,
-0.5, -0.5,
-0.5, 0.5],
dtype="float32")
INDICES = numpy.array([0, 1, 3,
1, 2, 3],
dtype="int32")
class GLProgram:
def __init__(self):
self.gl_program = glCreateProgram()
self.mvp_matrix = self.projection()
self.shaders()
self.gl_buffers()
def gl_texture(self, texture_path):
image = Image.open(texture_path).transpose(Image.FLIP_TOP_BOTTOM)
image_data = numpy.fromstring(image.tobytes(), numpy.uint8)
width, height = image.size
texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image_data)
glGenerateMipmap(GL_TEXTURE_2D)
return texture
def gl_buffers(self):
self.vao = glGenVertexArrays(1)
glBindVertexArray(self.vao)
self.pos_vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.pos_vbo)
glBufferData(GL_ARRAY_BUFFER, VERT_DATA, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(0)
self.text_coord_vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.text_coord_vbo)
glBufferData(GL_ARRAY_BUFFER, TEXTURE_COORD_DATA, GL_STATIC_DRAW)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(1)
self.pos_ebo = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.pos_ebo)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.pos_ebo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, INDICES, GL_STATIC_DRAW)
self.brick_texture = self.gl_texture("check.jpg")
def shaders(self):
vertex_shader = glCreateShader(GL_VERTEX_SHADER)
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER)
with open("VertexShader.vert", "r") as vert_file:
vert_source = vert_file.read()
with open("FragmentShader.frag", "r") as frag_file:
frag_source = frag_file.read()
glShaderSource(vertex_shader, vert_source)
glShaderSource(fragment_shader, frag_source)
glCompileShader(vertex_shader)
if not glGetShaderiv(vertex_shader, GL_COMPILE_STATUS):
info_log = glGetShaderInfoLog(vertex_shader)
print ("Compilation Failure for " + vertex_shader + " shader:\n" + info_log)
glCompileShader(fragment_shader)
if not glGetShaderiv(fragment_shader, GL_COMPILE_STATUS):
info_log = glGetShaderInfoLog(fragment_shader)
print ("Compilation Failure for " + fragment_shader + " shader:\n" + info_log)
glAttachShader(self.gl_program, vertex_shader)
glAttachShader(self.gl_program, fragment_shader)
glLinkProgram(self.gl_program)
glDeleteShader(vertex_shader)
glDeleteShader(fragment_shader)
def projection(self):
scale_matrix = pyrr.matrix44.create_from_scale(Vector3([1, 1, 1]))
rot_matrix = Matrix44.identity()
trans_matrix = pyrr.matrix44.create_from_translation(Vector3([1, 1, 0]))
model_matrix = scale_matrix * rot_matrix * trans_matrix
view_matrix = pyrr.matrix44.create_look_at(numpy.array([4, 3, 3]), numpy.array([1, 1, 0]), numpy.array([0, 1, 0]))
proj_matrix = pyrr.matrix44.create_perspective_projection_matrix(45.0, 1280/720, 0.1, 1000.0)
mvp_matrix = proj_matrix * view_matrix * model_matrix
return mvp_matrix
def display(self):
glEnable(GL_DEPTH_TEST)
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glUseProgram(self.gl_program)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, self.brick_texture)
texture_uniform = glGetUniformLocation(self.gl_program, "the_texture")
glUniform1i(texture_uniform, 0)
trans_uniform = glGetUniformLocation(self.gl_program, "mvp")
glUniformMatrix4fv(trans_uniform, 1, GL_FALSE, self.mvp_matrix)
glBindVertexArray(self.vao)
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
glUseProgram(0)
def main():
pygame.init()
os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.display.set_caption("3D Graphics")
pygame.display.set_mode((1280, 720), pygame.DOUBLEBUF | pygame.OPENGL)
clock = pygame.time.Clock()
gl = GLProgram()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
clock.tick(60)
gl.display()
pygame.display.flip()
if __name__ == "__main__":
main()
Vertex Shader:
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 text_coord;
out vec2 final_text_coord;
uniform mat4 mvp;
void main() {
gl_Position = mvp * vec4(position, 1.0);
final_text_coord = text_coord;
}
Fragment Shader:
#version 330 core
in vec2 final_text_coord;
out vec4 frag_color;
uniform sampler2D the_texture;
void main() {
frag_color = texture(the_texture, final_text_coord);
}
There were a couple of issues with your code:
Matrix multiplication. Multiplying two 2-dimensional numpy arrays together results in a component-wise product, not in matrix multiplication. Using numpy.matmul will solve that one.
The matrix conventions were not clear.
As the documentation for pyrr states, matrices are laid out as row-major in memory, which is the opposite of GL's default convention. Furthermore, pyrr creates the matrices transposed to standrad GL conventions. One might think that both things will cancel itself out, that is only true to a certain extent. It will break whenever you actually do some other operations on these matrices (like multiplication), which will then use the native convention, and screw things up.
I did hack around in your code, providing two different solutions. Since you changed your code a bit between your question and your answer, I ended up with a wild mix of both versions. I also disabled the texturing stuff because I'm lacking some libraries (and the image files). I modified the fragment shader accordingly, but that is not important.
import OpenGL, PIL, pygame, numpy, pyrr, math, sys, os
from OpenGL.GL import *
from PIL import Image
from pyrr import Matrix44, Vector4, Vector3, Quaternion
VERT_DATA = numpy.array([0.5, 0.5, 0.0,
0.5, -0.5, 0.0,
-0.5, -0.5, 0.0,
-0.5, 0.5, 0.0],
dtype="float32")
COLOR_DATA = numpy.array([1.0, 0.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
0.0, 1.0, 1.0, 1.0],
dtype="float32")
TEXTURE_COORD_DATA = numpy.array([0.5, 0.5,
0.5, -0.5,
-0.5, -0.5,
-0.5, 0.5],
dtype="float32")
INDICES = numpy.array([0, 1, 3,
1, 2, 3],
dtype="int32")
WINDOW_WIDTH=1280
WINDOW_HEIGHT=720
class GLProgram:
def __init__(self):
self.gl_program = glCreateProgram()
self.mvp_matrix = self.projection()
self.shaders()
self.gl_buffers()
self.cube_model_matrix, self.cube_view_matrix, self.cube_proj_matrix = self.gl_translate(Vector3([1.0, 1.0, 1.0]), 45.0, Vector3([0.5, 0.5, 0.5]))
self.cube_mvp = self.gl_translate3(Vector3([1.0, 1.0, 1.0]), -45.0, Vector3([0.5, 0.5, 0.5]))
def gl_texture(self, texture_path):
return 0
def gl_buffers(self):
self.vao = glGenVertexArrays(1)
glBindVertexArray(self.vao)
self.pos_vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.pos_vbo)
glBufferData(GL_ARRAY_BUFFER, VERT_DATA, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(0)
self.text_coord_vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.text_coord_vbo)
glBufferData(GL_ARRAY_BUFFER, TEXTURE_COORD_DATA, GL_STATIC_DRAW)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(1)
self.pos_ebo = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.pos_ebo)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.pos_ebo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, INDICES, GL_STATIC_DRAW)
self.brick_texture = self.gl_texture("check.jpg")
def shaders(self):
vertex_shader = glCreateShader(GL_VERTEX_SHADER)
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER)
with open("VertexShader.vert", "r") as vert_file:
vert_source = vert_file.read()
with open("FragmentShader.frag", "r") as frag_file:
frag_source = frag_file.read()
glShaderSource(vertex_shader, vert_source)
glShaderSource(fragment_shader, frag_source)
glCompileShader(vertex_shader)
if not glGetShaderiv(vertex_shader, GL_COMPILE_STATUS):
info_log = glGetShaderInfoLog(vertex_shader)
print ("Compilation Failure for " + vertex_shader + " shader:\n" + info_log)
glCompileShader(fragment_shader)
if not glGetShaderiv(fragment_shader, GL_COMPILE_STATUS):
info_log = glGetShaderInfoLog(fragment_shader)
print ("Compilation Failure for " + fragment_shader + " shader:\n" + info_log)
glAttachShader(self.gl_program, vertex_shader)
glAttachShader(self.gl_program, fragment_shader)
glLinkProgram(self.gl_program)
glDeleteShader(vertex_shader)
glDeleteShader(fragment_shader)
def projection(self):
scale_matrix = pyrr.matrix44.create_from_scale(Vector3([1, 1, 1]))
rot_matrix = Matrix44.identity()
trans_matrix = pyrr.matrix44.create_from_translation(Vector3([1, 1, 0]))
model_matrix = scale_matrix * rot_matrix * trans_matrix
view_matrix = pyrr.matrix44.create_look_at(numpy.array([4, 3, 3]), numpy.array([1, 1, 0]), numpy.array([0, 1, 0]))
proj_matrix = pyrr.matrix44.create_perspective_projection_matrix(45.0, 1280/720, 0.1, 1000.0)
mvp_matrix = proj_matrix * view_matrix * model_matrix
return mvp_matrix
def gl_translate(self, translation, rotation, scale):
trans_matrix = pyrr.matrix44.create_from_translation(translation)
rot_matrix = numpy.transpose(pyrr.matrix44.create_from_y_rotation(rotation))
scale_matrix = numpy.transpose(pyrr.matrix44.create_from_scale(scale))
model_matrix = scale_matrix * rot_matrix * trans_matrix
view_matrix = pyrr.matrix44.create_look_at(numpy.array([2.0, 2.0, 3.0], dtype="float32"),
numpy.array([0.0, 0.0, 0.0], dtype="float32"),
numpy.array([0.0, 1.0, 0.0], dtype="float32"))
proj_matrix = pyrr.matrix44.create_perspective_projection(45.0, WINDOW_WIDTH/WINDOW_HEIGHT, 0.1, 200.0)
return model_matrix, view_matrix, proj_matrix
def gl_translate2(self, translation, rotation, scale):
trans_matrix = pyrr.matrix44.create_from_translation(translation)
rot_matrix = pyrr.matrix44.create_from_y_rotation(rotation)
scale_matrix = pyrr.matrix44.create_from_scale(scale)
model_matrix = numpy.matmul(numpy.matmul(scale_matrix,rot_matrix),trans_matrix)
view_matrix = pyrr.matrix44.create_look_at(numpy.array([2.0, 2.0, 3.0], dtype="float32"),
numpy.array([0.0, 0.0, 0.0], dtype="float32"),
numpy.array([0.0, 1.0, 0.0], dtype="float32"))
proj_matrix = pyrr.matrix44.create_perspective_projection(45.0, WINDOW_WIDTH/WINDOW_HEIGHT, 0.1, 200.0)
m = numpy.matmul(numpy.matmul(model_matrix,view_matrix),proj_matrix)
return m
def gl_translate3(self, translation, rotation, scale):
trans_matrix = numpy.transpose(pyrr.matrix44.create_from_translation(translation))
rot_matrix = numpy.transpose(pyrr.matrix44.create_from_y_rotation(rotation))
scale_matrix = numpy.transpose(pyrr.matrix44.create_from_scale(scale))
model_matrix = numpy.matmul(numpy.matmul(trans_matrix,rot_matrix),scale_matrix)
view_matrix = numpy.transpose(pyrr.matrix44.create_look_at(numpy.array([2.0, 2.0, 3.0], dtype="float32"),
numpy.array([0.0, 0.0, 0.0], dtype="float32"),
numpy.array([0.0, 1.0, 0.0], dtype="float32")))
proj_matrix = numpy.transpose(pyrr.matrix44.create_perspective_projection(45.0, WINDOW_WIDTH/WINDOW_HEIGHT, 0.1, 200.0))
m = numpy.matmul(numpy.matmul(proj_matrix,view_matrix),model_matrix)
return numpy.transpose(m)
def display(self):
glEnable(GL_DEPTH_TEST)
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glUseProgram(self.gl_program)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, self.brick_texture)
texture_uniform = glGetUniformLocation(self.gl_program, "the_texture")
glUniform1i(texture_uniform, 0)
trans_uniform = glGetUniformLocation(self.gl_program, "mvp")
glUniformMatrix4fv(trans_uniform, 1, GL_FALSE, self.cube_mvp)
#model_location = glGetUniformLocation(self.gl_program, "model")
#glUniformMatrix4fv(model_location, 1, GL_FALSE, self.cube_model_matrix)
#view_location = glGetUniformLocation(self.gl_program, "view")
#glUniformMatrix4fv(view_location, 1, GL_FALSE, self.cube_view_matrix)
#proj_location = glGetUniformLocation(self.gl_program, "proj")
#glUniformMatrix4fv(proj_location, 1, GL_FALSE, self.cube_proj_matrix)
glBindVertexArray(self.vao)
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
glUseProgram(0)
def main():
pygame.init()
os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.display.set_caption("3D Graphics")
pygame.display.set_mode((1280, 720), pygame.DOUBLEBUF | pygame.OPENGL)
clock = pygame.time.Clock()
gl = GLProgram()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
clock.tick(60)
gl.display()
pygame.display.flip()
if __name__ == "__main__":
main()
with this Vertex Shader:
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 text_coord;
out vec2 final_text_coord;
uniform mat4 mvp;
void main() {
gl_Position = mvp * vec4(position, 1.0);
final_text_coord = text_coord;
}
and this Fragment Shader:
#version 330 core
in vec2 final_text_coord;
out vec4 frag_color;
uniform sampler2D the_texture;
void main() {
frag_color = vec4(1,0,0,1);
}
I specifically added the methods gl_translate2 and gl_translate3. Both result in the same matrix, the 2 variant is just using the native multiplication order convention of the pyrr library, and gl_translate3 use the GL conventions.
I also created the matrix with different parameters as
self.cube_mvp = self.gl_translate3(Vector3([1.0, 1.0, 1.0]), -45.0, Vector3([0.5, 0.5, 0.5]))
which differs in the negative sign for the rotation (and compensates for the additional transpose you do in gl_transaltion).
The result is different to what you got in your posted answer, but that is due to your model matrix being broken in that answer, as a result of the wrong multiplication function (which especially removed the translation part and distorted the rotation into some shear operation).
The result I'm getting with both gl_translate2 and gl_translate3 is:
and this looks very plausible for the parameters specified.