Повышение производительности рисования с помощью шестиугольной сетки

Я работаю над своей самой первой игрой openGL, вдохновленной игрой «Greed Corp» в сети PlayStation. Это пошаговая стратегическая игра, основанная на гексагональной сетке. Каждая шестиугольная плитка имеет свою высоту и текстуру.

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

public class HexTile
{
    private float height;

    private int[] textures = new int[1];

    private float vertices[] = {     0.0f,   0.0f, 0.0f,    //center
                                     0.0f,   1.0f, 0.0f,    // top
                                    -1.0f,   0.5f, 0.0f,    // left top
                                    -1.0f,  -0.5f, 0.0f,    // left bottom
                                     0.0f,  -1.0f, 0.0f,    // bottom
                                     1.0f,  -0.5f, 0.0f,    // right bottom
                                     1.0f,  0.5f, 0.0f,     // right top
    };

    private short[] indices = {      0, 1, 2, 3, 4, 5, 6, 1};

    //private float texture[] = { };

    private FloatBuffer vertexBuffer;
    private ShortBuffer indexBuffer;
    //private FloatBuffer textureBuffer;    

    public HexTile()
    {
        ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
        vbb.order(ByteOrder.nativeOrder());
        vertexBuffer = vbb.asFloatBuffer();
        vertexBuffer.put(vertices);
        vertexBuffer.position(0);

        ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
        ibb.order(ByteOrder.nativeOrder());
        indexBuffer = ibb.asShortBuffer();
        indexBuffer.put(indices);
        indexBuffer.position(0);

        /*ByteBuffer tbb = ByteBuffer.allocateDirect(texture.length * 4);
        tbb.order(ByteOrder.nativeOrder());
        textureBuffer = tbb.asFloatBuffer();
        textureBuffer.put(texture);
        textureBuffer.position(0);*/
    }

    public void setHeight(float h)
    {
        height = h;
    }
    public float getHeight()
    {
        return height;
    }

    public void draw(GL10 gl)
    {
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
        //gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);

        gl.glDrawElements(GL10.GL_TRIANGLE_FAN, indices.length, GL10.GL_UNSIGNED_SHORT, indexBuffer);
    }

    public void loadGLTexture(GL10 gl, Context context)
    {
        textures[0] = -1;
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.hex);

        while(textures[0] <= 0)
            gl.glGenTextures(1, textures, 0); 

        gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

        bitmap.recycle();
    }
}

В каждом кадре я перебираю все видимые плитки, чтобы отрисовать их, это будет МАКСИМАЛЬНО 11 * 9 плиток. Это, однако, снижает мою частоту кадров до 38, и это даже без рисования на них текстур, только плоских шестиугольников.

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

Я был бы очень признателен за помощь в этом, потому что я хотел бы начать работу над самой игрой ^.^


person Quint Stoffers    schedule 06.06.2011    source источник


Ответы (2)


Предполагая, что ваша шестигранная сетка статична, вы можете просто прокрутить все свои шестиугольники один раз, сгенерировать их геометрию и добавить все к одному (или нескольким, если у вас более 2 ^ 16 вершин) большому VBO, который вы можете нарисовать за один раз.

Что касается текстур, вы можете использовать атлас текстур.

person genpfault    schedule 06.06.2011
comment
Хм, боюсь, сетка не статична, и я не думаю, что смогу правильно сделать то, что вы предложили, с тем, что я знаю об openGL прямо сейчас. Полагаю, мне придется еще немного повозиться с openGL. Я добавлю эту страницу в закладки для дальнейшего использования, спасибо. - person Quint Stoffers; 06.06.2011
comment
@Quipeace: Насколько он нестатичен? Все ваши гексы перемещаются в каждом кадре или только небольшая часть? Если это просто подмножество, вы можете варьировать обновления VBO, как текстуры. - person genpfault; 07.06.2011

В настоящее время я также изучаю OpenGL, я создал одно приложение Android OpenGL под названием «Cloud Stream», где обнаружил аналогичные проблемы с производительностью.

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

Это усложняет кодирование, поскольку вы, по сути, рисуете все свои плитки одновременно, но, похоже, это немного ускоряет работу. В моем приложении все облака рисуются за один вызов.

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

Еще одно предложение, которое я нашел в Интернете, заключалось в том, чтобы использовать Short вместо Float для ваших вершин. А затем изменить масштаб вашего готового рендеринга, чтобы получить желаемый размер. Это уменьшит размер ваших вершин и немного ускорит работу... не очень много, но все же стоит попробовать, я бы сказал.

И последнее, что я хотел бы добавить, если вы в конечном итоге используете какую-либо прозрачность... имейте в виду, что вы должны рисовать задом наперед, чтобы она работала, что также влияет на производительность. Если вы рисуете спереди назад, механизм рендеринга автоматически знает, что не рисовать, когда координаты не видны... рисование сзади наперед означает, что все нарисовано. Помните об этом и всегда старайтесь рисовать спереди назад, когда это возможно.

Удачи в вашей игре, я хотел бы знать, как у вас дела... Я только начинаю свою игру, и я очень рад начать. Если вы еще не сталкивались с этим... Я думаю, это стоит прочитать. http://www.codeproject.com/KB/graphics/hexagonal_part1.aspx

person CatalystNZ    schedule 29.12.2011