Ваша обработка смещения кажется мне немного странной? Сначала вы приводите 32-битный тип int в тип указателя. Это будет беспокоить, если вы используете 64-битную ОС. Если вы измените смещение на ptr uint8_t, это устранит одну из проблем (и устранит необходимость в приведении).
const uint8_t* offset = 0;
Еще одна проблема, которую я вижу, заключается в том, что ваши вычисления смещения кажутся несколько запутанными. Вы уверены, что не просто хотели передать 0 для каждого смещения?
// bind an entirely new buffer
buffers[i]->Bind();
/* snip */
// ok, so if buffers[0] contains the vertices. The offset for the next
// buffer will be (numVertices * sizeof(float) * 3) ?
// So then I bind buffers[1] (let's say they contain vertex colours)
// The array itself is (numVertices * sizeof(float) * 3) in size,
// however the offset from the previous iteration is going to point
// past the end of the buffer. That seems wrong to my eyes?
offset += BufferElement::GetSize(element.type) * element.count;
Похоже, что код вычисления смещения предполагает, что все данные будут поступать из одного буфера. Однако вы, кажется, связываете другой буфер для каждого элемента, который, как вы предполагаете, указывает на нулевое смещение для каждого буфера. Что-то подсказывает мне, что вам вообще не нужно вычислять смещение, а вместо этого следует добавить его в качестве переменной var к структуре вашего элемента (т.е. вы будете указывать вручную для каждого элемента - что позволит вам использовать как отдельные буферы, так и несколько элементов хранятся в одном буфере)
Последняя проблема, если element.type отличается от GL_FLOAT или GL_HALF_FLOAT, у вас есть ошибка. Для целочисленных типов вы должны использовать glVertexAttribIPointer, а для GL_DOUBLE вам нужно использовать glVertexAttribLPointer.