Problems with OpenGL 4 VBOs and Numpy arrays, Pyglet/Python
Asked Answered
T

0

10

I'm getting started with using OpenGL 4 in Python (through Pyglet and some framework code I got off the net/wrote myself for loading Shaders/Programs), but I think I understand things fairly well, so I don't think there is a problem somewhere in my code.

So what is my problem? It seems that when either numpy arrays or the buffers reach a certain size, strange things happen when I'm drawing. Please look at the linked picture to see what I mean. https://i.sstatic.net/63lYp.png

On the picture you can see that I'm drawing some "Robots", each of which is made out of 8 boxes. The data for the robot was calculated just once, using basic Cube vertex/color/index data and then appropriately translated/scaled/rotated and appended into a bigger array. In the picture on the left I'm drawing 172 of those robots in one VAO, the right picture has 173 robots. As you can see, strange stuff happens when I go over that "magical" number. It looks as if all vertices are somehow connected to the first point to be drawn on the screen (upper-right-front part of the first drawn robots "body"). If I move the first robot somewhere else, all points are still connected to that point - to illustrate that I made sure that the point in the picture is not centered to (0,0,0) or something similar. The left picture has vertex and color data with 131328 floats, index data is 49248 long. The right picture has vertex and color data with 132096 floats while index data is 49536 floats long. If I divide the data into more then one VAO, then I can easily draw 100 times worth of robots (with 100 VAOs of course) without any problems (even 1000 times worth of robots with 1000 VAOs worked well, apart from taking quite a lot of memory and working with around 0.2 FPS).

To test out if there is anything wrong with Pyglets classes (or my own) I also rewrote the whole thing with OpenGL calls and I came across the same problem. So, are VBOs simply not meant to be that big (I somehow doubt that VBOs could only have around 17k triangles each)? Or does it have something to do with Numpy (I've worked with bigger arrays in Numpy before and I don't remember having any problems)? Oh btw, the size of the floats doesn't seem to matter (same things happens if all vertices are in the [-1,1] scale of if the go up to [-35000, 35000].

I've searched the topic quite extensively but I haven't come across anything similar in my search - if there is, I apologise. All I could find was the MemoryError when using really big numpy arrays, but my arrays are nowhere near the size needed to produce that.

My system specs are:

  • Intel Core i7
  • Nvidia GeForce GTX 465
  • Windows 8 64-bit
  • Python 3.3 (64-bit)
  • Pyglet 1.2alpha1
  • Numpy binary package for Python 3.3 64-bit from here

And while I'm almost sure there's nothing wrong with my code, I'm still popping the snippets that are related to the drawing here (again, I've tried it with base OpenGL calls too and it didn't work any better).

First off I have a "Handler" class, which is meant to store lots of static data so I can draw it with one glDraw* call (for instance the level of the game or something similar). It creates has methods for adding to it's data, a method to initialize its VBOs and VAO and a draw function, which gets a stack and draws on it. It also gets the program (vertex/color shader) with which it draws.

class Handler:
def __init__(self, program):
    self.vertexData = numpy.array((), dtype=GLfloat)
    self.colorData = numpy.array((), dtype=GLfloat)
    self.indexData = numpy.array((), dtype=GLshort)

    self.program = program

def initBuffers(self):
    self.vao = GLuint()
    glGenVertexArrays(1, self.vao)
    glBindVertexArray(self.vao)

    #=======================================================================
    # Vertices
    #=======================================================================
    self.vertexBufferObject = pyglet.graphics.vertexbuffer.create_buffer(self.vertexData.nbytes, GL_ARRAY_BUFFER, GL_STATIC_DRAW)
    self.vertexBufferObject.bind()
    self.vertexBufferObject.set_data(self.vertexData.ctypes.data)
    glEnableVertexAttribArray(0)
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0)

    #=======================================================================
    # Color
    #=======================================================================
    self.colorBufferObject = pyglet.graphics.vertexbuffer.create_buffer(self.colorData.nbytes, GL_ARRAY_BUFFER, GL_STATIC_DRAW)
    self.colorBufferObject.bind()
    self.colorBufferObject.set_data(self.colorData.ctypes.data)
    glEnableVertexAttribArray(1)
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0)

    #=======================================================================
    # Index
    #=======================================================================
    self.indexBufferObject = pyglet.graphics.vertexbuffer.create_buffer(self.indexData.nbytes, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW)
    self.indexBufferObject.bind()
    self.indexBufferObject.set_data(self.indexData.ctypes.data)

    glBindBuffer(GL_ARRAY_BUFFER, 0)
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
    glBindVertexArray(0)

    self.len = len(self.indexData)

def addTo(self, vertices, color = None, index = None):
    if(color != None):
        self.colorData = numpy.append(self.colorData, color)
    if(index != None):
        self.indexData = numpy.append(self.indexData, index + len(self.vertexData)//4)
    self.vertexData = numpy.append(self.vertexData, vertices)

def draw(self, stack):
    glBindVertexArray(self.vao)
    self.indexBufferObject.bind()
    self.program.install()

    stack.push()
    self.program.usetM4F("modelToWorldMatrix", stack.ready(), row = GL_FALSE)
    glDrawElements(GL_TRIANGLES, self.len, GL_UNSIGNED_SHORT, 0)
    stack.pop()
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
    glBindVertexArray(0)
    self.program.uninstall()

And I initialize the handler with some very simple code, where translateMatrix(x, y, z) returns a translate matrix and applyTrans(base, trans) applies trans to every vertice (x,y,z,w) in base.

self.handler = Handler(self.ObjectColor)
    for i in range(1,n):
        self.handler.addTo(applyTrans(self.robots.vertexData, translateMatrix(2*i*(-1)**(i//2), 1.5*i*(-1)**i, 0.5*i*(-1)**i)), self.robots.colorData, self.robots.indexData)
self.handler.initBuffers()

And call it in the on_draw part of Pyglet Window with

self.handler.draw()

UPDATE:

I've found out what the problem is and I feel completely and utterly stupid now :P. Apparently I forgot to specify the dtype of one of numpy arrays, and it defaulted to 'int32'. Since I was drawing with the GL_UNSIGNED_SHORT flag (aka 'uint16') it became a problem. I've now made sure that it stays 'uint16' as long as possible (till the max of index data gets higher then 2^16) and added a check to see if indexData is 'uint16' or 'uint32' and added the appropriate flag on glDraw command.

This seems to have fixed it, as I can now easily add a few thousand (tried with 5.000 max) robots to one VBO and it still works.

What I still don't understand though, is why it looked like it did (all vertices connected to the first one) and why it started when it did. The max value of indexData when it was still OK was 32831, max value when it started acting up was 33023. So both values are obviously lower then 2^16, so why didn't GL_UNSIGNED_SHORT still work? I'll leave the question open for a bit more in case someone can answer this and will close after a few days/when I get an answer. Thanks!

Trincomalee answered 24/2, 2013 at 3:3 Comment(6)
Blind man's shot in the dark: It looks like this: If a triangle crosses z=0 before projection, the points with z<0 are mapped towards -inf, and those with z>0 towards +inf. Yeah, these triangles obviously should be clipped...Lavery
Thanks for the idea, even though it was wrong. Vertice values played no part in this problem (they could all be < or > then 0 and it still appeared) and also points weren't mapped towards +-inf, they all went just to the starting point (if you rotated you could see that it stopped at that point). I've figured out what the problem was and updated the question. If you have any ideas about the updated question, I'd love to hear them! :)Trincomalee
I want that robut for my skype avatar... :)Impregnable
@mlvljr: Grim reaper tribal ;-)Trincomalee
Yes, but I was after the lonely robot from i.imgur.com/8Oym6VA.png?1 :)Impregnable
Ah haha, crop the picture and use if you want, i'm "open sourcing" my lonely robot :DTrincomalee

© 2022 - 2024 — McMap. All rights reserved.