I'm trying to create a 2D python game with Pygame.
But I realized that I could use PyOpengl, so I try to learn how use it. I'm able to create a triangle strip on my screen with the vertices. However, I would like to render these triangles using indices. In the code below,I tried to render a simple square with 2 triangles, 4 vertices in one list, and indices in another list. Unfortunately I don't understand why the square doesn't appear. I probably misunderstood something about the handling of buffers, vaos, vbos or even pyopengl itself. ( I put comments to explain what I think the lines of code do )
import numpy as np
import pygame
from OpenGL.GL import *
vaos = []
vbos = []
pygame.init()
screen = pygame.display.set_mode((512, 512), pygame.OPENGL | pygame.DOUBLEBUF)
pygame.display.set_caption("With OpenGl")
glViewport(0, 0, 512, 512)
# Creating vertices and indices
vertices = [-0.5, 0.5,
-0.5, -0.5,
0.5, -0.5,
0.5, 0.5]
indices = [0, 1, 3,
3, 1, 2]
# Creating VAO and binding it
vaoID = glGenVertexArrays(1)
vaos.append(vaoID)
glBindVertexArray(vaoID)
# Creating VBO for verticices and binding it
verticesID = glGenBuffers(1)
vbos.append(verticesID)
glBindBuffer(GL_ARRAY_BUFFER, verticesID)
# Creating bufferData to store vertices position
verticesBuffer = np.array(vertices, dtype='f')
glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW)
glVertexAttribPointer(0, 2, GL_FLOAT, False, 0, None)
glBindBuffer(GL_ARRAY_BUFFER, 0)
# Creating VBO for indices and binding it
indicesID = glGenBuffers(1)
vbos.append(indicesID)
# Creating bufferData to store indices
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesID)
indicesBuffer = np.array(indices, dtype='uint16')
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL_STATIC_DRAW)
# unbind VAO because I finished to use it
glBindVertexArray(0)
run = True
clock = pygame.time.Clock()
while run:
clock.tick(60)
for e in pygame.event.get():
if e.type == pygame.QUIT:
run = False
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_o:
run = False
# Preparing screen
glClear(GL_COLOR_BUFFER_BIT)
glClearColor(1, 0, 0, 1)
# Rendering
# binding VAO
glBindVertexArray(vaoID)
# enabling VertexAttribArray to get vertices
glEnableVertexAttribArray(0)
# draw elements
glDrawElements(GL_TRIANGLES, len(vertices) * 4, GL_UNSIGNED_INT, 0)
# disabling VertexAttribArray and unbind VAO because I finished to use them
glDisableVertexAttribArray(0)
glBindVertexArray(0)
pygame.display.flip()
# Cleanup
for vao in vaos:
glDeleteVertexArrays(1, vao)
for vbo in vbos:
glDeleteBuffers(1, vbo)
pygame.quit()
When a named buffer object is bound to the GL_ELEMENT_ARRAY_BUFFER
target, the last parameter of glDrawElements
is treated as a byte offset into the buffer object's data store. However, the type of the parameter has to be a pointer anyway (ctypes.c_void_p
).
Hence, if the offset is 0, then the last argument can either be None
or ctypes.c_void_p(0)
otherwise the offset must be cast to ctypes.c_void_p
.
In addition, the type argument must match the type of the indices in the buffer. Since the type of the indexes is 'uint16'
, the type argument must be GL_UNSIGNED_SHORT
instead of GL_UNSIGNED_INT
.
The 3 possible combinations of index type and type argument are:
GL_UNSIGNED_BYTE
GL_UNSIGNED_SHORT
GL_UNSIGNED_INT
The correct code is either
glDrawElements(GL_TRIANGLES, len(vertices) * 4, GL_UNSIGNED_INT, 0)
glDrawElements(GL_TRIANGLES, len(vertices) * 4, GL_UNSIGNED_SHORT, None)
or
glDrawElements(GL_TRIANGLES, len(vertices) * 4, GL_UNSIGNED_SHORT, ctypes.c_void_p(0))