Related
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()
I've been trying to rotate an object but upon rotation, I realized it was just flat. What is strange is that I can clearly see the inputs for the z dim are there, it just isn't being accounted for. Here is my code:
import moderngl
from PyQt5 import QtOpenGL, QtCore, QtGui
from PyQt5.QtCore import Qt, pyqtSignal
import numpy as np
from pyrr import matrix44
cube_verts4 = np.array([
-1.0, 1.0, -1.0, 1.0,
-1.0, -1.0, -1.0, 1.0,
1.0, -1.0, -1.0, 1.0,
1.0, 1.0, -1.0, 1.0,
-1.0, 1.0, 1.0, 1.0,
-1.0, -1.0, 1.0, 1.0,
1.0, -1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0,
], dtype=np.float32)
cube_ibo_idxs = np.array([
0, 1, 2,
2, 3, 1,
3, 2, 6,
6, 5, 3,
5, 6, 7,
7, 4, 5,
4, 7, 1,
1, 0, 4,
0, 3, 5,
5, 4, 0,
1, 7, 6,
6, 2, 1
], dtype=np.int32)
class OpenGLWindowWidget(QtOpenGL.QGLWidget):
vsync = True
remove_event = pyqtSignal(str)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.gl_version = (4, 3)
fmt = QtOpenGL.QGLFormat()
# need compute shader stuff
fmt.setVersion(self.gl_version[0], self.gl_version[1])
fmt.setProfile(QtOpenGL.QGLFormat.CoreProfile)
fmt.setDepthBufferSize(24)
fmt.setDoubleBuffer(True)
fmt.setSwapInterval(1 if self.vsync else 0)
self.ctx = None
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.update)
self.timer.start(16)
self.last_mouse_pos = None
self.rotation_x = 0
self.rotation_y = 0
def mousePressEvent(self, event: QtGui.QMouseEvent) -> None:
self.last_mouse_pos = event.pos()
def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None:
dx = event.x() - self.last_mouse_pos.x()
dy = event.y() - self.last_mouse_pos.y()
if event.buttons() & Qt.LeftButton:
self.rotation_x += dx * 0.01
self.rotation_y += dy * 0.01
self.last_mouse_pos = event.pos()
#property
def gl_version_code(self) -> int:
return self.gl_version[0] * 100 + self.gl_version[1] * 10
#property
def aspect_ratio(self):
return self.width() / self.height()
def initializeGL(self) -> None:
self.ctx = moderngl.create_context(
require=self.gl_version_code)
self.prog = self.ctx.program(
vertex_shader='''
#version 330
in vec4 vertex;
in float power;
uniform mat4 mvp_matrix;
void main() {
gl_Position = vec4(mvp_matrix * vertex);
}
''',
fragment_shader='''
#version 330
out vec4 color;
void main() {
color = vec4(0.0, 0.0, 0.0, 1.0);
}
''',
)
self.mvp_matrix = self.prog["mvp_matrix"]
self.vbo = self.ctx.buffer(
cube_verts4.astype('f4').tobytes())
self.ibo = self.ctx.buffer(
cube_ibo_idxs.astype('i4').tobytes())
vao_content = [
# 4 floats are assigned to the 'in' variable named 'vertex' in the shader code
(self.vbo, '4f', 'vertex'),
]
self.vao = self.ctx.vertex_array(self.prog, vao_content,
self.ibo)
def paintGL(self):
target_width = 2
target_height = 2
r_aspect_ratio = target_width / target_height
if self.aspect_ratio > r_aspect_ratio:
v_a = self.aspect_ratio / r_aspect_ratio
projection = matrix44.create_orthogonal_projection_matrix(
-v_a * target_width / 2.0,
v_a * target_width / 2.0, -target_height / 2.0,
target_height / 2.0, 0, 100, dtype=np.float32)
else:
a_v = r_aspect_ratio / self.aspect_ratio
projection = matrix44.create_orthogonal_projection_matrix(
-target_width / 2.0, target_width / 2.0,
-a_v * target_height / 2.0,
a_v * target_height / 2.0,
0, 100, dtype=np.float32)
rotate = matrix44.create_from_y_rotation(
self.rotation_x) * matrix44.create_from_x_rotation(
self.rotation_y)
self.mvp_matrix.write((rotate * projection).astype('f4').tobytes())
self.ctx.viewport = (0, 0, self.width(), self.height())
self.ctx.clear(0.0, 1.0, 1.0)
self.vao.render()
self.ctx.finish()
This is what I get out of it when I rotate.
I would have expected the silhouette of a cube, instead i get the silhouette of a plane.
I'm not even sure what could be causing this effect. I initially thought it was something to do with vec3's alignment, but I replaced my vertex + vbo code to use vec4s instead and it still doesn't work. I'm at a loss for how my depth is being "removed". I'm not familiar with pyrr so maybe some how the matrix transformation there is incorrect?
The element indices don't form a cube. Use the following indices:
cube_ibo_idxs = np.array([
0, 1, 2, 0, 2, 3,
3, 2, 6, 3, 6, 7,
7, 6, 5, 7, 5, 4,
7, 4, 0, 7, 0, 3,
4, 5, 1, 4, 1, 0,
1, 5, 6, 1, 6, 2
], dtype=np.int32)
pyrr Matrix44 operations return a numpy.array.
for array, * means element-wise multiplication, while # means matrix multiplication. See array. So you'Ve to use # isntead of *. Alternatively you can use numpy.matmul.
In the orthographic projection the near plane is set 0 and the far plane is set 100.
projection = matrix44.create_orthogonal_projection_matrix(
v_a * -target_width / 2.0, v_a * target_width / 2.0,
-target_height / 2.0, target_height / 2.0,
0, 100, dtype=np.float32)
Since the center of the geometry at (0, 0, 0), the cube mesh is partially clipped by the near plane of the cuboid view volume. Either change the near plane (e.g. -100) or draw the cube inbetween the near and far plane. This means you've to translate the mesh along the z axis. Since the (view space) z-axis points out of the view port (in Right-handed system), you've to translate the mesh in negative z direction (e.g. -3):
rotate = matrix44.create_from_y_rotation(-self.rotation_x) # \
matrix44.create_from_x_rotation(-self.rotation_y) # \
matrix44.create_from_translation(np.array([0, 0, -3], dtype=np.float32))
Further I recommend to enable the Depth Test. See ModernGL - Context:
self.ctx.enable(moderngl.DEPTH_TEST)
Use following function to draw the geometry:
def paintGL(self):
target_width = 4
target_height = 4
r_aspect_ratio = target_width / target_height
if self.aspect_ratio > r_aspect_ratio:
v_a = self.aspect_ratio / r_aspect_ratio
projection = matrix44.create_orthogonal_projection_matrix(
v_a * -target_width / 2.0, v_a * target_width / 2.0,
-target_height / 2.0, target_height / 2.0,
0, 100, dtype=np.float32)
else:
a_v = r_aspect_ratio / self.aspect_ratio
projection = matrix44.create_orthogonal_projection_matrix(
-target_width / 2.0, target_width / 2.0,
-a_v * target_height / 2.0, a_v * target_height / 2.0,
0, 100, dtype=np.float32)
rotate = matrix44.create_from_y_rotation(-self.rotation_x) # \
matrix44.create_from_x_rotation(-self.rotation_y) # \
matrix44.create_from_translation(np.array([0, 0, -3], dtype=np.float32))
self.mvp_matrix.write((rotate # projection).astype('f4').tobytes())
self.ctx.viewport = (0, 0, self.width(), self.height())
self.ctx.clear(0.0, 1.0, 1.0)
self.ctx.enable(moderngl.DEPTH_TEST)
self.vao.render()
self.ctx.finish()
If you use the following vertex shader
#version 330
in vec4 vertex;
in float power;
out vec4 v_clip_pos;
uniform mat4 mvp_matrix;
void main() {
v_clip_pos = mvp_matrix * vertex;
gl_Position = v_clip_pos;
}
and fragment shader
#version 330
in vec4 v_clip_pos;
out vec4 color;
void main() {
vec3 ndc_pos = v_clip_pos.xyz / v_clip_pos.w;
vec3 dx = dFdx( ndc_pos );
vec3 dy = dFdy( ndc_pos );
vec3 N = normalize(cross(dx, dy));
N *= sign(N.z);
vec3 L = vec3(0.0, 0.0, 1.0);
float NdotL = dot(N, L);
vec3 diffuse_color = vec3(0.5) * NdotL;
color = vec4( diffuse_color.rgb, 1.0 );
}
then you can achiev a slight 3D effect.
I ported over the .cpp version of spinningcube found there to python for a better understanding of opengl and to create something new. While I get the same result as the compiled version from the book source code from both 6th and 7ed as the program is the same from the two editions, the program in its current state displays a green screen only. The book of OpenGl Superbible 7th ed. on page 177 shows a spinning colored cube is supposed to fly around. Any assistance would be greatly appreciated.
Update - June 24, 2019 - I have updated the code so that the cube appears, spins, and moves per the excellent code from Rabbid76. Thank You.
#!/usr/bin/python3
import sys
import time
import math
fullscreen = True
# sys.path.append("../shared")
# from math3d import m3dDegToRad, m3dRotationMatrix44, M3DMatrix44f, m3dLoadIdentity44, \
# m3dTranslateMatrix44, m3dScaleMatrix44, \
# m3dMatrixMultiply44, m3dTransposeMatrix44, \
# m3dRadToDeg
import numpy.matlib
import numpy as np
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()
from math import cos, sin
from array import array
M3D_PI = 3.14159265358979323846
M3D_PI_DIV_180 = M3D_PI / 180.0
M3D_INV_PI_DIV_180 = 57.2957795130823229
# Translate matrix. Only 4x4 matrices supported
def m3dTranslateMatrix44(m, x, y, z):
m[12] += x
m[13] += y
m[14] += z
def m3dDegToRad(num):
return (num * M3D_PI_DIV_180)
def m3dRadToDeg(num):
return (num * M3D_INV_PI_DIV_180)
def m3dOrtho(l, r, t, b, n, f):
return (GLfloat * 16)(
2/(r-l), 0, 0, 0,
0, 2/(t-b), 0, 0,
0, 0, -2/(f-n), 0,
-(r+l)/(r-l), -(t+b)/(t-b), -(f+n)/(f-n), 1)
def m3dPerspective(fov_y, aspect, n, f):
a = aspect
ta = math.tan( fov_y / 2 )
return (GLfloat * 16)(
1/(ta*a), 0, 0, 0,
0, 1/ta, 0, 0,
0, 0, -(f+n)/(f-n), -1,
0, 0, -2*f*n/(f-n), 0)
# Creates a 4x4 rotation matrix, takes radians NOT degrees
def m3dRotationMatrix44(m, angle, x, y, z):
s = sin(angle)
c = cos(angle)
mag = float((x * x + y * y + z * z) ** 0.5)
if mag == 0.0:
m3dLoadIdentity(m)
return
x /= mag
y /= mag
z /= mag
xx = x * x
yy = y * y
zz = z * z
xy = x * y
yz = y * z
zx = z * x
xs = x * s
ys = y * s
zs = z * s
one_c = 1.0 - c
m[0] = (one_c * xx) + c
m[1] = (one_c * xy) - zs
m[2] = (one_c * zx) + ys
m[3] = 0.0
m[4] = (one_c * xy) + zs
m[5] = (one_c * yy) + c
m[6] = (one_c * yz) - xs
m[7] = 0.0
m[8] = (one_c * zx) - ys
m[9] = (one_c * yz) + xs
m[10] = (one_c * zz) + c
m[11] = 0.0
m[12] = 0.0
m[13] = 0.0
m[14] = 0.0
m[15] = 1.0
def m3dMultiply(A, B):
C = (GLfloat * 16)(*identityMatrix)
for k in range(0, 4):
for j in range(0, 4):
C[k*4+j] = A[0*4+j] * B[k*4+0] + A[1*4+j] * B[k*4+1] + \
A[2*4+j] * B[k*4+2] + A[3*4+j] * B[k*4+3]
return C
def translate(tx, ty, tz):
"""creates the matrix equivalent of glTranslate"""
return np.array([1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
tx, ty, tz, 1.0], np.float32)
def rotation_matrix(axis, theta):
"""
Return the rotation matrix associated with counterclockwise rotation about
the given axis by theta radians.
"""
axis = np.asarray(axis)
axis = axis / math.sqrt(np.dot(axis, axis))
a = math.cos(theta / 2.0)
b, c, d = -axis * math.sin(theta / 2.0)
aa, bb, cc, dd = a * a, b * b, c * c, d * d
bc, ad, ac, ab, bd, cd = b * c, a * d, a * c, a * b, b * d, c * d
return np.array([[aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac), 0],
[2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab), 0],
[2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc, 0],
[0,0,0,1]])
identityMatrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]
mv_location = (GLfloat * 16)(*identityMatrix)
proj_location = (GLfloat * 16)(*identityMatrix)
proj_matrix = (GLfloat * 16)(*identityMatrix)
many_cubes = False
# Vertex program
vs_source = '''
#version 410 core
in vec4 position;
out VS_OUT
{
vec4 color;
} vs_out;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
void main(void)
{
gl_Position = proj_matrix * mv_matrix * position;
vs_out.color = position * 2.0 + vec4(0.5, 0.5, 0.5, 0.0);
}
'''
# Fragment program
fs_source = '''
#version 410 core
out vec4 color;
in VS_OUT
{
vec4 color;
} fs_in;
void main(void)
{
color = fs_in.color;
}
'''
def compile_program(vertex_source, fragment_source):
global mv_location
global proj_location
vertex_shader = None
fragment_shader = None
if vertex_source:
vertex_shader = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertex_shader, vertex_source)
glCompileShader(vertex_shader)
if not glGetShaderiv(vertex_shader, GL_COMPILE_STATUS):
raise Exception('failed to compile shader "%s":\n%s' %
('vertex_shader', glGetShaderInfoLog(vertex_shader)))
if fragment_source:
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragment_shader, fragment_source)
glCompileShader(fragment_shader)
if not glGetShaderiv(fragment_shader, GL_COMPILE_STATUS):
raise Exception('failed to compile shader "%s":\n%s' %
('fragment_shader', glGetShaderInfoLog(fragment_shader)))
program = glCreateProgram()
glAttachShader(program, vertex_shader)
glAttachShader(program, fragment_shader)
glLinkProgram(program)
mv_location = glGetUniformLocation(program, "mv_matrix");
proj_location = glGetUniformLocation(program, "proj_matrix");
vao = GLuint(0)
glGenVertexArrays(1, vao);
glBindVertexArray(vao);
vertex_positions = [
-0.25, 0.25, -0.25,
-0.25, -0.25, -0.25,
0.25, -0.25, -0.25,
0.25, -0.25, -0.25,
0.25, 0.25, -0.25,
-0.25, 0.25, -0.25,
0.25, -0.25, -0.25,
0.25, -0.25, 0.25,
0.25, 0.25, -0.25,
0.25, -0.25, 0.25,
0.25, 0.25, 0.25,
0.25, 0.25, -0.25,
0.25, -0.25, 0.25,
-0.25, -0.25, 0.25,
0.25, 0.25, 0.25,
-0.25, -0.25, 0.25,
-0.25, 0.25, 0.25,
0.25, 0.25, 0.25,
-0.25, -0.25, 0.25,
-0.25, -0.25, -0.25,
-0.25, 0.25, 0.25,
-0.25, -0.25, -0.25,
-0.25, 0.25, -0.25,
-0.25, 0.25, 0.25,
-0.25, -0.25, 0.25,
0.25, -0.25, 0.25,
0.25, -0.25, -0.25,
0.25, -0.25, -0.25,
-0.25, -0.25, -0.25,
-0.25, -0.25, 0.25,
-0.25, 0.25, -0.25,
0.25, 0.25, -0.25,
0.25, 0.25, 0.25,
0.25, 0.25, 0.25,
-0.25, 0.25, 0.25,
-0.25, 0.25, -0.25 ]
buffer = GLuint(0)
glGenBuffers(1, buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
#ar=numpy.array(vertex_positions, dtype='float32')
ar=array("f",vertex_positions)
glBufferData(GL_ARRAY_BUFFER, ar.tostring(), GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None);
glEnableVertexAttribArray(0);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CW);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
return program
class Scene:
def __init__(self, width, height):
self.width = width
self.height = height
def display(self):
global mv_location
global proj_location
global proj_matrix
global many_cubes
currentTime = time.time()
green = [ 0.0, 0.25, 0.0, 1.0 ]
one = 1.0;
glViewport(0, 0, int((1360/2)-(512/2)), int((768/2)-(512/2)))
glClearBufferfv(GL_COLOR, 0, green);
glClearBufferfv(GL_DEPTH, 0, one);
glUseProgram(compile_program(vs_source, fs_source))
#proj_matrix = m3dOrtho(-1, 1, -1, 1, -10, 10)
#proj_matrix = m3dPerspective(50.0*math.pi/180.0, 512/512, 0.1, 1000.0)
#proj_matrix = m3dPerspective(m3dDegToRad(50.0), float(self.width) / float(self.height), 0.1, 1000.0);
glUniformMatrix4fv(proj_location, 1, GL_FALSE, proj_matrix)
if (many_cubes == True):
for i in range(0, 24):
f = i + currentTime * 0.3;
mv_matrix = (GLfloat * 16)(*identityMatrix)
T = (GLfloat * 16)(*identityMatrix)
m3dTranslateMatrix44(T, 0, 0, -4)
W = (GLfloat * 16)(*identityMatrix)
m3dTranslateMatrix44(W, sin(2.1 * f) * 0.5, cos(1.7 * f) * 0.5, sin(1.3 * f) * cos(1.5 * f) * 2.0)
RX = (GLfloat * 16)(*identityMatrix)
m3dRotationMatrix44(RX, currentTime * m3dDegToRad(45.0), 0.0, 1.0, 0.0)
RY = (GLfloat * 16)(*identityMatrix)
m3dRotationMatrix44(RY, currentTime * m3dDegToRad(81.0), 1.0, 0.0, 0.0)
mv_matrix = m3dMultiply(W, m3dMultiply(T, m3dMultiply(RY, RX)))
# or can multiply with numpy
#R = np.matmul(np.array(W).reshape(4,4) , np.matmul(np.array(RX).reshape(4,4), np.array(RY).reshape(4,4)))
#mv_matrix = np.matmul(R, np.array(T).reshape(4,4))
# third way this could be done
# T = np.matrix(translate(0.0, 0.0, -4.0)).reshape(4,4)
# W = np.matrix(translate(sin(2.1 * f) * 0.5, cos(1.7 * f) * 0.5, sin(1.3 * f) * cos(1.5 * f) * 2.0)).reshape(4,4)
# RX = np.matrix(rotation_matrix( [1.0, 0.0, 0.0], currentTime * m3dDegToRad(17.0)))
# RY = np.matrix(rotation_matrix( [0.0, 1.0, 0.0], currentTime * m3dDegToRad(13.0)))
# mv_matrix = RX * RY * T * W
glUniformMatrix4fv(mv_location, 1, GL_FALSE, mv_matrix)
glDrawArrays(GL_TRIANGLES, 0, 36)
else:
f = currentTime * 0.3;
mv_matrix = (GLfloat * 16)(*identityMatrix)
T = (GLfloat * 16)(*identityMatrix)
m3dTranslateMatrix44(T, 0, 0, -4)
W = (GLfloat * 16)(*identityMatrix)
m3dTranslateMatrix44(W, sin(2.1 * f) * 0.5, cos(1.7 * f) * 0.5, sin(1.3 * f) * cos(1.5 * f) * 2.0)
RX = (GLfloat * 16)(*identityMatrix)
m3dRotationMatrix44(RX, currentTime * m3dDegToRad(45.0), 0.0, 1.0, 0.0)
RY = (GLfloat * 16)(*identityMatrix)
m3dRotationMatrix44(RY, currentTime * m3dDegToRad(81.0), 1.0, 0.0, 0.0)
mv_matrix = m3dMultiply(W, m3dMultiply(T, m3dMultiply(RY, RX)))
# second way to that can multiply with numpy
#R = np.matmul(np.array(W).reshape(4,4) , np.matmul(np.array(RX).reshape(4,4), np.array(RY).reshape(4,4)))
#mv_matrix = np.matmul(R, np.array(T).reshape(4,4))
# third way this could be done
# T = np.matrix(translate(0.0, 0.0, -4.0)).reshape(4,4)
# W = np.matrix(translate(sin(2.1 * f) * 0.5, cos(1.7 * f) * 0.5, sin(1.3 * f) * cos(1.5 * f) * 2.0)).reshape(4,4)
# RX = np.matrix(rotation_matrix( [1.0, 0.0, 0.0], currentTime * m3dDegToRad(17.0)))
# RY = np.matrix(rotation_matrix( [0.0, 1.0, 0.0], currentTime * m3dDegToRad(13.0)))
# mv_matrix = RX * RY * T * W
glUniformMatrix4fv(mv_location, 1, GL_FALSE, mv_matrix)
glDrawArrays(GL_TRIANGLES, 0, 36)
glutSwapBuffers()
def reshape(self, width, height):
global proj_matrix
proj_matrix = m3dPerspective(m3dDegToRad(50.0), float(self.width) / float(self.height), 0.1, 1000.0);
self.width = width
self.height = height
def keyboard(self, key, x, y ):
global fullscreen
global many_cubes
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
elif key == b'm' or key == b'M':
if (many_cubes == True):
many_cubes = False
else:
many_cubes = 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 - Spinny Cube')
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()
Matrices have to be initialized by the Identity Matrix and ech matrix need its "own" array of data:
identityMatrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]
mv_matrix = (GLfloat * 16)(*identityMatrix)
proj_matrix = (GLfloat * 16)(*identityMatrix)
If the projection matrix is the identity matrix, then the geometry has to be in normalized device space. The normalized device space is in range from (-1, -1, -1) to (1, 1, 1) and form a perfect cube volume. Your geometry is clipped by the far plane of the view volume, because you've set a z translation of -4.0.
Set up an Orthographic Projection Matrix, which "increase" the view volume:
r = right, l = left, b = bottom, t = top, n = near, f = far
X: 2/(r-l) 0 0 0
y: 0 2/(t-b) 0 0
z: 0 0 -2/(f-n) 0
t: -(r+l)/(r-l) -(t+b)/(t-b) -(f+n)/(f-n) 1
def m3dOrtho(l, r, t, b, n, f):
return (GLfloat * 16)(
2/(r-l), 0, 0, 0,
0, 2/(t-b), 0, 0,
0, 0, -2/(f-n), 0,
-(r+l)/(r-l), -(t+b)/(t-b), -(f+n)/(f-n), 1)
proj_matrix = m3dOrtho(-1, 1, -1, 1, -10, 10)
glUniformMatrix4fv(proj_location, 1, GL_FALSE, proj_matrix)
mv_matrix = (GLfloat * 16)(*identityMatrix)
m3dTranslateMatrix44(mv_matrix, 0.0, 0.0, -4.0)
m3dTranslateMatrix44(mv_matrix, sin(2.1 * f) * 0.5,
cos(1.7 * f) * 0.5,
sin(1.3 * f) * cos(1.5 * f) * 2.0)
Further note, that operations like glRotatef and gluPerspective change the current matrix of the deprecated fixed fucntion pipeline and make no sens at all if you use a shader with your own matrix uniforms.
Note, matrix operations can also be performed by libraries like PyGLM or NumPy.
For 3D objects I recommend to use Perspective Projection:
x: 1/(ta*a) 0 0 0
y: 0 1/ta 0 0
z: 0 0 -(f+n)/(f-n) -1
t: 0 0 -2*f*n/(f-n) 0
where:
a = w / h
ta = tan( fov_y / 2 );
2 * n / (r-l) = 1 / (ta * a)
2 * n / (t-b) = 1 / ta
e.g.
import math
def m3dPerspective(fov_y, aspect, n, f):
a = aspect
ta = math.tan( fov_y / 2 )
return (GLfloat * 16)(
1/(ta*a), 0, 0, 0,
0, 1/ta, 0, 0,
0, 0, -(f+n)/(f-n), -1,
0, 0, -2*f*n/(f-n), 0)
proj_matrix = m3dPerspective(50.0*math.pi/180.0, 512/512, 0.1, 1000.0)
glUniformMatrix4fv(proj_location, 1, GL_FALSE, proj_matrix)
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.
import textwrap
import numpy as np
from ctypes import *
from OpenGL.GL import *
from OpenGL.GL.ARB.multitexture import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
class TestOpenglManager():
# -------- Magic functions --------
def __init__(self):
self.window_width = 800
self.window_height = 800
# -------- Glut stuff --------
def reshape(self, w, h):
self.window_width = w
self.window_height = h
def animate(self):
glutPostRedisplay()
def visible(self, vis):
if (vis == GLUT_VISIBLE):
glutIdleFunc(self.animate)
else:
glutIdleFunc(0)
def key_pressed(self, *args):
if args[0] == b"\x1b":
sys.exit()
def run(self):
glutInit(sys.argv)
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
glutInitWindowSize(self.window_width, self.window_height)
glutInitWindowPosition(800, 100)
glutCreateWindow(b'Test')
glutDisplayFunc(self.display)
glutReshapeFunc(self.reshape)
glutIdleFunc(self.animate)
glutVisibilityFunc(self.visible)
glutKeyboardFunc(self.key_pressed)
self.init()
glutMainLoop()
# -------- Resource allocation --------
def init_shaders(self):
def make_vs(source):
vs = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vs, source)
glCompileShader(vs)
result = glGetShaderiv(vs, GL_COMPILE_STATUS)
if not(result):
raise Exception("Error: {0}".format(
glGetShaderInfoLog(vs)
))
return vs
def make_fs(source):
fs = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fs, source)
glCompileShader(fs)
result = glGetShaderiv(fs, GL_COMPILE_STATUS)
if not(result):
raise Exception("Error: {0}".format(
glGetShaderInfoLog(fs)
))
return fs
def make_program(vs, fs):
program = glCreateProgram()
glAttachShader(program, vs)
glAttachShader(program, fs)
glLinkProgram(program)
return program
vs = textwrap.dedent("""
#version 130
in vec3 position;
void main()
{
gl_Position = vec4(position, 1.0f);
}
""")
fs = textwrap.dedent("""
#version 130
out vec4 frag_color;
uniform vec3 color;
void main() {
frag_color = vec4(color,1.0);
}
""")
self.prog = make_program(make_vs(vs), make_fs(fs))
def init_vbos(self):
vertices = np.array([
# Positions
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=np.float32)
indices = np.array([
0, 1, 3,
1, 2, 3
], dtype=np.int32)
vao_id = glGenVertexArrays(1)
vbo_id = glGenBuffers(1)
ebo_id = glGenBuffers(1)
glBindVertexArray(vao_id)
print("Vertices: Uploading {0} bytes".format(
ArrayDatatype.arrayByteCount(vertices)))
glBindBuffer(GL_ARRAY_BUFFER, vbo_id)
glBufferData(
GL_ARRAY_BUFFER,
ArrayDatatype.arrayByteCount(vertices),
vertices, GL_STATIC_DRAW
)
print("Indices: Uploading {0} bytes".format(
ArrayDatatype.arrayByteCount(indices)))
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_id)
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
ArrayDatatype.arrayByteCount(indices),
indices, GL_STATIC_DRAW
)
print("Position: Location {0}".format(
glGetAttribLocation(self.prog, "position")))
vertex_stride = 3
glEnableVertexAttribArray(glGetAttribLocation(self.prog, "position"))
glVertexAttribPointer(glGetAttribLocation(self.prog, "position"),
3, GL_FLOAT, GL_FALSE, vertex_stride, c_void_p(0)
)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
self.obj_metadata = {
"vao_id": vao_id,
"vbo_id": vbo_id,
"ebo_id": ebo_id,
"type": "3v3c3t",
"stride": vertex_stride,
"indices": len(indices),
"vertices": vertices,
"num_triangles": int(len(vertices) / vertex_stride),
"num_vertices": len(vertices)
}
def init(self):
glClearColor(0.0, 0.0, 0.0, 0.0)
# glEnable(GL_MULTISAMPLE)
# glEnable(GL_DEPTH_TEST)
# glEnable(GL_TEXTURE_2D)
self.init_shaders()
self.init_vbos()
def display(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glBindVertexArray(self.obj_metadata["vao_id"])
glUseProgram(self.prog)
glUniform3f(glGetUniformLocation(self.prog, "color"), 0.0, 1.0, 0.0)
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
glBindVertexArray(0)
glUseProgram(0)
glBindVertexArray(0)
glutSwapBuffers()
if __name__ == "__main__":
TestOpenglManager().run()
I've been trying to figure out for a while what could be wrong with the above snippet and I don't understand what's wrong, it should drawing a green triangle but instead I'll get a black screen
What's going on?
First of all your vertex_stride is incorrect. It should be 0 not 3. Lastly with PyOpenGL when calling glDrawElements you need to pass None or ctypes.c_void_p(0) and not 0.
The thing is that the stride is a byte offset between each vertex attribute. So as your vertex is laid out as:
X Y Z
X Y Z
X Y Z
Then there is an offset of 0 bytes between the next pair of X, Y, Z. However if your vertex was laid out in this manner:
X Y Z R G B
X Y Z R G B
X Y Z R G B
Then having the stride as 3 (or more correctly 3 * sizeof(float)) would be correct.