Проблемы с VBO OpenGL 4 и массивами Numpy, Pyglet/Python
Я начинаю использовать OpenGL 4 в Python (через Pyglet и некоторый фреймворковый код, который я получил из сети / написал сам для загрузки шейдеров / программ), но я думаю, что понимаю вещи довольно хорошо, поэтому я не думаю, что есть проблема где-то в моем коде.
Так в чем моя проблема? Кажется, что, когда числовые массивы или буферы достигают определенного размера, странные вещи случаются, когда я рисую. Пожалуйста, посмотрите на картинку, чтобы понять, что я имею в виду. https://i.imgur.com/8Oym6VA.png?1
На картинке видно, что я рисую несколько "роботов", каждый из которых состоит из 8 коробок. Данные для робота были рассчитаны только один раз с использованием базовых данных вершины / цвета / индекса куба, а затем соответствующим образом переведены / масштабированы / повернуты и добавлены в больший массив. На рисунке слева я рисую 172 таких робота в одном VAO, на правом рисунке 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].
Я довольно широко искал эту тему, но ничего подобного в своем поиске не встречал - если есть, то прошу прощения. Все, что я мог найти, это MemoryError при использовании действительно больших массивов, но мои массивы далеко не соответствуют размеру, необходимому для этого.
Мои системные характеристики:
- Intel Core i7
- Nvidia GeForce GTX 465
- Windows 8 64-битная
- Python 3.3 (64-разрядная версия)
- Pyglet 1.2alpha1
- Numpy бинарный пакет для Python 3.3 64-битный отсюда
И хотя я почти уверен, что в моем коде нет ничего плохого, я все еще показываю фрагменты, связанные с чертежом (опять же, я попробовал его и с базовыми вызовами 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 к каждой вершине (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()
И вызвать его в on_draw части окна Pyglet с
self.handler.draw()
ОБНОВИТЬ:
Я выяснил, в чем проблема, и теперь я чувствую себя совершенно глупо:P. По-видимому, я забыл указать dtype одного из массивов numpy, и по умолчанию он был равен int32. Поскольку я рисовал с флагом GL_UNSIGNED_SHORT (он же "uint16"), это стало проблемой. Теперь я позаботился о том, чтобы он оставался "uint16" как можно дольше (пока максимум данных индекса не превысит 2^16), и добавил проверку, чтобы увидеть, является ли indexData "uint16" или "uint32", и добавил соответствующие флаг команды glDraw.
Это, кажется, исправило это, поскольку теперь я могу легко добавить несколько тысяч (пробовал с максимумом 5000) к одному VBO, и это все еще работает.
Однако я до сих пор не понимаю, почему это выглядело так (все вершины связаны с первой) и почему это началось, когда это произошло. Максимальное значение indexData, когда он все еще был в порядке, было 32831, максимальное значение, когда он начал действовать, было 33023. Таким образом, оба значения, очевидно, ниже, чем 2^16, так почему же GL_UNSIGNED_SHORT по-прежнему не работает? Я оставлю вопрос открытым еще немного на тот случай, если кто-нибудь ответит на него и закроется через несколько дней / когда я получу ответ. Спасибо!
1 ответ
Вы можете использовать массивы Numpy с плавающими объектами вершинного буфера. Я не использовал их в контексте Pyglet, хотя. Что-то вроде:
from OpenGL.arrays import vbo
# setup
vertices_gl = vbo.VBO(vertPoints)
colours_gl = vbo.VBO(vertPoints)
# in drawing code
vertices.bind()
colours.bind()
glVertexPointer(2, gl.GL_FLOAT, 0, vertices_gl)
glColorPointer(2, gl.GL_FLOAT, 0, vertices_gl)