OpenGL spritesheets - новичок нуждается в некотором руководстве

Представьте себе следующий сценарий: у вас есть набор спрайт-листов символов RPG в формате PNG, и вы хотите использовать их в приложении OpenGL.

Отдельные символы имеют размер (обычно) 16 на 24 пикселя (то есть 24 пикселя в высоту) и могут иметь любую ширину и высоту, не оставляя отступов. Вроде как:

http://helmet.kafuka.org/terra.net/nomura/Terra.png

У меня уже есть код для определения целочисленного прямоугольника отсечения с учетом индекса кадра и размера:

int framesPerRow = sheet.Width / cellWidth;
int framesPerColumn = sheet.Height / cellHeight;
framesTotal = framesPerRow * framesPerColumn;
int left = frameIndex % framesPerRow;
int top = frameIndex / framesPerRow;
//Clipping rect's width and height are obviously cellWidth and cellHeight.

Запуск этого кода с frameIndex = 11, cellWidth = 16, cellHeight = 24 вернет клип (32, 24)-(48, 48) предполагая, что это справа / снизу в противоположность ширине / высоте.

Актуальный вопрос

Теперь, учитывая прямоугольник отсечения и координату X / Y для размещения спрайта, как мне нарисовать это в OpenGL? Наличие нулевой координаты в верхнем левом углу является предпочтительным.

4 ответа

Решение

Вы должны начать думать в "текстурном пространстве", где координаты находятся в диапазоне [0, 1].

Итак, если у вас есть спрайт-лист:

class SpriteSheet {
    int spriteWidth, spriteHeight;
    int texWidth, texHeight;

    int tex;

public:
    SpriteSheet(int t, int tW, int tH, int sW, int sH)
    : tex(t), texWidth(tW), texHeight(tH), spriteWidth(sW), spriteHeight(sH)
    {}

    void drawSprite(float posX, float posY, int frameIndex);
};

Все, что вам нужно сделать, это отправить вершины и вершины текстуры в OpenGL:

    void SpriteSheet::drawSprite(float posX, float posY, int frameIndex) {
        const float verts[] = {
            posX, posY,
            posX + spriteWidth, posY,
            posX + spriteWidth, posY + spriteHeight,
            posX, posY + spriteHeight
        };
        const float tw = float(spriteWidth) / texWidth;
        const float th = float(spriteHeight) / texHeight;
        const int numPerRow = texWidth / spriteWidth;
        const float tx = (frameIndex % numPerRow) * tw;
        const float ty = (frameIndex / numPerRow + 1) * th;
        const float texVerts[] = {
            tx, ty,
            tx + tw, ty,
            tx + tw, ty + th,
            tx, ty + th
        };

        // ... Bind the texture, enable the proper arrays

        glVertexPointer(2, GL_FLOAT, verts);
        glTextureVertexPointer(2, GL_FLOAT, texVerts);
        glDrawArrays(GL_TRI_STRIP, 0, 4);
    }

};

Решение Franks уже очень хорошо.

Просто (очень важный) знак, так как некоторые из комментариев говорят об обратном.

Пожалуйста, никогда не используйте glBegin/glEnd. Никогда не говорите кому-нибудь использовать это.

Единственный раз, когда можно использовать glBegin / glEnd, это ваша первая программа OpenGL.

Массивы не намного сложнее, но...

  • ... они быстрее.
  • ... они все еще будут работать с более новыми версиями OpenGL.
  • ... они будут работать с GLES.
  • ... загружать их из файлов намного проще.

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

Самый простой способ - загрузить PNG в текстуру (при условии, что у вас есть возможность загружать изображения в память, вам нужен htat), а затем нарисовать его с помощью четырехугольника, задавая соответствующие координаты текстуры (они идут от 0 до 1 с координатами с плавающей запятой)., так что вам нужно разделить на ширину или высоту текстуры соответственно).

Используйте glBegin(GL_QUADS), glTexcoord2f(), glVertex2f(), glEnd() для самого простого (но не самого быстрого) способа нарисовать это.

Чтобы сделать нулевой верхний левый угол, используйте gluOrtho(), чтобы установить матрицу вида иначе, чем обычный GL (посмотрите документы для этой функции, установите top в 0 и bottom в 1 или screen_height, если вы хотите целочисленные координаты), или просто внесите изменения ваш цикл рисования и просто сделайте glVertex2f(x/screen_width, 1-y/screen_height).

Есть лучшие и более быстрые способы сделать это, но это, вероятно, один из самых простых, если вы изучаете сырой OpenGL с нуля.

Предложение, если можно. Я использую SDL для загрузки своих текстур, поэтому я сделал следующее: 1. Я загрузил текстуру. 2. Я определил, как разделить таблицу спрайтов на отдельные спрайты. 3. Я разделяю их на отдельные поверхности. 4. Я делаю текстуру для каждой (у меня есть класс спрайта для управления ими). 5. Освободите поверхности. Это занимает больше времени (очевидно) при загрузке, но окупается позже. Таким образом, это намного проще (и быстрее), поскольку вам нужно только рассчитать индекс текстуры, которую вы хотите отобразить, а затем отобразить ее. Затем вы можете масштабировать / перевести его по своему усмотрению и вызвать список отображения, чтобы отобразить все, что вы хотите. Или, вы можете сделать это в немедленном режиме, либо работает:)

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