Главная » Программирование игр под Android » Отправка вершин к OpenGLES – РАЗРАБОТКА ИГР ДЛЯ ОС ANDROID

0

Как мы опишем местоположение трех вершин нашего первого треугольника? Легко – предположим, что наша система координат лежит в диапазоне от (0; 0; 1) до (320; 480; -1). Тогда мы можем сделать следующее:

Первые три строки должны быть вам уже знакомы. Единственная интересная часть касается того, сколько байт мы выделяем. У нас есть три вершины, каждая из которых имеет положение, определяемое координатами по осям х и у. Каждая координата – это число с плавающей точкой (f1oat), которое занимает 4 байта. Все вершины имеют две координаты, каждая из которых занимает 4 байта, так что в целом для нашего треугольника необходимо 24 байта.

ПРИМЕЧАНИЕ

Мы можем определить вершины только с помощью координат х и у, a OpenGL ES автоматически задаст z-координате значение ноль.

Далее копируем в буфер массив чисел типа float, содержащий данные о вершинах. Наш треугольник начинается в нижнем левом углу (0; 0), далее к правой границе видимой области/экрана (319; 0), а затем к середине верхней границы видимой области/экрана. Поскольку мы уже умеем пользоваться буфером NIO, мы также применяем метод f 1 i р к нашему буферу. Таким образом, posi ti on будет 0, a limit будет 6 (помните, что значения свойств 1 imit и position FloatBuffer задаются в числах с плавающей точкой, а не в байтах).

Как только наш буфер NIO готов, мы можем указать OpenGL ES отрисовать его в текущем состоянии (например, с областью просмотра и матрицей проекции). Это можно сделать с помощью следующего фрагмента:

Вызов метода gl EnabledientState можно назвать рудиментарным. Так мы сообщаем OpenGL ES, что вершины, которые мы собираемся нарисовать, имеют позиции. Это немного глупо по двум следующим причинам: константа называется GL10. GL VERTEX ARRAY, несколько путано. Было бы разумнее назвать ее GL10.GL P0SITI0N ARRAY; невозможно нарисовать то, у чего нет позиции, так что использовать данный метод несколько излишне. Мы тем не менее применим его для удобства работы с OpenGL ES.

Обращаясь к gl VertexPointerC, мы сообщаем OpenGL ES, где можно найти позиции вершин, а также некоторую другую дополнительную информацию. Первый параметр сообщает OpenGL ES, что каждая позиция вершины состоит из двух координат, х и у. Если бы вы ввели х, у и z, координат было бы три. Второй параметр сообщает OpenGL ES тип данных, в которых хранится каждая координата. В нашем случае это GL10. GLFL0AT, который показывает, что мы использовали f1oat по 4 байта каждый. Третий параметр, stride (шаг), сообщает OpenGL, насколько далеко в байтах вершины расположены друг от друга. В предыдущем случае шаг был равен нулю, так как позиции располагались очень близко друг к другу (вершина 1 (х; у), вершина 2 (х; у) и т. д.). Последним параметром является FloatBuffer, касательно которого следует запомнить две вещи:

FloatBuffег является блоком памяти в нативной куче, так что он имеет начальный адрес; позиция FloatBuffer – смещение относительно начального адреса.

OpenGL ES берет адрес начала буфера и прибавляет к нему позицию буфера, чтобы попасть к тому числу с плавающей точкой, с которого начнется считывание вершин, когда мы укажем выполнить отрисовку содержимого буфера. Указатель на вершины (который опять-таки правильнее называть указателем на местоположение) – это состояние OpenGL ES. Пока мы не изменим его (и контекст не потеряется), OpenGL ES будет помнить и использовать его для всех последующих запросов, требующих позицию вершин.

И наконец, рассмотрим вызов gl DrawArraysC. Этот метод отрисует наш треугольник. Первый параметр определяет, элементарную фигуру какого типа мы собираемся рисовать. В данном случае мы хотим нарисовать несколько треугольников, определяемых GL10.GLTRIANGLES. Следующий параметр – это смещение относительно первой вершины на обозначенную в указателе вершину. Данное смещение измеряется в вершинах, а не байтах или числах с плавающей точкой. Если бы мы описывали более чем один треугольник, мы могли бы использовать этот фрагмент, чтобы визуализировать лишь часть списка треугольников. Наш последний аргумент сообщает OpenGL ES, сколько вершин нужно использовать для визуализации. В нашем случае это три вершины. Обратите внимание, что нам всегда необходимо определить количество, кратное трем, если мы рисуем GL10. GLTRIANGLES. Каждый треугольник состоит из трех вершин, так что это целесообразно. Для других простейших фигур правила несколько отличаются.

Когда мы запускаем команду gl VertexPoi nter, OpenGL ES передает месторасположения вершины графическому процессору и сохраняет их для всех последующих команд визуализации. Каждый раз, когда мы указываем OpenGL ES визуализировать вершины, их местоположение узнается из данных, которые мы определили с помощью gl VertexPoi nter.

Каждая наша вершина может иметь и другие свойства кроме местоположения. Еще одним свойством может быть цвет вершины. Обычно эти свойства называются свойствами вершины.

Может возникнуть вопрос: как OpenGL ES узнает, какого цвета сделать треугольник, если мы определили только местоположение. Дело в том, что OpenGL ES содержит настройки по умолчанию для свойств вершины, которые мы сами не указали. Большинство данных свойств можно настроить напрямую. Например, чтобы установить цвет для всех вершин, которые рисуем, можно использовать следующий метод:

Этот метод задаст цвет по умолчанию, используемый для всех вершин, для которых мы не указали цвет отдельно. Данный цвет указывается в значениях RGB А в диапазоне от 0,0 до 1,0 – такой же диапазон мы уже применяли при работе с цветом выше. Цвет по умолчанию, с которого начинает OpenGL ES, – это (1; 1; 1; 1), матовый белый.

Вот и весь код, который нам нужен для визуализации треугольника в параллельной проекции с помощью OpenGL ES. Эти 16 строк кода необходимы нам для очистки экрана, установки области просмотра и матрицы проекции, создания буфера N10, в котором мы храним информацию о положении вершин, и для отрисовки треугольника. А теперь сравните это количество кода с несколькими страницами, которые ушли у меня на то, чтобы объяснить все это. Конечно, я мог бы опустить некоторые детали и выражаться попроще. Проблема в том, что OpenGL ES местами довольно сложен, и для того, чтобы не получить после работы пустой экран, лучше сначала изучить все подробно, а не просто копировать и вставлять код.

Все вместе

Чтобы поДвести итоги этого раздела, объединим все с помощью реализации GLGame и Screen. В листинге 7.5 показан пример того, как это сделать.

Листинг 7.5. FirstTriangleTest.Java package com.bad.ogic.androidgames.glbasics;

Класс FirstTriangleTest наследует от GLGame и, соответственно, реализует метод Game.getStartScreenO. В этом методе мы создаем новый класс FirstTriangleScreen, который впоследствии будет вызываться GLGame, обновляться и отображаться. Обратите внимание, что, когда вызывается этот метод, мы уже находимся в основном цикле или даже скорее в потоке визуализации GLSurf aceVi ew, так что мы можем использовать методы OpenGL ES в конструкторе класса FirstTriangleScreen. Рассмотрим реализацию класса Screen подробнее:

Класс FirstTriangleScreen содержит два члена: экземпляр класса GLGraphics и наш проверенный буфер FloatBuf fег, который хранит 20-положения трех вершин нашего треугольника. В конструкторе переносим экземпляр класса GLGraphics из GLGame и создаем FloatBuf fer в соответствии с нашим предыдущим фрагментом кода. Поскольку конструктор Sc reen получает экземпляр класса Game, нам необходимо привести его к классу GLGame, чтобы иметь возможность вызвать метод GLGame.getGLGraphics.

Метод present отражает то, что мы с вами только что обсудили: мы устанавливаем область просмотра, очищаем экран, устанавливаем матрицу проекции, чтобы можно было работать в измененной системе координат, задаем цвет по умолчанию для вершин (в нашем случае красный), указываем, что у наших вершин будут определенные положения, сообщаем OpenGL ES, где находится информация об этих положениях вершин, и, наконец, отображаем наш маленький красный треугольник.

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

ПРИМЕЧАНИЕ

С этого момента мы сосредоточимся только на самих классах Screen, так как заключающие их производные классы GLGame, такие как RrstTriangleTest, не изменятся. Мы также немного сокращаем код, убирая все ненужные методы класса Screen. Следующие примеры будут различаться только названиями элементов, конструкторов и методов презентации.

На рис. 7.7 показано, какое изображение получилось в результате предыдущего примера.

Рассмотрим, что же мы сделали не так в этом примере с точки зрения оптимальных методов работы с OpenGL ES.

Мы устанавливали одни и те же состояния одним и тем же значениям снова и снова без какой-либо на то нужды. Изменения состояний в OpenGL ES занимают много памяти – некоторые немного больше, некоторые немного меньше. Мы всегда должны стремиться сократить количество изменений состояния, которые мы делаем за один кадр.

Точка наблюдения и матрица проекции никогда не изменяются с момента, как мы их установили. Мы можем переместить этот код в метод resume, который вызывается всего один раз, когда создается (или создается заново) поверхность OpenGL ES. Здесь же мы справляемся с потерей контекста OpenGL ES.

Рис. 7.7. Наш первый фрейдистский треугольник

Мы можем также переместить настройку цвета для очистки экрана и выполняемую по умолчанию установку цвета вершины в метод resume. Эти два цвета также не будут изменяться.

Мы можем переместить методы gl Enabled ientState и gl VertexPoi nter в метод resume.

Единственное, что надо вызывать в каждом кадре, – это методы glClearO и glDrawArrays. Они оба используют текущие состояния OpenGL ES, которые остаются нетронутыми до тех пор, пока мы не изменим их или не случится потеря контекста из-за того, что Activity была приостановлена и запущена вновь.

Если выполнить все эти оптимизации, у нас будет только два вызова OpenGL ES в основном цикле.

Чтобы окончательно не запутаться, мы пока не будем использовать данные оптимизации. Однако когда мы перейдем к нашей первой игре на OpenGL ES, нам придется придерживаться данных рекомендаций как можно строже.

Добавим еще несколько атрибутов к вершинам нашего треугольника. Для начала займемся цветом.

ПРИМЕЧАНИЕ

Наблюдательные читатели, наверное, уже заметили, что у треугольника на рис. 7.7 не хватает пиксела в нижнем правом углу. Возможно, это выглядит, как типичная ошибка занижения на единицу, но на самом деле, это происходит из-за того, что OpenGL ES растрирует (рисует пикселы) треугольника. Существует особое правило растрирования треугольника, которое вызывает подобный огрех. Однако не стоит переживать. Нас с вами волнует визуализация двухмерных прямоугольников (состоящих из двух треугольников), где этот эффект нивелируется.

Источник: Mario Zechner / Марио Цехнер, «Программирование игр под Android», пер. Егор Сидорович, Евгений Зазноба, Издательство «Питер»

По теме:

  • Комментарии