Эта статья дает краткий обзор того, как выводить изображения используя OpenGL и как libgdx упрощает и оптимизирует задачу через SpriteBatch
класс.
Отрисовка изображений
Изображение, которое было декодировано из исходного формата (например PNG) и загружено в видео память графического процессора, называется текстурой. Чтобы нарисовать текстуру описывается геометрия и текстура применяется с указанием где находится каждая соответствующая вершина геометрии на текстуре. Например геометрией может быть прямоугольник и текстура может быть применена так, что каждый угол прямоугольника будет соответствовать углу текстуры. Прямоугольник, который является подмножеством текстуры называется текстурным регионом.
Для фактической отрисовки, сначала привязывается текстура (текстура делается текущей), затем геометрия передается в OpenGL для рисования. Размер и позиция нарисованной текстуры на экране определяется геометрией и как настроено окно просмотра OpenGL. Многие 2D игры настраивают окно просмотра так, чтобы оно совпадало с разрешением экрана. Это означает что геометрия задается в пикселях, что делает легким рисование текстуры в соответствии с размерам и позицией на экране.
Очень часто текстура рисуется с отображением на прямоугольной геометрии. Так же очень часто, много раз делается рисование одной и той же текстуры или ее различных регионов. Было бы не эффективно каждый раз пересылать по одному прямоугольнику в графический процессор для отрисовки. Вместо этого, множество прямоугольников одной и тоже текстуры, можно описать и переслать все разу в графический процессор. Это то, что делается SpriteBatch
класс.
Класс SpriteBatch
предоставляет текстуру и координаты для каждого рисования прямоугольника. Он делает сбор геометрии без передачи ее в графический процессор. Если он получает текстуру отличную от последней, то он привязывает последнею текстуру, передавая собранную геометрию для рисования, и начинает сбор геометрии для новой текстуры.
Каждая смена текстуры при рисовании нескольких прямоугольников, приводит в том, что SpriteBatch
группирует много геометрии. Так же, привязка текстуры является довольно дорогостоящей операцией. По этим причинам, он хранит много маленьких изображений в одном большом изображении и затем рисует регионы из одного большого изображения для максимальной группировки геометрии и предотвращения частой смены текстур. Для большей информации смотрите TexturePacker.
SpriteBatch
Использование SpriteBatch
в приложении выглядит следующим образом:
public class Game implements ApplicationListener { private SpriteBatch batch; public void create() { batch = new SpriteBatch(); } public void render() { // Очистка экрана Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); batch.begin(); // Здесь происходит рисование! batch.end(); } public void resize(int width, int height) { } public void pause() { } public void resume() { } public void dispose() { } }
Все вызовы рисования SpriteBatch
должны быть сделаны между begin()
и end()
методами. Рисование не принадлежащие к SpriteBatch не может быть между begin()
и end()
методами.
Текстура
Класс Texture
декодирует файл изображения и загружает его в память графического процессора. Файл изображения должен располагаться в assets
директории, как описано в ручной настройки проекта. Размер изображения должен быть степенью двух (16x16, 64x256 и так далее).
private Texture texture; // ... texture = new Texture(Gdx.files.internal("image.png")); // ... batch.begin(); batch.draw(texture, 10, 10); batch.end();
Созданная текстура передается в SpriteBatch
для отрисовки. Текстура будет нарисована в прямоугольнике с позицией 10,10 и шириной и высотой разными размеру текстуры. SpriteBatch
имеет много методов для отрисовки текстур:
Методы и описание |
---|
draw(Texture texture, float x, float y)
Рисует текстуру используя ширину и высоту текстуры. |
draw(Texture texture, float x, float y,
int srcX, int srcY, int srcWidth, int srcHeight)
Рисует част текстуры. |
draw(Texture texture, float x, float y,
float width, float height, int srcX, int srcY,
int srcWidth, int srcHeight, boolean flipX, boolean flipY)
Рисует часть текстуры, растягивает до ширины |
draw(Texture texture, float x, float y,
float originX, float originY, float width, float height,
float scaleX, float scaleY, float rotation,
int srcX, int srcY, int srcWidth, int srcHeight,
boolean flipX, boolean flipY)
Этот метод рисует часть текстуры, растягивает до ширины |
draw(Texture texture, float x, float y,
float width, float height, float u,
float v, float u2, float v2)
Этот метод рисует часть текстуры, растягивает до ширины |
draw(Texture texture, float[] spriteVertices, int offset, int length)
Это продвинутый метод для передачи геометрии текстурных координат и цветовой информации. Этот метод можно использовать для рисования какие-либо четырехугольник, а не только прямоугольники. |
TextureRegion
Класс TextureRegion
описывает прямоугольник внутри текстуры и полезен при рисовании только части текстуры.
private TextureRegion region; // ... texture = new Texture(Gdx.files.internal("image.png")); region = new TextureRegion(texture, 20, 20, 50, 50); // ... batch.begin(); batch.draw(region, 10, 10); batch.end();
Здесь 20, 20, 50, 50
описывают часть текстуры, которая замет рисуется как 10,10. Тоже самое может быть достигнуто путем передачи Texture
и других параметров в SpriteBatch
, но TextureRegion
делает это удобнее, имея только один объект, который описывает то и другое.
Класс SpriteBatch
имеет много методов для рисования регионов текстуры.
Методы и описание |
---|
draw(TextureRegion region, float x, float y)
Рисует регион используя ширину и высоту региона. |
draw(TextureRegion region, float x, float y,
float width, float height)
Рисует регион, растягивая до ширины |
draw(TextureRegion region, float x, float y,
float originX, float originY, float width, float height,
float scaleX, float scaleY, float rotation)
Рисует регион, растягивая до ширины |
Спрайт
Класс Sprite
регион текстуры и геометрию, где он будет рисоваться и цвет для рисования.
private Sprite sprite; // ... texture = new Texture(Gdx.files.internal("image.png")); sprite = new Sprite(texture, 20, 20, 50, 50); sprite.setPosition(10, 10); sprite.setRotation(45); // ... batch.begin(); sprite.draw(batch); batch.end();
Здесь 20, 20, 50, 50
описывают часть текстуры, которая повернута на 45 градусов и рисуется в позиции 10,10. Тоже самое можно достигнуть передачей Texture
или TextureRegion
и других параметров в SpriteBatch
, но класс Sprite
делает это более удобным, имея один объект, которые описывает все. Кроме того, поскольку Sprite
содержит геометрию и пересчитывает ее только когда необходимо, он немного более эффективный, если масштаб, поворот или другие свойства остаются неизменными между кадрами.
Обратите внимание, что Sprite
смешивает информацию о модели (позиция, угол поворота и так далее) с информацией о виде (текстура для рисования). Это делает Sprite
несоответствующим при применении шаблона проектирования, который строго отделяет модель от вида. В этом случае, использование Texture
и TextureRegion
может иметь больший смысл.
Тонирование
Когда рисуется текстура, она может быть тонирована определенным цветом.
private Texture texture; private TextureRegion region; private Sprite sprite; // ... texture = new Texture(Gdx.files.internal("image.png")); region = new TextureRegion(texture, 20, 20, 50, 50); sprite = new Sprite(texture, 20, 20, 50, 50); sprite.setPosition(100, 10); sprite.setColor(0, 0, 1, 1); // ... batch.begin(); batch.setColor(1, 0, 0, 1); batch.draw(texture, 10, 10); batch.setColor(0, 1, 0, 1); batch.draw(region, 50, 10); sprite.draw(batch); batch.end();
Этот код показывает как рисовать текстуру, регион и спрайт с тонированием. Значения цвета описываются используя RGBA формат, со значения между 0 и 1. Альфа значение игнорируется при отключеным смешивании.
Смешивание
Смешивание включено по умолчанию. Это означает, что когда рисуется текстура, то полупрозрачные участки текстуры объединяются с пикселями уже находящимися на этом месте экрана.
Когда смешивание отключено, то все находящиеся на экране в этом месте, заменяется текстурой. Это более эффективно, так что смешивание должно быть отключено, если оно не нужно. Например, когда рисуется на весь экран большое изображение фона, то производительность может быть достигнуть путем отключения смешивания:
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // Эта строка очищает экран batch.begin(); batch.disableBlending(); backgroundSprite.draw(batch); batch.enableBlending(); // Рисование чего-либо другого. batch.end();
Примечание: обязательно убедитесь, что экран очищается при каждом кадре. Если этого не сделать, то текстура с альфа каналом, может рисоваться поверх себя сотню раз, делая этим кажущуюся непрозрачность. Кроме того, некоторые архитектуры графических процессоров работают лучше, когда экран очищается при каждом кадре, даже если непрозрачные изображения рисуются на весь экран.
Окно просмотра
SpriteBatch
управляет собственными матрицами проекции и преобразования. Когда создан SpriteBatch
, он использует текущий размер приложения для установки ортографической проекции, используя Y систему координат. Когда вызван метод begin()
, он устанавливает окно просмотра.
Настройка производительности
Класс SpriteBatch
имеет конструктор, который устанавливает максимальное число спрайтов, которые могут быть помещены в буфер перед оправкой графическому процессору. Если число слишком мало, это приведет к дополнительным обращениям к графическому процессору. Если оно слишком велико, то SpriteBatch
будет использовать больше памяти, чем это необходимо.
SpriteBatch
имеет публичное поле с именем maxSpritesInBatch
. Это поле указывает на самое большое количество спрайтов, оправленных сразу же графическому процессору, в течении всего жизненного цикла SpriteBatch
. Установка очень большого SpriteBatch
размера и затем проверка этого поля, могут помощь определить оптимальный размер для SpriteBatch
. Следует иметь такой размер, чтобы он был равным или немного больше чем maxSpritesInBatch
. Это поле может быть установлено в ноль или сброшено в любое время.
SpriteBatch
имеет публичное поле с именем renderCalls
. После вызова end()
метода, это поле показывает сколько раз группа геометрии, была передана графическому процессору между последними вызовами begin()
и end()
. Это происходит, когда должна быть привязана другая текстура или когда SpriteBatch
имеет в кэше достаточно спрайтов и становиться полностью заполненным. Соответственно, если для SpriteBatch
изменен размер и renderCalls
имеет большое значение (возможно больше чем 15-20), то это указывает, что происходит множественная привязка текстур.
SpriteBatch
имеет дополнительный конструктор, который принимает размер и количество буферов. Это дополнительная особенность, которая позволяет использовать VBO, а не обычный массив вершин. Таким образом, есть список буферов и при каждом вызове визуализации, используется следующий в списке буфер (меняя по кругу). Когда значение maxSpritesInBatch
велико и renderCalls
мало, то это может дать небольшой прирост производительность.
Комментариев нет:
Отправить комментарий