Динамическая оптимизация отображения плитки в OpenGL - PullRequest
1 голос
/ 15 июня 2011

Я работаю над основанной на плитке 2D-игрой сверху вниз с динамически генерируемым ландшафтом и начал (пере) переписывать графический движок в OpenGL.Игра написана на Java с использованием LWJGL, и я бы предпочел, чтобы она оставалась относительно независимой от платформы и играбельной на старых компьютерах.

В настоящее время я использую немедленный режим рисования, но, очевидно, это слишкоммедленно для всего, кроме самых простых сцен.

Существует два основных типа объектов, которые нарисованы: плитки, которые являются миром, и спрайты, которые в значительной степени все остальное (сущности, предметы, эффекты, т. д.).

Плитки имеют размер 20 * 20 пикселей и хранятся в виде кусков (40 * 40 листов).Генерация ландшафта выполняется в полных чанках, как в Minecraft.
Метод, который я сейчас использую, заключается в переборе 9 чанков рядом с игроком, а затем итерации по каждой плитке внутри, отрисовке одного квада для текстуры плитки и дополнительных дополнительных квадовдля функций в зависимости от материала.Это заканчивается довольно медленно, но простая проверка вне поля зрения дает увеличение FPS в 5-10 раз.

Для оптимизации этого я рассмотрел использование VBO и четырехслойных полос, но у меня проблема при изменении ландшафта.Это происходит не каждый кадр, но не очень редкое событие.Простым методом будет удаление и перестройка VBO блока каждый раз, когда он изменяется.Это не кажется лучшим способом, хотя.Я читал, что VBO могут быть «динамическими», позволяя изменять их содержимое.Как это можно сделать и какие данные можно эффективно изменить внутри них?Есть ли другие способы эффективного рисования мира?

Другой тип, спрайты, в настоящее время рисуется с помощью четырехугольника с текстурой, сопоставленной со спрайтовым листом.Таким образом, меняя координаты текстур, я могу даже анимировать их позже.Правда ли, что это правильный способ сделать анимацию?
В настоящее время даже очень большое количество спрайтов не сильно замедлит игру, и, понимая VBO, я смогу ускорить их еще больше, но яне видел никаких надежных и надежных учебников для эффективного способа сделать это.Кто-нибудь знает что-нибудь?

Спасибо за помощь!

1 Ответ

7 голосов
/ 15 июня 2011

В настоящее время я использую режим непосредственного рисования, но, очевидно, он слишком медленный для всего, кроме самых простых сцен.

Я не согласен.Если вы не рисуете лот плиток (десятки тысяч на кадр), немедленный режим должен подойти вам.

Ключ - это то, что вам нужно будет сделать, чтобы стать хорошимпроизводительность в любом случае: текстурные атласы.Все ваши плитки должны храниться в одной текстуре.Вы используете координаты текстуры, чтобы вытягивать различные плитки из этой текстуры при рендеринге.Так что если ваш рендеринг выглядит следующим образом:

for(tile in tileList) //Pseudocode. Not actual C++
{
    glBindTexture(GL_TEXTURE_2D, tile.texture);
    glBegin(GL_QUADS);
        glTexCoord2f(0.0f, 0.0f);
        glVertex2fv(tile.lowerLeft);
        glTexCoord2f(0.0f, 1.0f);
        glVertex2fv(tile.upperLeft);
        glTexCoord2f(1.0f, 1.0f);
        glVertex2fv(tile.upperRight);
        glTexCoord2f(1.0f, 0.0f);
        glVertex2fv(tile.lowerRight);
    glEnd();
}

Вы можете преобразовать его в это:

glBindTexture(GL_TEXTURE_2D, allTilesTexture);
glBegin(GL_QUADS);
for(tile in tileList) //Still pseudocode.
{
    glTexCoord2f(tile.texCoord.lowerLeft);
    glVertex2fv(tile.lowerLeft);
    glTexCoord2f(tile.texCoord.upperLeft);
    glVertex2fv(tile.upperLeft);
    glTexCoord2f(tile.texCoord.upperRight);
    glVertex2fv(tile.upperRight);
    glTexCoord2f(tile.texCoord.lowerRight);
    glVertex2fv(tile.lowerRight);
}
glEnd();

Если вы уже используете атлас текстуры и все еще не используетеполучив приемлемую производительность, вы можете перейти к буферизации объектов и тому подобное.Но вы не получите большей производительности от буферных объектов, если не сделаете это в первую очередь.

Если все ваши плитки не могут уместиться в одну текстуру, то вам нужно будет сделать одну из двух вещей:используйте несколько текстур (рендеринг как можно большего количества плиток с каждой текстурой в одной паре glBegin / glEnd) или используйте массив текстур.Текстурные массивы доступны только на оборудовании уровня OpenGL 3.0.Это означает, что любой Radeon HDxxxx или GeForce 8xxxx или лучше.

Вы упомянули, что иногда вы визуализируете «элементы» поверх плиток.Эти функции, вероятно, используют смешивание и режимы glTexEnv, отличающиеся от обычных плиток.В этом случае вам нужно найти способы сгруппировать аналогичные функции в одну пару glBegin / glEnd.

Как вы уже поняли, ключом к производительности является минимизация количества вызовов glBindTexture и glBegin./ glEnd.Делайте как можно больше работы в каждом glBegin / glEnd.

Если вы хотите продолжить использование подхода, основанного на буфере (и вам следует беспокоиться, только если подход с текстурным атласом не повысил вашу производительность)это довольно просто.Поместите все свои куски плитки в один буферный объект.Не делайте буфер для каждого;для этого нет никакой реальной причины, а данные вершин размером 40x40 имеют размер только 12 800 байт.Вы можете поместить 81 такой блок в один буфер размером 1 МБ.Таким образом, вам нужно только вызвать glBindBuffer для вашей местности.Что, опять же, экономит вашу производительность.

Мне нужно было бы узнать больше об этих «функциях», которые вы иногда используете, чтобы предложить способ их оптимизации.Но что касается динамических буферов, я бы не волновался.Просто используйте glBufferSubData, чтобы обновить часть рассматриваемого буфера.Если это оказывается медленным, есть несколько вариантов сделать это быстрее, что вы можете использовать.Но вам не стоит беспокоиться, если вы не знаете , что это необходимо, поскольку они сложные.

Спрайты, вероятно, приносят абсолютную минимум из буфераобъектный подход.Там действительно ничего не может быть получено по немедленному режиму.Даже если вы рендерите сотни из них, у каждого будет своя собственная матрица преобразования.Это означает, что каждый из них должен быть отдельным вызовом розыгрыша.Так что это может быть glBegin / glEnd.

...