OpenGL: рендеринг тысяч кубов с массивами вершин работает не слишком хорошо

Я пытаюсь использовать массивы вершин для рендеринга около 2097152 кубов с LWJGL (нет, не все сразу). Я реализовал множество типов отбраковки полигонов, чтобы повысить свою производительность примерно с 2 кадров в секунду до примерно 60 кадров в секунду. На протяжении всего этого проекта я работал с рендерингом Immediate Mode, и я думаю, что пришло время для обновления. И здесь на помощь приходят массивы вершин.

Я не хочу использовать VBO, поэтому пока экспериментирую с VAO. Кажется, я не могу найти практичный (или эффективный) метод рендеринга. К сожалению, все, что я пробую, дает мне худший FPS, чем Immediate Mode. В каждом кадре я загружаю FloatBuffer для каждого куба с видимыми полигонами, а затем рисую их, используя обычные методы массива вершин. Эта настройка вызывает у меня головную боль, потому что я получаю меньше FPS, чем при использовании Immediate Mode и без отбраковки полигонов.

Я думаю, что делаю что-то не так. Итак, среди всех вас, ярких, амбициозных программистов OpenGL/LWJGL, кто-нибудь знает, как это можно сделать более эффективным и действенным способом?

Вот мой код для рендеринга (усеченный, чтобы не было слишком много беспорядка):

for(int z = 0; z < chunk.bpc; z++) {
for(int y = 0; y < chunk.bpc; y++) {
    for(int x = 0; x < chunk.bpc; x++) {
        if(((z == chunk.bpc - 1 || z == 0) || (y == chunk.bpc - 1 || y == 0) || (x == chunk.bpc - 1 || x == 0)) 
            && chunk.data[(x * chunk.bpc + z) * chunk.bpc + y] == i) {

                List<Float> vertices = new ArrayList<Float>();

                float xp = x + locX, yp = y + locY, zp = z + locZ;

            if(z == chunk.bpc - 1 && chunk.z$ == null) {
                vertices.add(xp); vertices.add(yp); vertices.add(zp + size);
                vertices.add(xp + size); vertices.add(yp); vertices.add(zp + size);
                vertices.add(xp + size); vertices.add(yp + size); vertices.add(zp + size);
                vertices.add(xp); vertices.add(yp + size); vertices.add(zp + size);
            }

            if(z == 0 && chunk.z_ == null) {
                vertices.add(xp); vertices.add(yp); vertices.add(zp);
                vertices.add(xp); vertices.add(yp + size); vertices.add(zp);
                vertices.add(xp + size); vertices.add(yp + size); vertices.add(zp);
                vertices.add(xp + size); vertices.add(yp); vertices.add(zp);
            }

            if(y == chunk.bpc - 1 && chunk.y$ == null) {
                vertices.add(xp); vertices.add(yp + size); vertices.add(zp);
                vertices.add(xp); vertices.add(yp + size); vertices.add(zp + size);
                vertices.add(xp + size); vertices.add(yp + size); vertices.add(zp + size);
                vertices.add(xp + size); vertices.add(yp + size); vertices.add(zp);
            }

            if(y == 0 && chunk.y_ == null) {
                vertices.add(xp); vertices.add(yp); vertices.add(zp);
                vertices.add(xp + size); vertices.add(yp); vertices.add(zp);
                vertices.add(xp + size); vertices.add(yp); vertices.add(zp + size);
                vertices.add(xp); vertices.add(yp); vertices.add(zp + size);
            }

            if(x == chunk.bpc - 1 && chunk.x$ == null) {
                vertices.add(xp + size); vertices.add(yp); vertices.add(zp);
                vertices.add(xp + size); vertices.add(yp + size); vertices.add(zp);
                vertices.add(xp + size); vertices.add(yp + size); vertices.add(zp + size);
                vertices.add(xp + size); vertices.add(yp); vertices.add(zp + size);
            }

            if(x == 0 && chunk.x_ == null) {
                vertices.add(xp); vertices.add(yp); vertices.add(zp);
                vertices.add(xp); vertices.add(yp); vertices.add(zp + size);
                vertices.add(xp); vertices.add(yp + size); vertices.add(zp + size);
                vertices.add(xp); vertices.add(yp + size); vertices.add(zp);
            }

            float[] verts = new float[vertices.size()];
            for(int a = 0; a < verts.length; a++) {
                verts[a] = vertices.get(a);
            }

            FloatBuffer cubeBuffer = BufferUtils.createFloatBuffer(verts.length);
            cubeBuffer.put(verts);
            cubeBuffer.flip();

            GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);

            GL11.glVertexPointer(3, 0, cubeBuffer);

            GL11.glDrawArrays(GL11.GL_QUADS, 0, verts.length / 3);

            GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY);
        }
    }
}
}

(Просто игнорируйте некоторые из этих переменных, они просто отбирают разные полигоны)

Поэтому я не знаю, есть ли более эффективный способ сделать это, но если есть, было бы неплохо, если бы я мог получить несколько указателей. Заранее спасибо! О, и...

ЕСТЬ ЧТО-ТО ОЧЕНЬ НЕПРАВИЛЬНО В ТОМ, КАК Я ВИЗУАЛИЗИРУЮ.


person CoderTheTyler    schedule 27.01.2013    source источник


Ответы (2)


Хотя я читал о LWJGL и VAO, я никогда не использовал их лично, VBO всегда помогали мне. Но если я посмотрю на ваш фрагмент кода, вы, кажется, вызываете этот фрагмент каждый кадр. Итак, по сути, вы меняете данные в каждом кадре, создаете новый буфер, передаете данные из в буфер, а затем визуализируете данные в буфере. Создание нового буфера для каждого кадра дорого, поэтому сделайте это один раз, а затем повторно используйте свой буфер. И если вы меняете данные в каждом кадре, то VAO и VBO, вероятно, не дадут вам большей производительности, чем немедленный режим. Причина кроется в том, что в непосредственном режиме вы каждый кадр передаете данные в память GPU и рендерите их, такая передача стоит дорого. С другой стороны, если данные не изменяются в каждом кадре, то VAO и VBO (и более ранние списки отображения) дают вам ускорение, позволяя хранить данные в памяти графического процессора, поэтому вам не нужно каждый раз передавать их из ОЗУ. по шине PCI-E в память графического процессора.

person Bart    schedule 27.01.2013
comment
Могу ли я иметь один буфер, в котором хранятся данные для всех вершин, вместо создания нового буфера для каждого куба? - person CoderTheTyler; 27.01.2013
comment
да. Вы должны положить намного больше кубов в буфер. Существует определенный оптимум того, сколько вершин вы должны хранить в буфере, но вы можете хранить в нем миллионы вершин, что намного больше, чем горстка вершин, которые вы храните в нем сейчас. - person Bart; 28.01.2013
comment
Можете ли вы указать мне где-нибудь учебник или пример того, как заставить это работать? Я уже пытался добавить гораздо больше вершин, но все равно ужасно потерпел неудачу, так как отрисовывал только один куб. - person CoderTheTyler; 28.01.2013
comment
Я ничего не знаю наизусть, но попробуйте поискать в Интернете, это довольно простой материал OpenGL, поэтому я предполагаю, что в Интернете есть несколько примеров / руководств. Выберите тот, который соответствует вашему уровню знаний. Вики OpenGL о буферных объектах и ​​спецификации вершин также может быть вам полезна: opengl.org/wiki/Buffer_Object и opengl.org/wiki/Vertex_Specification - person Bart; 28.01.2013

Не регенерируйте свою геометрию каждый кадр. Динамическое выделение памяти во внутреннем цикле рендеринга, как правило, плохая идея.

Генерируйте геометрию для фрагмента один раз, когда фрагмент появляется в поле зрения и если/когда этот фрагмент изменяется.

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

person genpfault    schedule 27.01.2013
comment
Я думаю, что мой главный вопрос сейчас заключается в том, могу ли я загрузить все свои вершины для всей моей геометрии в один буфер, а затем визуализировать из него вместо нового буфера для каждого отдельного куба...? - person CoderTheTyler; 27.01.2013
comment
@MrDoctorProfessorTyler: Вы действительно спрашиваете нас, как переместить ваш код из нашего цикла рендеринга в некоторый код инициализации? - person Nicol Bolas; 28.01.2013
comment
Я совершенно новичок в этом, так что да. Или, по крайней мере, укажите мне правильное направление - person CoderTheTyler; 28.01.2013