In this Python Modern Opengl article i want to show you Rotating The Cube. before starting our main topic about Opengl Rotating Cube, you can check the previous articles.
Also for math you need to install a library in python
1 |
pip install pyrr |
Python Modern Opengl Rotating the Cube
Let’s create example, so now this is the complete code for Python Modern Opengl Rotating The Cube.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
import glfw from OpenGL.GL import * import OpenGL.GL.shaders import numpy as np import pyrr def main(): if not glfw.init(): return window = glfw.create_window(720, 600, "Codeloop.org - Pyopengl Rotating Cube", None, None) if not window: glfw.terminate() return glfw.make_context_current(window) cube = [-0.5, -0.5, 0.5, 1.0, 0.0, 0.0, 0.5, -0.5, 0.5, 0.0, 1.0, 0.0, 0.5, 0.5, 0.5, 0.0, 0.0, 1.0, -0.5, 0.5, 0.5, 1.0, 1.0, 1.0, -0.5, -0.5, -0.5, 1.0, 0.0, 0.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.0, 0.5, 0.5, -0.5, 0.0, 0.0, 1.0, -0.5, 0.5, -0.5, 1.0, 1.0, 1.0] # convert to 32bit float cube = np.array(cube, dtype=np.float32) indices = [0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4, 4, 5, 1, 1, 0, 4, 6, 7, 3, 3, 2, 6, 5, 6, 2, 2, 1, 5, 7, 4, 0, 0, 3, 7] indices = np.array(indices, dtype=np.uint32) VERTEX_SHADER = """ #version 330 in vec3 position; in vec3 color; out vec3 newColor; uniform mat4 transform; void main() { gl_Position = transform * vec4(position, 1.0f); newColor = color; } """ FRAGMENT_SHADER = """ #version 330 in vec3 newColor; out vec4 outColor; void main() { outColor = vec4(newColor, 1.0f); } """ # Compile The Program and shaders shader = OpenGL.GL.shaders.compileProgram(OpenGL.GL.shaders.compileShader(VERTEX_SHADER, GL_VERTEX_SHADER), OpenGL.GL.shaders.compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER)) # Create Buffer object in gpu VBO = glGenBuffers(1) # Bind the buffer glBindBuffer(GL_ARRAY_BUFFER, VBO) glBufferData(GL_ARRAY_BUFFER, 192, cube, GL_STATIC_DRAW) # Create EBO EBO = glGenBuffers(1) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO) glBufferData(GL_ELEMENT_ARRAY_BUFFER, 144, indices, GL_STATIC_DRAW) # get the position from shader position = glGetAttribLocation(shader, 'position') glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(0)) glEnableVertexAttribArray(position) # get the color from shader color = glGetAttribLocation(shader, 'color') glVertexAttribPointer(color, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(12)) glEnableVertexAttribArray(color) glUseProgram(shader) glClearColor(0.0, 0.0, 0.0, 1.0) glEnable(GL_DEPTH_TEST) while not glfw.window_should_close(window): glfw.poll_events() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) rot_x = pyrr.Matrix44.from_x_rotation(0.5 * glfw.get_time()) rot_y = pyrr.Matrix44.from_y_rotation(0.8 * glfw.get_time()) transformLoc = glGetUniformLocation(shader, "transform") glUniformMatrix4fv(transformLoc, 1, GL_FALSE, rot_x * rot_y) # Draw Cube glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, None) glfw.swap_buffers(window) glfw.terminate() if __name__ == "__main__": main() |
So if you have followed my previous articles on Python Modern Opengl Programming, most of these codes will be familiar to you.but i will explain again some of them.
These are the cube values and also different color for the values, also we have converted the values to 32bit value.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
cube = [-0.5, -0.5, 0.5, 1.0, 0.0, 0.0, 0.5, -0.5, 0.5, 0.0, 1.0, 0.0, 0.5, 0.5, 0.5, 0.0, 0.0, 1.0, -0.5, 0.5, 0.5, 1.0, 1.0, 1.0, -0.5, -0.5, -0.5, 1.0, 0.0, 0.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.0, 0.5, 0.5, -0.5, 0.0, 0.0, 1.0, -0.5, 0.5, -0.5, 1.0, 1.0, 1.0] # convert to 32bit float cube = np.array(cube, dtype=np.float32) |
Because we are working with EBO (Element Buffer Object) we need to create indices for our cube, also we have converted to 32bit int.
1 2 3 4 5 6 7 8 |
indices = [0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4, 4, 5, 1, 1, 0, 4, 6, 7, 3, 3, 2, 6, 5, 6, 2, 2, 1, 5, 7, 4, 0, 0, 3, 7] indices = np.array(indices, dtype = np.uint32) |
These are the vertex and fragment shaders.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
VERTEX_SHADER = """ #version 330 in vec3 position; in vec3 color; out vec3 newColor; uniform mat4 transform; void main() { gl_Position = transform * vec4(position, 1.0f); newColor = color; } """ FRAGMENT_SHADER = """ #version 330 in vec3 newColor; out vec4 outColor; void main() { outColor = vec4(newColor, 1.0f); } """ |
In the vertex shader at the top we have the version of shader, after that we have two input values for our position and color. also we have an output value for our color that we will use that in the fragment shader. we have a new value in our vertex shader that is called uniform. OK now in the fragment shader we have the same shader version and we are going to take the input value of new color in our fragment shader.
What is Uniform Variable?
So a uniform is a global Shader variable declared with the “uniform” storage qualifier. These act as parameters that the user of a shader program can pass to that program. Their values are stored in a program object.
Uniforms are so named because they do not change from one shader invocation to the next within a particular rendering call. This makes them unlike shader stage inputs and outputs, which are often different for each invocation of a shader stage.
What Are Shaders?
Shaders are little programs that rest on the GPU. These programs are run for each specific section of the graphics pipeline. so In a basic sense, shaders are nothing more than programs transforming inputs to outputs. Shaders are also very isolated programs.
Vertex Shader
The vertex shader is a program on the graphics card that processes each vertex and its attributes as they appear in the vertex array. Its duty is to output the final vertex position in device coordinates and to output any data the fragment shader requires. That’s why the 3D transformation should take place here. The fragment shader depends on attributes like the color and texture coordinates, which will usually be passed from input to output without any calculations. Remember that our vertex position is already specified as device coordinates and no other attributes exist, so the vertex shader will be fairly bare bones.
Fragment Shader
the output from the vertex shader is interpolated over all the pixels on the screen covered by a primitive. These pixels are called fragments and this is what the fragment shader operates on. Just like the vertex shader it has one mandatory output, the final color of a fragment. It’s up to you to write the code for computing this color from vertex colors, texture coordinates and any other data coming from the vertex shader.
Compile the program and shaders
1 2 |
shader = OpenGL.GL.shaders.compileProgram(OpenGL.GL.shaders.compileShader(VERTEX_SHADER, GL_VERTEX_SHADER), OpenGL.GL.shaders.compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER)) |
The next step is to upload this vertex data to the graphics card. This is important because the memory on your graphics card is much faster and you won’t have to send the data again every time your scene needs to be rendered (about 60 times per second).
This is done by creating a Vertex Buffer Object (VBO):
1 |
VBO = glGenBuffers(1) |
Now it is time to create EBO
1 2 3 |
EBO = glGenBuffers(1) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO) glBufferData(GL_ELEMENT_ARRAY_BUFFER, 144, indices, GL_STATIC_DRAW) |
What is EBO?
To explain how element buffer objects work it’s best to give an example: suppose we want to draw a rectangle instead of a triangle. We can draw a rectangle using two triangles (OpenGL mainly works with triangles). This will generate the following set of vertices:
1 2 3 4 5 6 7 8 9 10 |
vertices = [ // first triangle 0.5f, 0.5f, 0.0f, // top right 0.5f, -0.5f, 0.0f, // bottom right -0.5f, 0.5f, 0.0f, // top left // second triangle 0.5f, -0.5f, 0.0f, // bottom right -0.5f, -0.5f, 0.0f, // bottom left -0.5f, 0.5f, 0.0f // top left ] |
As you can see, there is some overlap on the vertices specified. We specify bottom right and top left twice! This is an overhead of 50% since the same rectangle could also be specified with only 4 vertices, instead of 6. This will only get worse as soon as we have
more complex models that have over 1000s of triangles where there will be large chunks that overlap. What would be a better solution is to store only the unique vertices and then specify the order at which we want to draw these vertices in. In that case we would only have to store 4 vertices for the rectangle, and then just specify at which order we’d like to draw them.
Wouldn’t it be great if OpenGL provided us with a feature like that?
Thankfully, element buffer objects work exactly like that. An EBO is a buffer, just like a vertex buffer object, that stores indices that OpenGL uses to decide what vertices to draw. This so called indexed drawing is exactly the solution to our problem. To get started we first have to specify the (unique) vertices and the indices to draw them as a rectangle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
cube = [-0.5, -0.5, 0.5, 1.0, 0.0, 0.0, 0.5, -0.5, 0.5, 0.0, 1.0, 0.0, 0.5, 0.5, 0.5, 0.0, 0.0, 1.0, -0.5, 0.5, 0.5, 1.0, 1.0, 1.0, -0.5, -0.5, -0.5, 1.0, 0.0, 0.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.0, 0.5, 0.5, -0.5, 0.0, 0.0, 1.0, -0.5, 0.5, -0.5, 1.0, 1.0, 1.0] indices = [0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4, 4, 5, 1, 1, 0, 4, 6, 7, 3, 3, 2, 6, 5, 6, 2, 2, 1, 5, 7, 4, 0, 0, 3, 7] |
Also these lines of codes are for cube rotation in x and y positions
1 2 3 4 5 |
rot_x = pyrr.Matrix44.from_x_rotation(0.5 * glfw.get_time()) rot_y = pyrr.Matrix44.from_y_rotation(0.8 * glfw.get_time()) transformLoc = glGetUniformLocation(shader, "transform") glUniformMatrix4fv(transformLoc, 1, GL_FALSE, rot_x * rot_y) |
So run the code and this will be the result also i must say that right now it is s static image but when you run the code in your IDE you will see cube is rotating.
Subscribe and Get Free Video Courses & Articles in your Email
how to make an rotating cube in python with opengl 🙂
first step: copy and paste this code
thanks for watching :)))))))