Почему мои VBO медленнее, чем списки отображения?

Я создал два простых воксельных движка, буквально просто куски, которые содержат кубы. Во-первых, я использую списки отображения и могу без проблем воспроизводить сотни фрагментов со скоростью 60 кадров в секунду, несмотря на то, что технологии, стоящей за ней, уже много лет, и она устарела. В моей версии VBO я пытаюсь отрендерить 27 чанков, и я внезапно падаю ниже 50 FPS. Что дает? Я использую шейдеры для моей версии VBO, но не для первого списка отображения. Без шейдеров для версии VBO я все равно получаю ту же частоту кадров в секунду. Я выложу соответствующий код:

ВБО

Инициализация чанка:

public void initGL() {
    rand = new Random();

    sizeX = (int) pos.getX() + CHUNKSIZE;
    sizeY = (int) pos.getY() + CHUNKSIZE;
    sizeZ = (int) pos.getZ() + CHUNKSIZE;

    tiles = new byte[sizeX][sizeY][sizeZ];

    vCoords = BufferUtils.createFloatBuffer(CHUNKSIZE * CHUNKSIZE * CHUNKSIZE * (3 * 4 * 6));
    cCoords = BufferUtils.createFloatBuffer(CHUNKSIZE * CHUNKSIZE * CHUNKSIZE * (4 * 4 * 6));

    createChunk();

    verticeCount = CHUNKSIZE * CHUNKSIZE * CHUNKSIZE * (4 * 4 * 6);

    vCoords.flip();
    cCoords.flip();

    vID = glGenBuffers();
    glBindBuffer(GL_ARRAY_BUFFER, vID);
    glBufferData(GL_ARRAY_BUFFER, vCoords, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    cID = glGenBuffers();
    glBindBuffer(GL_ARRAY_BUFFER, cID);
    glBufferData(GL_ARRAY_BUFFER, cCoords, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}
private void createChunk() {
    for (int x = (int) pos.getX(); x < sizeX; x++) {
        for (int y = (int) pos.getY(); y < sizeY; y++) {
            for (int z = (int) pos.getZ(); z < sizeZ; z++) {
                if (rand.nextBoolean() == true) {
                    tiles[x][y][z] = Tile.Grass.getId();
                } else {
                    tiles[x][y][z] = Tile.Void.getId();
                }
                vCoords.put(Shape.createCubeVertices(x, y, z, 1));
                cCoords.put(Shape.getCubeColors(tiles[x][y][z]));
            }
        }
    }
}

И затем рендеринг:

public void render() {
    glBindBuffer(GL_ARRAY_BUFFER, vID);
    glVertexPointer(3, GL_FLOAT, 0, 0L);

    glBindBuffer(GL_ARRAY_BUFFER, cID);
    glColorPointer(4, GL_FLOAT, 0, 0L);

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    shader.use();
    glDrawArrays(GL_QUADS, 0, verticeCount);
    shader.release();

    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
}

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

Показать список

Инициализация:

public void init() {
    rand = new Random();

    opaqueID = glGenLists(1);

    tiles = new byte[(int) lPosition.x][(int) lPosition.y][(int) lPosition.z];

    genRandomWorld();
    rebuild();
}
public void rebuild() {
    glNewList(opaqueID, GL_COMPILE);
    glBegin(GL_QUADS);
    for (int x = (int) sPosition.x; x < (int) lPosition.x; x++) {
        for (int y = (int) sPosition.y; y < (int) lPosition.y; y++) {
            for (int z = (int) sPosition.z; z < (int) lPosition.z; z++) {
                if (checkCubeHidden(x, y, z)) {
                    // check if tiles hidden. if not, add vertices to
                    // display list
                    if (type != 0) {
                        Tile.getTile(tiles[x][y][z]).getVertices(x, y, z, 1, spritesheet.getTextureCoordsX(tiles[x][y][z]), spritesheet.getTextureCoordsY(tiles[x][y][z]));
                    } else {
                        Tile.getTile(tiles[x][y][z]).getVertices(x, y, z, 1);
                    }
                }
            }
        }
    }
    glEnd();
    glEndList();
    spritesheet.bind();
}

Следует отметить, что в моей версии списка отображения я добавляю только видимые кубы. Таким образом, это может быть несправедливым преимуществом, но оно не должно сводить версию VBO к тому же FPS с 27 частями по сравнению с 500 частями для версии списка отображения. Я рендеринг так:

public void render() {
    if (tiles.length != -1) {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glCallList(opaqueID);
    }
}

Итак, после всего этого кода я действительно все еще задаюсь вопросом, почему моя версия VBO так чертовски медленна? У меня есть одномерный список чанков в моей версии списка отображения, когда я вызываю их для рендеринга, и 3-мерный список в моей версии VBO, но я думаю, что JVM в значительной степени устраняет любую задержку с дополнительными измерениями. Итак, что я делаю не так?

1 ответ

Трудно ответить на такой вопрос без реального проекта и профилировщика, так что это теории:

  • Вы не показываете код генерации списков отображения подробно, поэтому я предполагаю, что вы делаете что-то похожее glColor(); glVertex3f(); в цикле (не то, что вы объявили цвет один раз и сделали с ним).
  • Реализация списка отображения зависит от реализации, но обычно представляет собой чередующийся массив свойств вершин, потому что это гораздо удобнее для кэша (все подпорки вершин тесно выровнены по 16 байтам, а не распределены по размеру массива). С другой стороны, используемый вами VBO состоит из двух не чередующихся частей - Coordinates и Colours. Это может вызвать чрезмерное использование кеша (особенно при больших объемах данных).

Как отмечено в комментариях:

попробуйте чередовать ваши данные о положении и цвете в одном буфере. Это обычная рекомендация для статических данных, так как она дает лучшие шаблоны доступа к памяти во время рендеринга. - GuyRT`

Другие вопросы по тегам