PyOpenGL Perspective Projection - python

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.

Related

Color of quad in opengl is different than specified

The quad that I have created is not the same color as I specified. The quad is supposed to be completely red, as evidenced in the code below. The last three values in each row are the ones that specify the color.
quad = [-0.5, -0.5, 0.0, width / height, 1.0, 0.0, 0.0,
-0.5, 0.5, 0.0, width / height, 1.0, 0.0, 0.0,
0.5, 0.5, 0.0, width / height, 1.0, 0.0, 0.0,
0.5, -0.5, 0.0, width / height, 1.0, 0.0, 0.0]
Instead of the expected result, I get this:
How can I make the quad look fully red, and not multiple colors?
Full code:
import math
import glfw
import numpy
import pyrr
from OpenGL.GL import *
from OpenGL.GL.shaders import *
width, height = 500, 500
def draw():
global shader
quad = [-0.5, -0.5, 0.0, width / height, 1.0, 0.0, 0.0,
-0.5, 0.5, 0.0, width / height, 1.0, 0.0, 0.0,
0.5, 0.5, 0.0, width / height, 1.0, 0.0, 0.0,
0.5, -0.5, 0.0, width / height, 1.0, 0.0, 0.0]
quad = numpy.array(quad,
dtype='float32')
indices = [0, 1, 2, 3]
indices = numpy.array(indices, dtype='uint32')
vertex_shader_ = """
#version 140
in vec4 position;
in vec3 color;
uniform mat4 transform;
out vec4 out_color;
void main(){
gl_Position = position;
out_color = vec4(color.rgb, 1);
};
"""
fragment_shader_ = """
#version 140
in vec4 out_color;
void main(){
gl_FragColor = out_color;
};
"""
shader = compileProgram(compileShader(vertex_shader_, GL_VERTEX_SHADER),
compileShader(fragment_shader_, GL_FRAGMENT_SHADER))
glUseProgram(shader)
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, quad.nbytes, quad, GL_STATIC_DRAW)
EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.nbytes, indices, GL_STATIC_DRAW)
position = glGetAttribLocation(shader, "position")
glVertexAttribPointer(position, 4, GL_FLOAT, GL_FALSE, 28, ctypes.c_void_p(0))
glEnableVertexAttribArray(position)
color = glGetAttribLocation(shader, "color")
glVertexAttribPointer(color, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(16))
glEnableVertexAttribArray(color)
def Screen():
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glDrawArrays(GL_POLYGON, 0, 4)
draw()
glViewport(0, 0, width, height)
def main():
global width, height
if not glfw.init():
return
window = glfw.create_window(500, 500, "Opengl GLFW Window", None, None)
if not window:
glfw.terminate()
return
glfw.make_context_current(window)
while not glfw.window_should_close(window):
glfw.poll_events()
Screen()
width, height = glfw.get_window_size(window)
print(width, height)
glViewport(0, 0, width, height)
glfw.swap_buffers(window)
glfw.terminate()
if __name__ == '__main__':
main()
The stride parameter in each glVertexAttribPointer() was given two different numbers. Thanks for #genpfault for pointing that out!

How to make block collision in python opengl

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()

How to get I the correct texture using indexing in opengl

I want to impement a pyramid which has different texture in each side.
The pyramid with the textures is correct without using an index data array. When i use the indexdata the textures get mixed with each other. Do i have to correct the AttribPointer? (My texture is a collage image of 5 different images)
*I want to keep the indexData array
Here is a part of the code
from OpenGL.GLUT import *
from OpenGL.GLU import *
from OpenGL.GL import *
import numpy as np
from framework import *
from PIL import Image
from math import tan, cos, sin
# A 1-D array of 3 4-D vertices (X,Y,Z,W)
# Note that this must be a numpy array, since as of
# 170111 support for lists has not been implemented.
vertexData = np.array(
[ 0.0, 0.5, -1.3, 1.0, #0
0.3, 0.0, -1.0, 1.0, #1
-0.3, 0.0, -1.0, 1.0, #2
-0.3, 0.0, -1.6, 1.0, #3
0.3, 0.0, -1.6, 1.0, #4
0.0, 0.0, -1.3, 1.0, #5
#texture
0.8, 0.0,
1.0, 0.0,
0.8, 0.4,
0.0, 0.3,
0.3, 0.3,
0.3, 0.0,
0.5, 1.0,
0.5, 0.5,
0.6, 1.0,
0.1, 1.0,
0.4, 1.0,
0.0, 0.4,
0.0, 0.0,
0.0, 0.3,
0.3, 0.0,
0.0, 0.0,
0.0, 0.3,
0.3, 0.0,
0.0, 0.0,
0.0, 0.3, #leukos toixos
0.3, 0.0,
0.0, 0.0,
0.0, 0.3,
0.3, 0.0,
],
dtype='float32'
)
indexData = np.array(
[0,1,2,
0,2,3,
0,3,4,
0,4,1,
5,2,1,
5,1,4,
5,4,3,
5,3,2],)
vertexDim = 4
texcoordDim = 2
nVertices = 8*3
# Global variable to represent the compiled shader program, written in GLSL
theProgram = None
# Global variable to represent the buffer that will hold the position vectors
vertexBufferObject = None
sampleTexture = None
texUnitUniform = None
# Global variables to store the location of the shader's uniform variables
perspectiveMatrixUnif = None
WorldtoCameraMatrixUnif = None
# Global display variables
perspectiveMatrix = None
CameraMatrix = None
fFrustumScale = 1.0
def loadTexture(path):
img = Image.open(path).transpose(Image.FLIP_TOP_BOTTOM) #anoigoume tin eikona
#img.show()
img_data = np.frombuffer(img.tobytes(), np.uint8)
width, height = img.size
# glTexImage2D expects the first element of the image data to be the
# bottom-left corner of the image. Subsequent elements go left to right,
# with subsequent lines going from bottom to top.
# However, the image data was created with PIL Image tostring and numpy's
# fromstring, which means we have to do a bit of reorganization. The first
# element in the data output by tostring() will be the top-left corner of
# the image, with following values going left-to-right and lines going
# top-to-bottom. So, we need to flip the vertical coordinate (y).
texture = glGenTextures(1)
#glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
glBindTexture(GL_TEXTURE_2D, texture)#ginetai sundesi me to antikeimeno pou ftiaxtike
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)#parametroi gia tin ufi
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)#gia na exw digrammiko filtrarisma
#glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
#glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, img_data)#sundesi twn dedomenwn
#glGenerateMipmap(GL_TEXTURE_2D)
return texture
# Set up the list of shaders, and call functions to compile them
def initializeProgram():
shaderList = []
shaderList.append(loadShader(GL_VERTEX_SHADER, "Texture.vert"))
shaderList.append(loadShader(GL_FRAGMENT_SHADER, "Texture.frag"))
global theProgram
theProgram = createProgram(shaderList)
for shader in shaderList:
glDeleteShader(shader)
global perspectiveMatrixUnif
perspectiveMatrixUnif = glGetUniformLocation(theProgram, "perspectiveMatrix")
global WorldtoCameraMatrixUnif
WorldtoCameraMatrixUnif = glGetUniformLocation(theProgram, "cameraMatrix")
global sampleTexture
global texUnitUniform
sampleTexture = loadTexture('pic.jpg') #i eikona
texUnitUniform = glGetUniformLocation(theProgram, "texUnit")#gia na vrethei apo ton fragment shader
fzNear = 0.5
fzFar = 3.0
global CameraMatrix
CameraPos=np.array([0.8, 0.8, -1.0])
CameraTarget=np.array([1.0, 1.0, 0.0])
CameraUp=np.array([-1.0, 1.0, 0.0])
CameraTarget=CameraTarget/np.linalg.norm(CameraTarget)
CameraRight=np.cross(CameraUp, CameraTarget)
CameraRight=CameraRight/np.linalg.norm(CameraRight)
CameraUp=np.cross(CameraTarget, CameraRight)
CameraRotMatrix=np.vstack((CameraRight, CameraUp, CameraTarget,[0.0, 0.0, 0.0]))
CameraRotMatrix=np.hstack((CameraRotMatrix, [[0.0], [0.0], [0.0], [1.0]]))
CameraTranMatrix=np.eye(4)
CameraTranMatrix[0][3]=-CameraPos[0]
CameraTranMatrix[1][3]=-CameraPos[1]
CameraTranMatrix[2][3]=-CameraPos[2]
CameraMatrix=np.matmul(CameraRotMatrix,CameraTranMatrix)
global perspectiveMatrix
perspectiveMatrix = np.zeros(16, dtype='float32')
perspectiveMatrix[0] = fFrustumScale
perspectiveMatrix[5] = fFrustumScale
perspectiveMatrix[10] = (fzFar + fzNear) / (fzNear - fzFar)
perspectiveMatrix[14] = (2 * fzFar * fzNear) / (fzNear - fzFar)
perspectiveMatrix[11] = -1.0
glUseProgram(theProgram)
glActiveTexture(GL_TEXTURE0)#energi tin monada ufis pou me endiaferei
glBindTexture(GL_TEXTURE_2D, sampleTexture)#sundesi tis ufis me ton stoxo
glUniform1i(texUnitUniform, 0)#sundesi tis metavlitis me tin monada 0
glUniformMatrix4fv(perspectiveMatrixUnif, 1, GL_FALSE, perspectiveMatrix)
glUniformMatrix4fv(WorldtoCameraMatrixUnif, 1, GL_TRUE, CameraMatrix)
glUseProgram(0)
# Set up the vertex buffer that will store our vertex coordinates for OpenGL's access
def initializeVertexBuffer():
global vertexBufferObject
vertexBufferObject = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject)
glBufferData( # PyOpenGL allows for the omission of the size parameter
GL_ARRAY_BUFFER,
vertexData,
GL_STREAM_DRAW
)
glBindBuffer(GL_ARRAY_BUFFER, 0)
indexBufferObject = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferObject)
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
indexData,
GL_STATIC_DRAW
)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
def computeAngleRad(fElapsedTime, fLoopDuration):
fScale = 3.14159 * 2.0 / fLoopDuration
fCurrTimeThroughLoop = fElapsedTime % fLoopDuration
return fCurrTimeThroughLoop * fScale
def rotateZ(fElapsedTime):
fAngRad = computeAngleRad(fElapsedTime, 4.0)
fCos = cos(fAngRad)
fSin = sin(fAngRad)
# Initialize the OpenGL environment
def init():
initializeProgram()
initializeVertexBuffer()
glBindVertexArray(glGenVertexArrays(1))
glEnable(GL_CULL_FACE)
glCullFace(GL_BACK)
glFrontFace(GL_CW)
glEnable(GL_DEPTH_TEST)
# Called to update the display.
# Because we are using double-buffering, glutSwapBuffers is called at the end
# to write the rendered buffer to the display.
def display():
glClearColor(0.0, 0.0, 0.0, 0.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glUseProgram(theProgram)
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject)
glEnableVertexAttribArray(0)
glEnableVertexAttribArray(1)
glVertexAttribPointer(0, vertexDim, GL_FLOAT, GL_FALSE, 0, None)
# a ctype void pointer must be used to pass in the offset into the bound GL_ARRAY_BUFFER
# also note that python's underlying float type is usally 64-bit, but
# we have specified that our vertex array contains float32 data.
texcoordOffset = c_void_p(vertexDim*nVertices*4)
glVertexAttribPointer(1, texcoordDim, GL_FLOAT, GL_FALSE, 0, texcoordOffset)
fElapsedTime = glutGet(GLUT_ELAPSED_TIME) / 2000.0
AngRad = computeAngleRad(fElapsedTime, 2.0)
worldMatrix = np.identity(4, dtype='float32')
worldMatrix[0][0] = cos(AngRad)
worldMatrix[0][2] = sin(AngRad)
worldMatrix[2][0] = -sin(AngRad)
worldMatrix[2][2] = cos(AngRad)
matTrans1 = np.identity(4, dtype='float32')
matTrans1[2][3] = 1.3
matTrans2 = np.identity(4, dtype='float32')
matTrans2[2][3] = -1.3
newTransformA= np.matmul(worldMatrix,matTrans1)
newTransformB = np.matmul (matTrans2,newTransformA)
transformMatrix = newTransformB
glUniformMatrix4fv(WorldtoCameraMatrixUnif, 1, GL_FALSE, transformMatrix.transpose())
glDrawArrays(GL_TRIANGLES, 0, nVertices)
glDisableVertexAttribArray(0)
glDisableVertexAttribArray(1)
glUseProgram(0)
glutSwapBuffers()
glutPostRedisplay()
# keyboard input handler: exits the program if 'esc' is pressed
def keyboard(key, x, y):
if ord(key) == 27: # ord() is needed to get the keycode
glutLeaveMainLoop()
return
# Called whenever the window's size changes (including once when the program starts)
def reshape(w, h):
global perspectiveMatrix
perspectiveMatrix[0] = fFrustumScale / (w / float(h))
perspectiveMatrix[5] = fFrustumScale
glUseProgram(theProgram)
glUniformMatrix4fv(perspectiveMatrixUnif, 1, GL_FALSE, perspectiveMatrix)
glUseProgram(0)
glViewport(0, 0, w, h)
# The main function
def main():
glutInit()
displayMode = GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH | GLUT_STENCIL;
glutInitDisplayMode (displayMode)
width = 500;
height = 500;
glutInitWindowSize (width, height)
glutInitWindowPosition (300, 200)
window = glutCreateWindow(b"Tutorial Window")
init()
glutDisplayFunc(display)
glutReshapeFunc(reshape)
glutKeyboardFunc(keyboard)
glutMainLoop();
if __name__ == '__main__':
main()

Framebuffer issue (render to texture) with GTK3 GLArea vs GLFW: Identical OpenGL program works in GLFW but not GTK3's GLArea

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)

glBufferSubData , Haven't implemented type-inference for lists yet - instanced rendering

Porting over a chapter 7 example of instanced rendering from Superbible OpenGL 7th ed. and run into a problem with the function glBufferSubData
Whatever I do to it, it won't accept the data. Make it into a pointer, a byte string, list itself. Please help any assistance would be very much appreciated. Thank You.
Update: Using the excellent answer from Rabbid76 the function glBufferSubData is now accepting the data and the numpy version is very nice, the ctypes version is an insightful answer and very good to know. Also, about the 2nd parameter, it does indeed need to be a int or long, not a GLuint(0) in python.
Update and Success: Another very fine answer by Rabbid76 to get the rendering working.
The function glVertexAttribPointer needs a pointer to the lengths of the initial lists of data so it can offset. Thank You very much.
source code to: instancedattribs.py
#!/usr/bin/python3
import sys
import time
import ctypes
fullscreen = True
try:
from OpenGL.GLUT import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, glBindVertexArray
except:
print ('''
ERROR: PyOpenGL not installed properly.
''')
sys.exit()
import numpy as np
square_buffer = GLuint(0)
square_vao = GLuint(0)
square_program = GLuint(0)
square_vs_source = '''
#version 410 core
layout (location = 0) in vec4 position;
layout (location = 1) in vec4 instance_color;
layout (location = 2) in vec4 instance_position;
out Fragment
{
vec4 color;
} fragment;
void main(void)
{
gl_Position = (position + instance_position) * vec4(0.25, 0.25, 1.0, 1.0);
fragment.color = instance_color;
}
'''
square_fs_source = '''
#version 410 core
precision highp float;
in Fragment
{
vec4 color;
} fragment;
out vec4 color;
void main(void)
{
color = fragment.color;
}
'''
class Scene:
def __init__(self, width, height):
global square_buffer
global square_vao
global square_program
self.width = width
self.height = height
square_vertices = np.array([
-1.0, -1.0, 0.0, 1.0,
1.0, -1.0, 0.0, 1.0,
1.0, 1.0, 0.0, 1.0,
-1.0, 1.0, 0.0, 1.0], dtype='float32')
instance_colors = np.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,
1.0, 1.0, 0.0, 1.0], dtype='float32')
instance_positions = np.array([
-2.0, -2.0, 0.0, 0.0,
2.0, -2.0, 0.0, 0.0,
2.0, 2.0, 0.0, 0.0,
-2.0, 2.0, 0.0, 0.0], dtype='float32')
glGenVertexArrays(1, square_vao)
glGenBuffers(1, square_buffer)
glBindVertexArray(square_vao)
glBindBuffer(GL_ARRAY_BUFFER, square_buffer)
offset = 0 # not GLuint(0)
bufferSize = (len(square_vertices) + len(instance_colors) + len(instance_positions))*4
glBufferData(GL_ARRAY_BUFFER, bufferSize, None, GL_STATIC_DRAW)
glBufferSubData(GL_ARRAY_BUFFER, offset, len(square_vertices)*4, square_vertices)
offset += len(square_vertices)*4
glBufferSubData(GL_ARRAY_BUFFER, offset, len(instance_colors)*4, instance_colors)
offset += len(instance_colors)*4
glBufferSubData(GL_ARRAY_BUFFER, offset, len(instance_positions)*4, instance_positions)
offset += len(instance_positions)*4
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
offsetInstanceColor = len(square_vertices)*4
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(offsetInstanceColor))
offsetInstancPosition = (len(instance_colors) + len(instance_positions))*4
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(offsetInstancPosition))
glEnableVertexAttribArray(0)
glEnableVertexAttribArray(1)
glEnableVertexAttribArray(2)
glVertexAttribDivisor(1, 1)
glVertexAttribDivisor(2, 1)
square_program = glCreateProgram()
square_vs = GLuint(0)
square_vs = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(square_vs, square_vs_source)
glCompileShader(square_vs)
glAttachShader(square_program, square_vs)
square_fs = GLuint(0)
square_fs = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(square_fs, square_fs_source)
glCompileShader(square_fs)
glAttachShader(square_program, square_fs)
glLinkProgram(square_program)
glDeleteShader(square_vs)
glDeleteShader(square_fs)
def display(self):
black = [ 0.0, 0.0, 0.0, 0.0 ]
glClearBufferfv(GL_COLOR, 0, black)
glUseProgram(square_program)
glBindVertexArray(square_vao)
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, 4)
glutSwapBuffers()
def reshape(self, width, height):
self.width = width
self.height = height
def keyboard(self, key, x, y ):
global fullscreen
print ('key:' , key)
if key == b'\x1b': # ESC
sys.exit()
elif key == b'f' or key == b'F': #fullscreen toggle
if (fullscreen == True):
glutReshapeWindow(512, 512)
glutPositionWindow(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
else:
glutFullScreen()
fullscreen = True
print('done')
def init(self):
pass
def timer(self, blah):
glutPostRedisplay()
glutTimerFunc( int(1/60), self.timer, 0)
time.sleep(1/60.0)
if __name__ == '__main__':
start = time.time()
glutInit()
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
glutInitWindowSize(512, 512)
w1 = glutCreateWindow('OpenGL SuperBible - Instanced Attributes')
glutInitWindowPosition(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
many_cubes = False
#glutFullScreen()
scene = Scene(512,512)
glutReshapeFunc(scene.reshape)
glutDisplayFunc(scene.display)
glutKeyboardFunc(scene.keyboard)
glutIdleFunc(scene.display)
#glutTimerFunc( int(1/60), scene.timer, 0)
scene.init()
glutMainLoop()
expected rendering output:
Ported from: instancedattribs.cpp
In compare to glBufferData, PyOpenGl's glBufferSubData the size parameter can't be omitted.
You've to pass the size of the buffer (in bytes) and a pointer to the buffer. But note, the 2nd and the 3rd parameter have to be a python int or long, even PyOpneGL's GLuint will cause an error.
You've some possibilities, either create an array PyOpenGL's GLfloat
offset = 0
dataArray = (GLfloat*len(square_vertices))(*square_vertices)
glBufferSubData(GL_ARRAY_BUFFER, offset, len(dataArray)*4, dataArray)
or use pythons built-in ctypes library:
import ctypes
offset = 0
dataArray = (ctypes.c_float*len(square_vertices))(*square_vertices)
glBufferSubData(GL_ARRAY_BUFFER, offset, len(dataArray)*4, dataArray)
or create a NumPy array:
import numpy as np
offset = 0
dataArray = np.array(square_vertices, dtype='float32')
glBufferSubData(GL_ARRAY_BUFFER, offset, len(dataArray)*4, dataArray)
Note, for the 2nd and 3d parameter you can use a cast to either int (e.g int(offset)) or long (e.g. long(offset)).
Further note, the offset and size parameters to glBufferData, glBufferSubData and glVertexAttribPointer are values in size of bytes rather than the number of elements of the arrays.
The size in bytes is calculated by the number of elements multiplied by the size of 1 element.
The size of 1 element is 4, since the size of float (GLfloat, ctypes.c_float, 'float32') in bytes is 4.
If a named buffer object is bound, then the last parameter of glVertexAttribPointer is treated as a byte offset into the buffer object's data store, but the type of the parameter is still a pointer.
So you've to use ctypes.c_void_p(offset). If offset is 0 it is possible to pass None.
bufferSize = (len(square_vertices) + len(instance_colors) + len(instance_positions))*4
glBufferData(GL_ARRAY_BUFFER, bufferSize, None, GL_STATIC_DRAW)
glBufferSubData(GL_ARRAY_BUFFER, offset, len(square_vertices)*4, square_vertices)
offset += len(square_vertices)*4
glBufferSubData(GL_ARRAY_BUFFER, offset, len(instance_colors)*4, instance_colors)
offset += len(instance_colors)*4
glBufferSubData(GL_ARRAY_BUFFER, offset, len(instance_positions)*4, instance_positions)
offset += len(instance_positions)*4
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
offsetInstanceColor = len(square_vertices)*4
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(offsetInstanceColor))
offsetInstancPosition = (len(instance_colors) + len(instance_positions))*4
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(offsetInstancPosition))

Categories

Resources