If you have never encountered OpenGL, then you might not be aware of the fact that it is an excellent 3D graphics API and an absolute pleasure to work with. One caveat however is that setting up your application to use OpenGL can be... kind of painful. The struggle comes from the fact that each GUI library handles the OpenGL rendering context slightly differently and some GUIs require various tricks . Also initializing the viewport can be done in several different ways and getting it right (while trying to hack some code together) can also be painful, especially if you have to recompile every time you change something.

PyOpenGL , as the name implies, is the Pythonic OpenGL API. If you have OpenGL experience but have never used PyOpenGL you should take the time to read the PyOpenGL for OpenGL Programmers tutorial. Although you can pretty much write C OpenGL in Python the PyOpenGL package also provides a nicer more python oriented interface for the OGL API.

3.1 Use the Pythonic functions A large portion of the PyOpenGL API has pythonic wrappers, try to use those functions instead of the ones you are used to in C, this will make your code cleaner and therefore easier to understand and debug. Look at PyOpenGL for OpenGL Programmers and the PyOpenGL doc pages to see exactly which calls are pythonic. For example you can call glGenTexture with one argument, the number of texture IDs you want, and it will return either a single integer based texture id or a list of texture IDs. You can also call it the standard way, initializing an ID and providing it to the function. Another example is you can call glVertexPointer the C way providing the necessary arguments or use the pythonic glVertexPointer[f|b|i|] set and only providing an array of the given type.

3.2 Zero is not NULL Some OpenGL calls require a pointer to a data buffer, and sometimes you must call these functions with a NULL pointer. In C/C++ it is perfectly legal to do the following: # 's 1 glBindBuffer ( GL_ARRAY_BUFFER , buffer )

2 glVertexPointer ( 3 , GL_FLOAT , 0 , 0 )

but in Python you have to do this: # 's 1 glBindBuffer ( GL_ARRAY_BUFFER , buffer )

2 glVertexPointer ( 3 , GL_FLOAT , 0 , None )



3.3 NumPy array data type When creating NumPy arrays you don't normally have to specify the data type, it will automatically be inferred from the content. However on a 64bit machine NumPy seems to automatically make floats into float64s which are actually doubles. Surely you can already see the problem with this, you are telling OGL that you are giving it an array of 256 floats, but instead it gets 128 doubles... the result is something like this: That is supposed to be a solid cube, surrounded with a wireframe cube Specifying the data type for a numpy array can be done using the dtype parameter to the array function: # 's 1 a = array ([ 0.0 , 0.5 , 1.0 ], dtype = float32 )



3.4 Generating Buffers Sometimes you would need to generate some buffers, for example using the glGenBuffers VBO call or the old version of the glGenTextures texture generation call. In this case you must define the variable beforehand and initialize it to the correct type: # 's 1 buffer = 0

2 glGenTextures ( 1 , buffer )

3

4 # Sometimes you need the exact data type like above:

5 from OpenGL.raw import GL

6 buffer = GL . GLuint ( 0 )

7 glGenBuffers ( 1 , buffer )



3.5 Loading Image Textures Although there are plenty of good imaging libraries for C/C++ I doubt that any of them are as easy to use as the Python Image Library (PIL). Although PIL's commercially licensed version has an OpenGL Image interface, with the free version in order to load an image as a texture you can do something like: # 's 1 import Image

2 img = Image . open ( 'some_img.png' ) # .jpg, .bmp, etc. also work

3 img_data = numpy . array ( list ( img . getdata ()), numpy . int8 )

4

5 texture = glGenTextures ( 1 )

6 glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 )

7 glBindTexture ( GL_TEXTURE_2D , texture )

8 glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP )

9 glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP )

10 glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR )

11 glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR )

12 glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGB , img . size [ 0 ], img . size [ 1 ], 0 , GL_RGB , GL_UNSIGNED_BYTE , img_data )



3.6 Geometry Rendering Performance In general the fewer OpenGL calls you make, the better, true in C but even more so in Python. The performance hierarchy for drawing geometry in OpenGL is as follows (from slowest to fastest): Immediate mode (glBegin/glEnd) - One of the strengths of OpenGL and one of the main reason OGL is so easy to learn, but it's also the slowest way of drawing primitives. # 's 1 # Somewhere before the drawing code

2 circle = [[ radius * math . sin ( math . radians ( deg )), radius * math . cos ( math . radians ( deg )), 0.0 ] for deg in xrange ( 360 )]

3

4 # In the drawing code

5 glBegin ( GL_LINE_STRIP )

6 for pt in circle :

7 glVertex ( pt )

8 glEnd ()

Vertex arrays - By precalculating vertex coordinates, color, normals and texture values and loading them into buffers you not only reduce the number of calls you make but you also allow the hardware to more efficiently transfer the data to the video card. The buffer data is still sent to the video card on every frame but this time in one giant block. # 's 1 glVertexPointerf ( circle )

2 glDrawArrays ( GL_LINE_STRIP , 0 , len ( circle ))

Interleaved Vertex Arrays - Same as vertex arrays, except that you can place multiple types of data inside one giant buffer and then render it all using the same glDrawArrays/glDrawElements calls as before. Create a buffer with the data then use glInterleavedArrays to prepare it for rendering. Vertex Buffer Objects (VBOs) - The geometry equivalent of the texture objects, VBOs allow you to move the vertex, color, normal, texture data to the video card and render it when needed, without moving the data to the card on every render call. Depending on your application, VBOs can provide a huge performance boost... although their support in PyOpenGL is a bit rough. Making things a lot easier is Nathan Ostgard's VBO class # 's 1 # Generate Buffers (inside Init method)

2 circle = array ( circle , dtype = float32 )

3 vertex_vbo = GL . GLuint ( 0 )

4 glGenBuffers ( 1 , vertex_vbo )

5 glBindBuffer ( GL_ARRAY_BUFFER , circle )

6 glBufferData ( GL_ARRAY_BUFFER , ADT . arrayByteCount ( circle ), ADT . voidDataPointer ( circle ), GL_STATIC_DRAW_ARB )

7

8 # Draw buffers (inside Render method)

9 glBindBuffer ( GL_ARRAY_BUFFER , vertex_vbo )

10 glVertexPointer ( 3 , GL_FLOAT , 0 , None )

11 glDrawArrays ( GL_LINE_STRIP , 0 , circle . shape [ 0 ])

