Научиться правильно пользоваться VBO - PullRequest
5 голосов
/ 19 октября 2011

Итак, я пытался научить себя использовать VBO, чтобы повысить производительность моего проекта OpenGL и изучить более сложные вещи, чем рендеринг с фиксированными функциями. Но я не нашел много способов достойного урока; лучшие из тех, что я нашел до сих пор, это учебники Сонгхо и материалы на OpenGL.org , но мне кажется, что мне не хватает каких-то базовых знаний, чтобы полностью понять, что происходит, хотя я не могу точно сказать, что именно я не получаю, за исключением использования нескольких параметров.

В любом случае, я забегаю вперед и придумываю какой-то каннибализированный код, который, по крайней мере, не падает, но приводит к странным результатам. То, что я хочу визуализировать, это (визуализация с использованием фиксированной функции; она должна быть коричневой и фоновый серый, но все мои скриншоты OpenGL, кажется, принимают пурпурный как их любимый цвет; возможно, это потому, что я использую SFML для окна?).

Target image

Что я получаю, так это:

Actual image

Я в растерянности. Вот соответствующий код, который я использую, сначала для настройки объектов буфера (я выделяю много памяти согласно рекомендации этого парня для выделения 4-8 МБ):

GLuint WorldBuffer;
GLuint IndexBuffer;

...

glGenBuffers(1, &WorldBuffer);
glBindBuffer(GL_ARRAY_BUFFER, WorldBuffer);
int SizeInBytes = 1024 * 2048;
glBufferData(GL_ARRAY_BUFFER, SizeInBytes, NULL, GL_STATIC_DRAW);

glGenBuffers(1, &IndexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer);
SizeInBytes = 1024 * 2048;
glBufferData(GL_ELEMENT_ARRAY_BUFFER, SizeInBytes, NULL, GL_STATIC_DRAW);

Затем для загрузки данных в буфер. Обратите внимание, что CreateVertexArray () заполняет вектор в переданном местоположении данными вершин, причем каждая вершина вносит 3 поплавка для позиции и 3 поплавка для нормальной (одна из самых запутанных вещей в различных уроках заключалась в том, какой формат я должен сохранить и передать свой фактический данные вершин в (это выглядело как достойное приближение):

std::vector<float>* VertArray = new std::vector<float>;
pWorld->CreateVertexArray(VertArray);
unsigned short Indice = 0;
for (int i = 0; i < VertArray->size(); ++i)
{
    std::cout << (*VertArray)[i] << std::endl;
    glBufferSubData(GL_ARRAY_BUFFER, i * sizeof(float), sizeof(float), &((*VertArray)[i]));
    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, i * sizeof(unsigned short), sizeof(unsigned short), &(Indice));
    ++Indice;
}
delete VertArray;
Indice -= 1;

После этого в игровом цикле я использую этот код:

    glBindBuffer(GL_ARRAY_BUFFER, WorldBuffer);        
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer);    

    glEnableClientState(GL_VERTEX_ARRAY);         
    glVertexPointer(3, GL_FLOAT, 0, 0);
    glNormalPointer(GL_FLOAT, 0, 0);

    glDrawElements(GL_TRIANGLES, Indice, GL_UNSIGNED_SHORT, 0);

    glDisableClientState(GL_VERTEX_ARRAY);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

Я буду совершенно честен - я не уверен, что понимаю, каким должен быть третий параметр glVertexPointer() и glNormalPointer() (шаг - это смещение в байтах, но Songho использует смещение 0 байтов между значениями - что?), или каков последний параметр любого из них. Начальное значение называется 0; но это должен быть указатель. Передача нулевого указателя для получения первой координаты / нормального значения массива кажется странной. Этот парень использует BUFFER_OFFSET(0) и BUFFER_OFFSET(12), но когда я пытаюсь это сделать, мне говорят, что BUFFER_OFFSET () не определено.

Кроме того, последний параметр glDrawElements() должен быть адресом, но опять же, Songho использует адрес 0. Если я использую &IndexBuffer вместо 0, я получаю пустой экран без какой-либо визуализации, кроме фона.

Может ли кто-нибудь просветить меня или, по крайней мере, указать мне в направлении чего-то, что поможет мне понять это? Спасибо!

Ответы [ 3 ]

7 голосов
/ 19 октября 2011

Начальное значение называется 0;но он должен быть указателем.

Имеет значение контекст (не означающий OpenGL).Если одна из функций gl * Pointer вызывается без привязки объекта буфера к GL_ARRAY_BUFFER, то это указатель на адресное пространство клиентского процесса.Если объект буфера привязан к GL_ARRAY_BUFFER, это смещение в текущем связанном объекте буфера (вы можете указать BO, формирующее виртуальное адресное пространство, на которое параметр gl * Pointer затем становится указателем на это адресное пространство на стороне сервера).

Теперь давайте посмотрим на ваш код

std::vector<float>* VertArray = new std::vector<float>;

Вы не должны смешивать контейнеры STL и new, узнать о шаблоне RAII.

pWorld->CreateVertexArray(VertArray);

Это проблематично, так как вы позже удалите VertexArray, оставив вас с висящим указателем.Не хорошо.

unsigned short Indice = 0;
for (int i = 0; i < VertArray->size(); ++i)
{
    std::cout << (*VertArray)[i] << std::endl;
    glBufferSubData(GL_ARRAY_BUFFER, i * sizeof(float), sizeof(float), &((*VertArray)[i]));

Вы должны отправлять большие пакеты данных с glBufferSubData, а не отдельными точками данных.

    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, i * sizeof(unsigned short), sizeof(unsigned short), &(Indice));

Вы передаете просто увеличивающиеся индексы в GL_ELEMENT_ARRAY_BUFFER, перечисляя таким образомвершины.Зачем?Вы можете получить это без дополнительной работы с использованием glDrawArrays вместо glDrawElements.

    ++Indice;
}
delete VertArray;

Вы удаляете VertArray, сохраняя при этом висячий указатель.

Indice -= 1;

Почему вы просто не использовали счетчик циклов i?

Так как это исправить?Вот так:

std::vector<float> VertexArray;
pWorld->LoadVertexArray(VertexArray); // World::LoadVertexArray(std::vector<float> &);

glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*VertexArray->size(), &VertexArray[0] );

и использование glDrawArrays;конечно, если вы не перечисляете вершины, но у вас есть список граней → индексы вершин, использование glDrawElements является обязательным.

4 голосов
/ 19 октября 2011

Не вызывайте glBufferSubData для каждой вершины.Это упускает из виду VBO.Вы должны создать большой буфер ваших данных вершин, а затем передать его OpenGL за один раз.

Чтение http://www.opengl.org/sdk/docs/man/xhtml/glVertexPointer.xml

При использовании VBO эти указатели относятся к данным VBO.Поэтому обычно это 0 или небольшое значение смещения.

stride = 0 означает, что данные плотно упакованы, и OpenGL может вычислить stride из других параметров.

Я обычно использую VBO следующим образом:

struct Vertex
{
    vec3f position;
    vec3f normal;
};

Vertex[size] data;

...

glBufferData(GL_ARRAY_BUFFER, size*sizeof(Vertex), data, GL_STATIC_DRAW);

...

glVertexPointer(3,GL_FLOAT,sizeof(Vertex),offsetof(Vertex,position));
glNormalPointer(3,GL_FLOAT,sizeof(Vertex),offsetof(Vertex,normal));

Просто передайте один фрагмент данных вершины.А затем используйте gl*Pointer, чтобы описать, как данные упакованы, используя макрос offsetof .

0 голосов
/ 20 октября 2011

Чтобы узнать о смещении последнего параметра, просто посмотрите на этот пост ... Что такое параметр "offset" в GLES20.glVertexAttribPointer / glDrawElements и откуда взялись ptr / indexes?

...