Reputation: 1116
I'm trying to create a "modern OpenGL" application using Python. Specifically, I'm using pyopengl to handle the OpenGL interface, and I'm using pygame to handle window generation, context, display, etc.
The problem I'm having is that I cannot rotate a cube object around more than 1 axis.
If I rotate around a single axis, then the program seems to function as intended, but if I try to create a rotation around more than 1 axis (by multiplying the rotation matrices together, as per the tutorial - there's a github link to the code here), then all I see is a quad which stretches and flips, rather than a rotating cube.
Notes: I have followed this exact same tutorial before just fine using GLFW on Windows, but I'm now on ubuntu and trying to use pygame rather than GLFW to handle the context. From what I've seen in other tutorials, this should work just fine. Perhaps I'm missing something?
I have tried re-structuring the code into a flat, sequential script, but it didn't make any difference. I have tried re-ordering the code to match the tutorial as closely as possible, but that also didn't work. I have also tried removing the vertex array object, but that didn't work either. It seems that my code structure isn't the issue (happy to be corrected on that!), but that I'm missing something on the OpenGL side of things.
How can I create the desired rotation around more than 1 axis so that I can see the expected rotating cube?
Thanks in advance for any help you can provide.
The code I'm using is as follows:
from OpenGL.GL import *
import OpenGL.GL.shaders
import ctypes
import pygame
import numpy
import pyrr
vertex_shader = """
#version 130
in vec4 position;
in vec4 colour;
uniform mat4 transformation;
out vec4 newColour;
void main()
{
gl_Position = transformation * position;
newColour = colour;
}
"""
fragment_shader = """
#version 130
in vec4 newColour;
out vec4 outColour;
void main()
{
outColour = newColour;
}
"""
vertices = [
-0.5, -0.5, 0.5, 1.0, 1.0, 0.0, 0.0, 1.0,
0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 1.0,
0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0,
-0.5, 0.5, 0.5, 1.0, 1.0, 0.0, 1.0, 1.0,
-0.5, -0.5, -0.5, 1.0, 0.0, 0.0, 0.0, 1.0,
0.5, -0.5, -0.5, 1.0, 0.0, 1.0, 0.0, 1.0,
0.5, 0.5, -0.5, 1.0, 0.0, 1.0, 1.0, 1.0,
-0.5, 0.5, -0.5, 1.0, 0.0, 0.0, 1.0, 1.0
]
vertices = numpy.array(vertices, dtype=numpy.float32)
indices = [0, 1, 2, 0, 2, 3, # front
5, 4, 7, 5, 7, 6, # back 7b 6c
3, 2, 7, 7, 2, 6, # top 3m 2w
2, 1, 5, 2, 5, 6, # right
1, 0, 5, 5, 0, 4, # bottom 4k 5g
3, 7, 4, 3, 4, 0 # left 0r 1y
]
indices = numpy.array(indices, dtype=numpy.uint32)
def create_object(shader):
# Create a new VAO (Vertex Array Object) and bind it
vertex_array_object = glGenVertexArrays(1)
glBindVertexArray(vertex_array_object)
# Generate buffers to hold our vertices
vertex_buffer = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer)
# Generate buffers to hold buffer indices
element_buffer = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer)
# Get the position of the 'position' in parameter of our shader and bind it.
position = glGetAttribLocation(shader, 'position')
glEnableVertexAttribArray(position)
# Describe the position data layout in the buffer
glVertexAttribPointer(position, 4, GL_FLOAT, False, 32, ctypes.c_void_p(0))
# Get the position of the 'colour' in parameter of our shader and bind it.
colour = glGetAttribLocation(shader, 'colour')
glEnableVertexAttribArray(colour)
# Describe the colour data layout in the buffer
glVertexAttribPointer(colour, 4, GL_FLOAT, False, 32, ctypes.c_void_p(16))
# Send the data over to the buffers
glBufferData(GL_ARRAY_BUFFER, 256, vertices, GL_STATIC_DRAW) # Vertices array
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 144, indices, GL_STATIC_DRAW) # Indices array
# Unbind the VAO first (Important)
glBindVertexArray(0)
# Unbind other stuff
glDisableVertexAttribArray(position)
glBindBuffer(GL_ARRAY_BUFFER, 0)
return vertex_array_object
def display(shader, vertex_array_object):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glUseProgram(shader)
rot_x = pyrr.matrix44.create_from_x_rotation(0.5 * pygame.time.get_ticks() / 1000, dtype=numpy.float32)
rot_y = pyrr.matrix44.create_from_y_rotation(0.8 * pygame.time.get_ticks() / 1000, dtype=numpy.float32)
rot = rot_x * rot_y
transform_location = glGetUniformLocation(shader, 'transformation')
glUniformMatrix4fv(transform_location, 1, GL_FALSE, rot) # change final argument to rot_x for single axis rotation
glBindVertexArray(vertex_array_object)
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, None)
glBindVertexArray(0)
glUseProgram(0)
def main():
display_width = 512
display_height = 512
pygame.init()
pygame.display.set_mode((display_width, display_height), pygame.OPENGL | pygame.DOUBLEBUF)
glClearColor(0.0, 0.0, 0.1, 1.0)
glEnable(GL_DEPTH_TEST)
# glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
shader = OpenGL.GL.shaders.compileProgram(
OpenGL.GL.shaders.compileShader(vertex_shader, GL_VERTEX_SHADER),
OpenGL.GL.shaders.compileShader(fragment_shader, GL_FRAGMENT_SHADER)
)
vertex_array_object = create_object(shader)
clock = pygame.time.Clock()
while True:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
if event.type == pygame.KEYUP and event.key == pygame.K_ESCAPE:
return
display(shader, vertex_array_object)
pygame.display.set_caption("FPS: %.2f" % clock.get_fps())
pygame.display.flip()
if __name__ == '__main__':
try:
main()
finally:
pygame.quit()
Upvotes: 1
Views: 854
Reputation: 210918
Pyrr's Matrix44
behaves like and is implemented using numpy.array
.
For Numpy arrays the *
operator means element-wise multiplication, while the @
operator means matrix multiplication. See array
.
The expression
rot = rot_x * rot_y
performs a component-wise multiplication of the elements of the matrix.
The correct operator for the matrix multiplication is:
rot = rot_x @ rot_y
Upvotes: 1
Reputation: 469
Alternatively to the previous answer, you can also use pyrr's matrix multiplication function.
rot = pyrr.matrix44.multiply(rot_x, rot_y)
Upvotes: 1