#objective-c #ios #opengl-es #rendering #vbo
#objective-c #iOS #opengl-es #визуализация #vbo
Вопрос:
Недавно я изменил свое приложение с использования OpenGL ES 1.1 на 2.0, и в процессе я решил дополнительно оптимизировать свой код, используя VBO. Я думаю, что я разобрался с основными отличиями с ES 2.0, и у меня есть результаты, показывающие, что я все делаю правильно без VBO, однако я был поставлен в тупик проблемой, которая возникает только при попытке использовать VBO.
Я использую пользовательскую структуру, содержащую положение вершины, цвет и размер (плюс небольшое дополнение для выравнивания данных), а затем чередую эти данные вершины в VBO. Эти данные используются для визуализации точечных спрайтов. Проблема в том, что я получаю EXC_BAD_ACCESS при вызове glDrawArrays(). Обратная трассировка при этой ошибке выглядит следующим образом:
#0 0x31fc7358 in gleRunVertexSubmitARM ()
#1 0x31fc87b2 in gleLLVMArrayFunc ()
#2 0x31fc872a in gleSetVertexArrayFunc ()
#3 0x31fbefcc in gleDrawArraysOrElements_ExecCore ()
#4 0x31fc4608 in glDrawArrays_IMM_Exec ()
#5 0x36ddaee2 in glDrawArrays ()
#6 0x0001cab8 in -[Renderer renderDrawingVertexBuffer:withSize:usingTexture:] (self=0x1783b0, _cmd=0x50fec, vboID=0, size=3584, drawTexture=5) at /Users/Stu/Documents/...
#7 0x000202f4 in -[EAGLView drawSmoothCurveFromPoints:endOfLine:] (self=0x195be0, _cmd=0x50d34, points=0x1956f0, endOfLine=1 '01') at /Users/Stu/Documents/...
#8 0x0001e50c in -[EAGLView drawView:] (self=0x195be0, _cmd=0x50d8a, sender=0x0) at /Users/Stu/Documents/...
#9 0x0001e174 in -[EAGLView layoutSubviews] (self=0x195be0, _cmd=0x3297d4ac) at /Users/Stu/Documents/...
#10 0x326805fa in -[UIView(CALayerDelegate) layoutSublayersOfLayer:] ()
#11 0x35efbf02 in -[NSObject(NSObject) performSelector:withObject:] ()
#12 0x333fbbb4 in -[CALayer layoutSublayers] ()
#13 0x333fb96c in CALayerLayoutIfNeeded ()
Это структура для хранения данных вершин:
typedef struct {
ISVertex2D vertex; // Made up of 2 GLfloats.
ISColor color; // Made up of 4 GLfloats.
GLfloat size;
GLfloat padding;
} ISPointSpriteData;
После того, как набор данных сохраняется в массиве этих структур, данные отправляются в VBO и визуализируются с помощью glDrawArrays():
[renderer prepareScene];
[renderer renderBackground];
glBindBuffer(GL_ARRAY_BUFFER, penVbo);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(ISPointSpriteData) * penVboSize, sizeof(ISPointSpriteData) * vertexCount, amp;vertexBuffer[0].vertex);
glBindTexture(GL_TEXTURE_2D, drawTexture);
glEnableVertexAttribArray(ATTRIB_VERTEX);
glEnableVertexAttribArray(ATTRIB_COLOR);
glEnableVertexAttribArray(ATTRIB_SIZE);
glDisableVertexAttribArray(ATTRIB_TEXTURE_COORD);
glUniform1i(uniforms[UNIFORM_IS_SPRITE], GL_TRUE);
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(ISPointSpriteData), (void*)offsetof(ISPointSpriteData, vertex));
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(ISPointSpriteData), (void*)offsetof(ISPointSpriteData, color));
glVertexAttribPointer(ATTRIB_SIZE, 1, GL_FLOAT, GL_FALSE, sizeof(ISPointSpriteData), (void*)offsetof(ISPointSpriteData, size));
glDrawArrays(GL_POINTS, 0, vertexCount); // <--- EXC_BAD_ACCESS
glDisableVertexAttribArray(ATTRIB_VERTEX);
glDisableVertexAttribArray(ATTRIB_COLOR);
glDisableVertexAttribArray(ATTRIB_SIZE);
[renderer presentScene];
Этот код сработал прямо перед тем, как я вставил VBO, с единственным изменением, заключающимся в том, что последний параметр вызовов glVertexAttribPointer (), очевидно, будет указывать на данные, а не на смещение. Что я упускаю? Я не думаю, что этот код особенно сложный, поэтому я могу только думать, что я где-то допустил глупую ошибку.
Спасибо
Ответ №1:
Проблема решена:
В моем коде было несколько ошибок, не все из которых были бы очевидны из списков в вопросе. Я изложу основные проблемы:
- В коде, не указанном в списке, я создавал VBO в служебной функции. В этой функции я неправильно передавал
GLuint
дескриптор буфера (я пытался передать его по ссылке в функции, которая возвращает void). В интересах сделать это более читаемым, я изменил функцию, чтобы просто инициализировать ее собственный дескриптор и вернуть его. Это остановило сбой программы вglDrawArrays()
строке (предположительно, неинициализированное значение, должно быть, уже было присвоено другому буферу где-то).
Возникла новая проблема — экран отрисовывался черным, и были БОЛЬШИЕ задержки между вызовами draw. Это было вызвано…
- Не отменяется привязка VBO после каждого вызова draw. Для каждого кадра сначала отрисовывается фоновая текстура, за которой следует содержимое VBO. Оставляя VBO привязанными к следующему фоновому рендерингу, я, очевидно, вызывал у OGL небольшие проблемы! Это было не только причиной черного экрана, но и большими задержками между вызовами draw.
Надеюсь, это поможет кому-нибудь еще в подобной ситуации!
Ответ №2:
Возможно, вам придется использовать glDrawElements вместо glDrawArrays. Потому что glDrawArrays не разрешает переключение или пропуск. Он считывает данные из непрерывного набора вершин.
«glDrawArrays() считывает данные вершин из включенных массивов, проходя прямо по массиву без пропусков или скачкообразных изменений. Поскольку glDrawArrays () не позволяет перемещаться по массивам вершин, вам все равно придется повторять общие вершины один раз для каждой грани.»
Комментарии:
1. Мне не нужно перемещать данные. Я рендерю точечные спрайты, и поэтому все, что мне нужно, это чтобы glDrawArrays () просматривал мои смежные данные вершин.
2. Более того, я должен добавить, что я успешно адаптировал примерное приложение GLES2Sample от Apple для использования VBO почти таким же образом, как я пытаюсь здесь.
3. Я полагаю, что у вас есть данные вершины в формате структуры. Это означает, что данные вершин не присутствуют последовательно в памяти. Данные каждой вершины присутствуют с пробелом sizeof (ISPointSpriteData). Вы пробовали glDrawElements?
4. Я не совсем уверен, что вы имеете в виду, извините. Данные чередуются, и все данные в структуре используются в шейдерах. Таким образом, существует разрыв в sizeof (ISPointSpriteData) между началом каждого набора данных вершин, но все промежуточные данные требуются и считываются glDrawArrays() Я думал. Извините, я не очень хорошо разбираюсь в памяти на действительно низком уровне. Кстати, я попробовал glDrawElements, и это не повлияло на мою проблему.
5. Сегодня я много играл с вещами, и я решил проблему. Среди прочего, я не отключал VBO после вызова draw. Я опубликовал полное объяснение в отдельном ответе. Тем не менее, большое спасибо за помощь.