Search code examples
pythonpython-3.xopenglshaderpyopengl

passing data to glBufferData in PyOpenGL, empty screen


I am using PyOpenGL to draw a simple triangle. However, I only get an empty screen. I believe the issue is that I don't pass data to glBufferData correctly. I tried to follow the answer in this stackoverflow question, but with no result. The shaders compile and work fine since drawing using glBegin() and glVertex() works. However, drawing using glDrawElements or glDrawArrays do not work. Here is my code (I am using pygame for the OpenGL window):

import numpy as np
import pygame as pg
from pygame.locals import *
from array import array
SCREEN_SIZE = (800, 600)


from OpenGL.GL import *
from OpenGL.GLU import *

def resize(width, height):

    glViewport(0, 0, width, height)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(60.0, float(width)/height, .1, 1000.)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()




def create_shader(shader_type,source):
    shader = glCreateShader(shader_type)
    glShaderSource(shader,source)
    glCompileShader(shader)
    print(glGetShaderiv(shader, GL_COMPILE_STATUS, None))
    return shader

pg.init()
screen = pg.display.set_mode(SCREEN_SIZE, HWSURFACE|OPENGL|DOUBLEBUF)

resize(*SCREEN_SIZE)

#creating and compiling fragment shader
fragment = create_shader(GL_FRAGMENT_SHADER,"""

#version 130

out vec4 finalColor; 

void main()
{
    finalColor = vec4(.0,0.0,1.0,1.0);
}
""")

#creating and compiling vertex shader
vertex = create_shader(GL_VERTEX_SHADER,"""

#version 130


in vec3 pos;

void main()
{   

    gl_Position=gl_ProjectionMatrix*gl_ModelViewMatrix *vec4(pos,1.0);



}
""")

#create and link program
program = glCreateProgram()
glAttachShader(program, fragment)
glAttachShader(program, vertex)
glLinkProgram(program)
#get location of the position variable in vertex shader
posAttrib = glGetAttribLocation(program, "pos")


vbo=glGenBuffers(1)
ebo=glGenBuffers(1)
vao=glGenVertexArrays(1)

#specify the integer and float types to use in the glGenBuffer function
dtypei=np.dtype('<u4')
dtypef=np.dtype('<f4')

glBindVertexArray(vao)

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, np.array([0,1,2],dtype=dtypei), GL_STATIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, vbo)

vert=[-1.5, -0.5, -4.0, -0.5, -0.5, -4.0, -0.5, 0.5, -4.0]
nparray = np.array(vert,dtype=dtypef)
glBufferData(GL_ARRAY_BUFFER, nparray, GL_STATIC_DRAW)

#can also try using the array library
#ar = array("f",vert)
#glBufferData(GL_ARRAY_BUFFER, ar.tostring(), GL_STATIC_DRAW)

#specify how the variable pos gets data from the buffer data
glEnableVertexAttribArray(posAttrib)
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, False, 3*4,0)

while 1:


    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(1.0, 1.0, 1.0, 0.0)

    glUseProgram(program)
    glBindVertexArray(vao)
    #this draw function produces NOTHING
    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0)
    #can also try using glDrawArrays
    #glDrawArrays(GL_TRIANGLES, 0, 3)

    #this drawing produces a triangle
    #glBegin(GL_TRIANGLES)
    #glColor3f( 0,1,0 )
    #glVertex3f( -1.5, -0.5, -4.0 )
    #glColor3f( 0,1,1 )
    #glVertex3f( -0.5, -0.5, -4.0 )
    #glColor3f( 1,1,0 )
    #glVertex3f( -0.5, 0.5, -4.0 )    
    #glEnd()



    pg.display.flip()
    pg.time.delay(10)

You can uncomment the glBegin()...glEnd() part and you will see that a blue triangle will be drawn, and thus the shaders work fine.

I have tried to change the variables dtypei and dtypef (which correspond to the int type respectively float type of the numpy arrays passed to glGenBuffers), with no luck. The different types one can try are

dtypei=np.dtype('<i4')
dtypei=np.dtype('>i4')
dtypei=np.dtype('=i4')
dtypei=np.int32

dtypei=np.dtype('<u4')
dtypei=np.dtype('>u4')
dtypei=np.dtype('=u4')
dtypei=np.uint32

dtypef=np.dtype('>f4')
dtypef=np.dtype('<f4')
dtypef=np.dtype('=f4')
dtypef=np.float32

which correspond to integers/floats with different byte orders (uint32, int32 and float32 are probably redundant). See this documentation. I also tried using the array object from the array library, as was also suggested in the other stackoverflow post, also without any result

I am really lost here.


Solution

  • The issue is not the numpy data type object, it's the call glVertexAttribPointer and glDrawElements:

    The data type of the 6th paramter of glVertexAttribPointer has to be ctypes.c_void_p

    This means you have to use a ctypes.cast:

    glVertexAttribPointer(posAttrib, 3, GL_FLOAT, False, 3*4, ctypes.cast(0, ctypes.c_void_p))
    

    or None:

    glVertexAttribPointer(posAttrib, 3, GL_FLOAT, False, 3*4, None)
    


    The same is the case for the 4th parameter of glDrawElements

    Either

    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, ctypes.cast(0, ctypes.c_void_p))
    

    or

    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, None)