0
\$\begingroup\$

I'm trying to create a sort of PyOpenGL renderer for the Bullet physics engine, and at the moment I'm rotating my basic cubes with math in Python. However, if I do the math in Python that's going to take more time than doing the math with OpenGL.

OpenGL has the ability to rotate a matrix using glRotatef(), but it only rotates the "current matrix". I'm not really interested in convoluting my render loop with an individual check for each object's rotation and position if I don't have to.

So my question is, is there a way I can, for example, load a matrix of points into OpenGL, rotate this matrix and then read it back and give it to my render loop? Or should I instead look for an alternative 3D math library? (Or for that matter, is running the code in python really all that laggy?)

\$\endgroup\$
  • \$\begingroup\$ It seems like you are doing things the old, fixed-function way with calls like glRotatef(). What you probably are looking for are Vertex Buffer Objects (VBOs). These would allow you to pass in an array of transformation (model-view) matrices. You could then use a shader program to take that attribute data and calculate the transforms on the GPU instead. \$\endgroup\$ – CodeSurgeon Jul 29 '18 at 3:26
  • \$\begingroup\$ Actually, I'm not even using glRotatef(), I'm using math in Python that I found on the internet to rotate my cubes. Currently, I'm moving towards VBOs in order to speed up rendering my game terrain, is there a way to rotate parts of a VBO? \$\endgroup\$ – C1ff Jul 30 '18 at 16:20
  • \$\begingroup\$ There are several approaches to rotating a part of a VBO. If you have the chance, you might want to take a look at this video for some ideas (it uses javascript and webgl, but it could give you some ideas to start with). I will try to write up a more thorough answer later today in the meantime. Also, what math library are you using? If you are doing this in python, I can vouch that it will be slow. I have written some math modules in cython that I wouldn't mind cleaning up and sharing, since numpy is not very convenient to use for 3d math. \$\endgroup\$ – CodeSurgeon Jul 31 '18 at 19:35
  • \$\begingroup\$ I've actually been using just the math library and making matrices out of lists. \$\endgroup\$ – C1ff Jul 31 '18 at 20:13
  • \$\begingroup\$ Wrote up an answer as a stream of consciousness. Please let me know if there are any parts that need to be clarified or are worded in a confusing manner! \$\endgroup\$ – CodeSurgeon Jul 31 '18 at 23:32
1
\$\begingroup\$

First of all, it is good that you appear to be on the right track with using VBOs! In general, it is a good idea when working with the GPU to pass as much data to the GPU at once as is possible. This means minimizing the number of draw calls (i.e. send a whole bunch of cubes in a single draw call rather than drawing one cube at a time with glDrawArrays or worse, glVertex).

Let's say that the vertices, texture coordinates, normals, etc are all in a massive interleaved VBO or set of VBOs (e.g. one for vertices, one for normals, and so on). How do you rotate these cubes? One approach would be to pass uniforms in for your model, view, and projection matrices for each cube and draw each cube in your VBO with a single glDrawArrays call. This, however, would be exceedingly slow, and it would be better if we could tell the GPU to draw all of our cubes in one go. While there are other techniques to consider in OpenGL such as instancing, one approach could be to pass in transformation matrices as an additional attribute per-vertex. These matrices could either be calculated on the CPU side (possible, but potentially slow, especially in python) or on the GPU side (fast and what I would generally recommend, but possibly wasteful of GPU resources to calculate these transformations repeatedly for each vertex). You can also consider transforming the vertices on the CPU side every frame and passing the transformed vertices to your shader instead, but this could once again be a bottleneck in python.

In addition to the concerns on the OpenGL/GPU side of things, there are also some unique challenges posed by using python. A useful blog post for me that points out some of these issues can be found here, but I will try to sum up some of the highlights.

  • There is first the issue of Python's memory model. In python, everything, even primitives such as integers, are all bulky objects built from a basic PyObject struct rather than direct, native machine types. This can make accessing the data of the underlying objects slow as a result, with even apparently simple operations such as adding two numbers invoking lots of operations behind the scenes in the python interpreter. For fast math operations, this is not conducive to good performance, and there is generally little to gain from the safety features (such as avoiding overflow) that are offered from this additional layer.
  • The issues of dealing with objects rather than primitives is worsened when you are working with lots of these objects (aka in many matrices and vectors). You mention in the comments that these matrices that you build are actually python lists. While these lists in python are generally contiguous in memory, they contain contiguous PyObjects representing floats rather than just the floats themselves. This can result in a loss of potential data locality. The result is that accessing each element in your matrices would be slower as well.
  • Finally, the performance of loops and function calls is significantly slower in python than it is in say, C. Games generally have a soft realtime requirement for performance, meaning that we generally want them to run interactively at a smooth framerate. If you are targeting 60 fps, for example, this means that you only have about 16.67 milliseconds to execute everything your game needs to do to render a given frame. On my laptop, this amounts to having 100,000 function calls at the most every frame. This can be limiting, especially in tight loops at the core of a game engine. This is frustrating when compared to the overhead of C functions, which are an order of magnitude or two faster to run. This overhead is signficant even when the python calls wrap C calls to system libraries like OpenGL! When possible, these expensive loops should be moved out of python and into faster, typed, and compiled code. The general idea is that you should pass in a group of objects from python to some C function that does all of the hard work!

Hopefully this gives a good idea of some of the challenges that come with using python. I do believe that it should be possible to create great games in python and that scripting languages like python have their place, but there are many factors to consider regarding performance. The choice you make ultimately depends on your workload and what exactly you are rendering to your screen. Let me know if there is anything that I can clear up!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.