Как остановить рендеринг невидимых лиц

Я делаю игру на основе вокселей, и для ее нужд я создаю движок блочного рендеринга.

Дело в том, что мне нужно сгенерировать много кубов. Каждый раз, когда я рендерю более 16x16x16 кусков этих блоков, мой FPS почти не падает, потому что он рендерит все 6 граней всех этих кубов. Это 24 576 квадов, а я этого не хочу.

Итак, мой вопрос: как остановить рендеринг вершин (или четырехугольников), которые не видны, и, следовательно, повысить производительность моей игры?

Вот класс для рендеринга блока:

public void renderBlock(int posx, int posy, int posz) {
  try{
    //t.bind();
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);// or even GL_FRONT_AND_BACK */);

    glPushMatrix();

    GL11.glTranslatef((2*posx+0.5f),(2*posy+0.5f),(2*posz+0.5f));             // Move Right 1.5 Units And Into The Screen 6.0
    GL11.glRotatef(rquad,1.0f,1.0f,1.0f);

    glBegin(GL_QUADS);               // Draw A Quad

    GL11.glColor3f(0.5f, 0.4f, 0.4f);             // Set The Color To Green
    GL11.glTexCoord2f(0,0);
    GL11.glVertex3f( 1f, 1f,-1f);         // Top Right Of The Quad (Top)
    GL11.glTexCoord2f(1,0);
    GL11.glVertex3f(-1f, 1f,-1f);         // Top Left Of The Quad (Top)
    GL11.glTexCoord2f(1,1);
    GL11.glVertex3f(-1f, 1f, 1f);         // Bottom Left Of The Quad (Top)
    GL11.glTexCoord2f(0,1);
    GL11.glVertex3f( 1f, 1f, 1f);         // Bottom Right Of The Quad (Top)

    //GL11.glColor3f(1.2f,0.5f,0.9f);             // Set The Color To Orange
    GL11.glTexCoord2f(0,0);
    GL11.glVertex3f( 1f,-1f, 1f);         // Top Right Of The Quad (Bottom)
    GL11.glTexCoord2f(0,1);
    GL11.glVertex3f(-1f,-1f, 1f);         // Top Left Of The Quad (Bottom)
    GL11.glTexCoord2f(1,1);
    GL11.glVertex3f(-1f,-1f,-1f);         // Bottom Left Of The Quad (Bottom)
    GL11.glTexCoord2f(1,0);
    GL11.glVertex3f( 1f,-1f,-1f);         // Bottom Right Of The Quad (Bottom)

    //GL11.glColor3f(1.0f,0.0f,0.0f);             // Set The Color To Red
    GL11.glTexCoord2f(0,0);
    GL11.glVertex3f( 1f, 1f, 1f);         // Top Right Of The Quad (Front)
    GL11.glTexCoord2f(1,0);
    GL11.glVertex3f(-1f, 1f, 1f);         // Top Left Of The Quad (Front)
    GL11.glTexCoord2f(1,1);
    GL11.glVertex3f(-1f,-1f, 1f);         // Bottom Left Of The Quad (Front)
    GL11.glTexCoord2f(0,1);
    GL11.glVertex3f( 1f,-1f, 1f);         // Bottom Right Of The Quad (Front)

    //GL11.glColor3f(1f,0.5f,0.0f);             // Set The Color To Yellow
    GL11.glTexCoord2f(0,0);
    GL11.glVertex3f( 1f,-1f,-1f);         // Bottom Left Of The Quad (Back)
    GL11.glTexCoord2f(1,0);
    GL11.glVertex3f(-1f,-1f,-1f);         // Bottom Right Of The Quad (Back)
    GL11.glTexCoord2f(1,1);
    GL11.glVertex3f(-1f, 1f,-1f);         // Top Right Of The Quad (Back)
    GL11.glTexCoord2f(0,1);
    GL11.glVertex3f( 1f, 1f,-1f);         // Top Left Of The Quad (Back)

    //GL11.glColor3f(0.0f,0.0f,0.3f);             // Set The Color To Blue
    GL11.glTexCoord2f(0,1);
    GL11.glVertex3f(-1f, 1f, 1f);         // Top Right Of The Quad (Left)
    GL11.glTexCoord2f(1,1);
    GL11.glVertex3f(-1f, 1f,-1f);         // Top Left Of The Quad (Left)
    GL11.glTexCoord2f(1,0);
    GL11.glVertex3f(-1f,-1f,-1f);         // Bottom Left Of The Quad (Left)
    GL11.glTexCoord2f(0,0);
    GL11.glVertex3f(-1f,-1f, 1f);         // Bottom Right Of The Quad (Left)

    //GL11.glColor3f(0.5f,0.0f,0.5f);             // Set The Color To Violet
    GL11.glTexCoord2f(0,0);
    GL11.glVertex3f( 1f, 1f,-1f);         // Top Right Of The Quad (Right)
    GL11.glTexCoord2f(1,0);
    GL11.glVertex3f( 1f, 1f, 1f);         // Top Left Of The Quad (Right)
    GL11.glTexCoord2f(1,1);
    GL11.glVertex3f( 1f,-1f, 1f);         // Bottom Left Of The Quad (Right)
    GL11.glTexCoord2f(0,1);
    GL11.glVertex3f( 1f,-1f,-1f);         // Bottom Right Of The Quad (Right)

    //rquad+=0.0001f;
    glEnd();
    glPopMatrix();
  }catch(NullPointerException t){t.printStackTrace(); System.out.println("rendering block failed");}
}

Вот код, который их отображает:

private void render() {
  GL11.glClear(GL11.GL_COLOR_BUFFER_BIT|GL11.GL_DEPTH_BUFFER_BIT);
  for(int y=0; y<32; y++){
    for(int x=0; x<16; x++){
      for(int z=0; z<16; z++) {
        b.renderBlock(x, y, z);

      }
    }
  }
}

person themorfeus    schedule 06.04.2012    source источник
comment
возможный дубликат Как это сделать удаление лица в юнит-кубическом мире а-ля Майнкрафт?   -  person Nicol Bolas    schedule 06.04.2012


Ответы (3)


Я рекомендую, как сказал ulmangt, использовать VBO, но перед этим нужно вычислить только видимые лица.

Это можно легко сделать, проверив (только один раз в начале), является ли лицо соседним с пустым вокселем («воздухом»). Если это так, добавьте этот четырехугольник (лицо) к рендерингу.

После этого вы просто делаете эту проверку только на соседях измененных вокселей. Пример: когда пользователь удаляет куб, проверьте 6 соседей этого вокселя и добавьте эти четырехугольники в визуализацию. Сделайте обратное при добавлении вокселей, удалите соседние квадраты.

Таким образом, с кубом 5x5x5, состоящим из вокселей, вместо 750 четырехугольников вы получите 150.

Другие преимущества можно получить, просто визуализируя фрагменты (группы вокселей) в поле зрения (игнорируя те, что находятся позади игрока) и используя ограничение расстояния.

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

person Diogo Gomes    schedule 09.04.2012
comment
Спасибо :) Теперь у вас есть идея, как определить, касается ли воксель вокселя? - person themorfeus; 12.04.2012
comment
Что ж, фрагмент вокселей будет просто 3D-массивом, заполненным типом вокселей (я думаю, что Minecraft использовал байт для представления типа вокселя в версиях для разработчиков, сейчас понятия не имею). Самый простой способ, который приходит на ум, — просто перебрать весь массив, проверить соседей каждого вокселя и добавить видимые четырехугольники в список. (TL;DR: итерация по трехмерному массиву вокселей, игнорирование вокселей воздушного типа, проверка соседей на предмет воздушных вокселей, добавление видимых четырехугольников.) - person Diogo Gomes; 13.04.2012

Ваш код имеет большую проблему с производительностью. Вы не должны использовать рендеринг OpenGL в немедленном режиме (glVertexXXX() calls) для отрисовки такого большого количества вершин.

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

Вместо этого следует использовать объекты буфера вершин. Это позволит вам загрузить всю вашу геометрию прямо на видеокарту, а затем нарисовать все ваши кубы одним вызовом метода Java (вероятно, glDrawArrays).

person ulmangt    schedule 06.04.2012
comment
Это просто исправление меньшей проблемы. Проблема здесь в том, что он пытается выполнить рендеринг вокселей методом грубой силы, который не имеет реальных шансов на успех. По крайней мере, не для любого размера ввода, который имеет значение. - person pmr; 06.04.2012

Хорошая идея - НЕ использовать немедленный режим для рендеринга ваших блоков, я использую списки отображения, потому что их проще всего настроить и они работают ОЧЕНЬ быстро. Во-вторых, даже если вы все еще используете только немедленный режим, используйте только один вызов glBegin/glEnd при рисовании. Используйте атлас текстур для текстур в будущем и на ваш главный вопрос, как остановить рендеринг невидимых лиц, это довольно просто. Как я это делаю, в основном делаю шесть методов для каждого лица, которые возвращают логическое значение. Вы бы просто вернули, если тип блока в направлении этой грани является воздушным блоком, если это так. Тогда это означает, что он вернет true, поэтому сделайте это. И в вашем методе рисования добавьте параметры "boolean backface, boolean topface... и т. д." и оператор if, проверяющий, какую сторону рисовать.

надеюсь помог, удачи!

person ABOODYFJ    schedule 26.08.2014
comment
этому вопросу более 2 лет, и я уже решил проблему, используя списки отображения. Хотя я уже совсем отошел от этого проекта. - person themorfeus; 27.08.2014
comment
Я не читал дату, тем не менее, это поможет людям, которые ищут ответы. - person ABOODYFJ; 31.08.2014