9

我开始在 Python 中使用 OpenGL 4(通过 Pyglet 和一些框架代码,我从网上下载/自己编写用于加载着色器/程序),但我认为我对事情的理解相当好,所以我认为没有我的代码中某处有问题。

那么我的问题是什么?似乎当 numpy 数组或缓冲区达到一定大小时,我在绘图时会发生奇怪的事情。请查看链接的图片以了解我的意思。 http://i.imgur.com/8Oym6VA.png?1

在图片上你可以看到我正在画一些“机器人”,每个机器人都是由 8 个盒子组成的。机器人的数据只计算一次,使用基本的立方体顶点/颜色/索引数据,然后适当地平移/缩放/旋转并附加到更大的数组中。在左图中,我在一个 VAO 中绘制了 172 个机器人,右图有 173 个机器人。正如你所看到的,当我检查那个“神奇”的数字时会发生奇怪的事情。看起来好像所有顶点都以某种方式连接到要在屏幕上绘制的第一个点(第一个绘制的机器人“身体”的右上角部分)。如果我将第一个机器人移动到其他地方,所有点仍然连接到该点 - 以说明我确保图片中的点不以 (0,0,0) 或类似的东西为中心。左图有 131328 个浮点数的顶点和颜色数据,索引数据为 49248 长。右图有 132096 个浮点数的顶点和颜色数据,而索引数据为 49536 个浮点数。如果我把数据分成不止一个VAO,那么我可以很容易地画出价值100倍的机器人(当然是100个VAO)没有任何问题(即使是价值1000倍的1000个VAO的机器人也能很好地工作,除了拿了很多内存和大约 0.2 FPS 的工作)。

为了测试 Pyglets 类(或我自己的)是否有任何问题,我还用 OpenGL 调用重写了整个事情,我遇到了同样的问题。那么,VBO 是否根本就没有那么大(我不知何故怀疑 VBO 每个只能有大约 17k 个三角形)?还是它与 Numpy 有关(我以前在 Numpy 中使用过更大的数组,但我不记得有任何问题)?哦,顺便说一句,浮动的大小似乎并不重要(如果所有顶点都在 [-1,1] 范围内,如果上升到 [-35000, 35000],也会发生同样的事情。

我已经广泛搜索了该主题,但在搜索中没有遇到任何类似的东西——如果有,我道歉。当使用非常大的 numpy 数组时,我能找到的只是 MemoryError,但我的数组远不及产生它所需的大小。

我的系统规格是:

  • 英特尔酷睿 i7
  • 英伟达 GeForce GTX 465
  • Windows 8 64 位
  • Python 3.3(64 位)
  • Pyglet 1.2alpha1
  • 用于 Python 3.3 64 位的 Numpy 二进制包,来自这里

虽然我几乎可以肯定我的代码没有任何问题,但我仍然在此处弹出与绘图相关的片段(同样,我也尝试过使用基本 OpenGL 调用,但效果并不好) .

首先,我有一个“处理程序”类,用于存储大量静态数据,因此我可以通过一个 glDraw* 调用(例如游戏关卡或类似内容)来绘制它。它创建了添加数据的方法、初始化其 VBO 和 VAO 的方法以及获取堆栈并在其上绘制的绘制函数。它还获得了它绘制的程序(顶点/颜色着色器)。

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()

我用一些非常简单的代码初始化处理程序,其中 translateMatrix(x, y, z) 返回一个平移矩阵,而 applyTrans(base, trans) 将 trans 应用于 base 中的每个顶点 (x,y,z,w)。

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()

并在 Pyglet Window 的 on_draw 部分调用它

self.handler.draw()

更新:

我发现了问题所在,我现在觉得自己完全愚蠢:P。显然我忘了指定一个numpy数组的dtype,它默认为'int32'。由于我使用 GL_UNSIGNED_SHORT 标志(又名“uint16”)进行绘图,因此它成为了一个问题。我现在确保它尽可能长时间地保持“uint16”(直到索引数据的最大值高于 2^16)并添加了一个检查以查看 indexData 是“uint16”还是“uint32”并添加了适当的glDraw 命令上的标志。

这似乎已经解决了它,因为我现在可以轻松地将几千个(尝试最多 5.000 个)机器人添加到一个 VBO 并且它仍然有效。

不过,我仍然不明白,为什么它看起来像这样(所有顶点都连接到第一个顶点)以及为什么它会在它出现时开始。indexData 正常时的最大值是 32831,开始起作用时的最大值是 33023。所以这两个值明显低于 2^16,为什么 GL_UNSIGNED_SHORT 仍然不起作用?如果有人可以回答这个问题,我将把这个问题留得更久一些,并在几天后/当我得到答案时关闭。谢谢!

4

0 回答 0