I'm taking intro to computer graphics this semester, currently getting into using PyOpenGL and PyQt5 to explore barycentric coordinates. My classmates using Windows machines are able to run the following code with no issues right out of the box, yet all of us on OSX are running into the same issues. I'm hoping someone else out there with more experience with this kind of environment might have an idea how we can solve these issues; we've all done plenty of googling already, with no results. Thanks!
Code:
import sys from array import array from ctypes import c_void_p from OpenGL.GL import * from OpenGL.GLU import * from PyQt5.QtGui import QImage, qRgb from PyQt5.QtOpenGL import QGLWidget from PyQt5.QtWidgets import QApplication from textwrap import dedent # create a function that draws a triangle via software rasterization def softwareRasterization(width, height, vertices, colors): image = QImage(width, height, QImage.Format_RGB32) # TODO: compute the bounding box around the given vertices # TODO: compute the barycentric coordinates for each point in the box # TODO: color the points that are inside of the triangle if image.save("triangle.jpg", None, 100): print("Output triangle.jpg") else: print("Unable to save triangle.jpg") # extend QGLWidget to draw a triangle via hardware rasterization class HardwareRasterizationWidget(QGLWidget): def __init__(self, vertices, colors, *args, **kwargs): super().__init__(*args, **kwargs) self.vertices = array('f') # TODO: convert the input coordinate to normalized device coordinates self.colors = array('f', colors) def _sizeof(self, a): return a.itemsize * len(a) def initializeGL(self): verticesSize = self._sizeof(self.vertices) # create a new Vertex Array Object on the GPU which saves the attribute # layout of our vertices vao = glGenVertexArrays(1) glBindVertexArray(vao) # create a buffer on the GPU for position and color data dataBuffer = glGenBuffers(1) # upload the data to the GPU, storing it in the buffer we just created # TODO: upload the color data into the GPU buffer as well glBindBuffer(GL_ARRAY_BUFFER, dataBuffer) glBufferData( GL_ARRAY_BUFFER, verticesSize, None, GL_STATIC_DRAW ) glBufferSubData( GL_ARRAY_BUFFER, 0, verticesSize, self.vertices.tostring() ) # load our vertex and fragment shaders into a program object on the GPU program = self.loadShaders() # bind the attribute "position" (defined in our vertex shader) to the # currently bound buffer object, which contains our position data # this information is stored in our vertex array object position = glGetAttribLocation(program, 'position') glEnableVertexAttribArray(position) glVertexAttribPointer( position, 3, GL_FLOAT, GL_FALSE, 0, c_void_p(0) ) # TODO: bind the attribute "color" to the buffer object def loadShaders(self): # create a GL Program Object program = glCreateProgram() # vertex shader # TODO: add a color input and color output vs_source = dedent(""" #version 330 in vec3 position; void main() { gl_Position = vec4(position, 1.0); }\ """) vs = glCreateShader(GL_VERTEX_SHADER) glShaderSource(vs, vs_source) glCompileShader(vs) glAttachShader(program, vs) if glGetShaderiv(vs, GL_COMPILE_STATUS) != GL_TRUE: raise RuntimeError(glGetShaderInfoLog(vs)) # fragment shader # TODO: add a color input with the same name as the vertex output fs_source = dedent(""" #version 330 void main() { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); }\ """) fs = glCreateShader(GL_FRAGMENT_SHADER) glShaderSource(fs, fs_source) glCompileShader(fs) glAttachShader(program, fs) if glGetShaderiv(fs, GL_COMPILE_STATUS) != GL_TRUE: raise RuntimeError(glGetShaderInfoLog(fs)) # use the program glLinkProgram(program) glUseProgram(program) return program def paintGL(self): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glDrawArrays(GL_TRIANGLES, 0, 3) def resizeGL(self, width, height): glViewport(0, 0, width, height) if __name__ == "__main__": width = 640 height = 480 # TODO: prompt the user for 3 points and colors separated by spaces # TODO: validate input and parse into the vertices and colors lists vertices = [ 50, 50, 0, # vertice 1 600, 20, 0, # vertice 2 300, 400, 0 # vertice 3 ] colors = [ 1, 0, 0, # color 1 0, 1, 0, # color 2 0, 0, 1 # color 3 ] softwareRasterization(width, height, vertices, colors) app = QApplication(sys.argv) w = HardwareRasterizationWidget(vertices, colors) pRatio = w.devicePixelRatio() w.resize(width/pRatio, height/pRatio) w.show() sys.exit(app.exec_())
Errors:
Traceback (most recent call last): File "/Users/JesseRichmond/vEnvPyCharm/lib/python3.5/site-packages/OpenGL/latebind.py", line 41, in __call__ return self._finalCall( *args, **named ) TypeError: 'NoneType' object is not callable During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/Users/JesseRichmond/Desktop/Spring 2017 Academics/Projects/310/rasterization.py", line 45, in initializeGL vao = glGenVertexArrays(1) File "/Users/JesseRichmond/vEnvPyCharm/lib/python3.5/site-packages/OpenGL/latebind.py", line 45, in __call__ return self._finalCall( *args, **named ) File "/Users/JesseRichmond/vEnvPyCharm/lib/python3.5/site-packages/OpenGL/wrapper.py", line 657, in wrapperCall result = wrappedOperation( *cArguments ) File "/Users/JesseRichmond/vEnvPyCharm/lib/python3.5/site-packages/OpenGL/platform/baseplatform.py", line 407, in __call__ self.__name__, self.__name__, OpenGL.error.NullFunctionError: Attempt to call an undefined function glGenVertexArrays, check for bool(glGenVertexArrays) before calling