
opengles.pptx
- Количество слайдов: 29
КАЛУЖСКИЙ ФИЛИАЛ ФЕДЕРАЛЬНОГО ГОСУДАРСТВЕННОГО БЮДЖЕТНОГО ОБРАЗОВАТЕЛЬНОГО УЧРЕЖДЕНИЯ ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ «МОСКОВСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ ИМ. Н. Э. БАУМАНА» Факультет "Фундаментальных наук" Кафедра "Программного обеспечения ЭВМ, информационных технологий и прикладной математики" Open. GL ES в системе Android Калуга
Open. GL ES (Open. GL for Embedded Systems — Open. GL для встраиваемых систем) — подмножество графического интерфейса Open. GL, разработанное специально для встраиваемых систем — мобильных телефонов, карманных компьютеров, игровых консолей. Open. GL ES определяется и продвигается консорциумом Khronos Group, в который входят производители программного и аппаратного обеспечения, заинтересованные в открытом API для графики и мультимедиа. Рассмотрим средства, предоставляемые платформой Android для работы с Open. GL ES версии 2. 0.
С чего начинается приложение Для начала необходимо создать новый Android проект в среде разработке Eclipse, имя проекта не имеет принципиального значения. Связующим звеном между виртуальной машины dalvik и низкоуровневым интерфейсом взаимодействия с библиотекой Open. GL является класс GLSurface. View. Данный класс является особым элементом отображения ( view ), который управляет Open. GL поверхностями ( surfaces ) и занимается выводом на них буфера кадра. Этот класс также очень упрощает работу с Open. GL, не ограничивая функционала. Класс позволяет • Производить рендер без задержек главного потока приложения. • Производить непрерывный процесс рендеринга или вариант рендеринга по требованию ( on-demand ). • Выполняет всевозможные настройки по выбору формата пикселя, частоты обновления и т. д.
Манифест Для возможности использования средств Open. GL ES 2 в манифест проекта необходимо добавить следующие строки. <!– Требование поддержки Open. GL ES 2. 0 --> <uses-feature android: gl. Es. Version="0 x 00020000" android: required="true" />
Методы родительской активности В методе on. Create () первым делом после вызова родительского класса создаётся GLSurface. View. Далее идёт проверка поддержки Open. GL ES 2. Для этого получается экземпляр Activity. Manager, который позволяет взаимодействовать с глобальным состоянием системы Android. С помощью этого можно получать различные параметры устройства, в частности наличие поддержки Open. GL ES 2. Так же очень важно, чтобы методы on. Resume () и on. Pause () для родительской активности вызывали одноимённые методы у GLSurface. View.
Процесс растеризации GLSurface. View имеет определённую логику работы, заключающуюся в обработке событий. События вызываются системой автоматически. Для программиста это означает, что необходимо написать реализацию следующих методов: • public void on. Surface. Created ( GL 10 gl. Unused, EGLConfig config ) Это событие наступает, когда поверхность ( surface ) была создана впервые. Данное событие также наступает, если контекст поверхности ( surface ) по какой-либо причине удаляется или пересоздаётся. • public void on. Surface. Changed ( GL 10 gl. Unused, int width, int height ) Событие наступает, если поверхность ( surface ) изменяется, например при изменении портретного режима на альбомный. Также событие наступает сразу после создания поверхности ( surface ). • public void on. Draw. Frame ( GL 10 gl. Unused ) Как видно из названия, событие вызывается для каждого нового кадра. Можно заметить, что первым параметром всех методов стоит некий экземпляр класса GL 10, который вдобавок ещё и не используется. В действительности вместо него используются статические методы класса GLES 20. Параметр был оставлен, потому что Open. GL ES 1. x имеет сходный интерфейс.
Буферы данных Данные для отрисовки примитивов необходимо поместить в массивы ( буферы данных ). Эти буфера могут содержать различную информацию, координаты, цвета, нормали и т. д. В остальном процесс работы с Open. GL полностью совпадает с изученным ранее. Однако Open. SE 2. 0 позволяет более гибко настраивать процесс растеризации, во многом благодаря возможности применения шейдеров. Из-за того, что приложение пишется на Java, а Open. GL ES 2 была написана на С, то данные перед использованием необходимо предварительно подготовить. Именно по этой причине в начале создаётся Byte. Buffer соответствующего размера. Так как все данные это float, то после создания буфера необходимо преобразовать его в Float. Buffer. Это может показаться чересчур запутанным механизмом, однако иного решения задачи пока не придумано.
Как не стоит делать gl. Begin ( GL_TRIANGLES ); gl. Vertex 3 f (-0. 5 f, -0. 25 f, 0. 0 f ); gl. Color 3 f ( 1. 0 f, 0. 0 f ); . . . gl. End (); Этот механизм не используется в Open. GL ES 2. Вместо него применяется более эффективное решение, а именно передача всей информации о геометрии и её свойствах через буфера данных.
Матрицы преобразований являются важной частью Open. GL. На практике используются матрицы 3 х3 и 4 х4. В машинной графике очень распространены операции перемножения матриц, поиск обратной матрицы, которые в общем случае не так просты. Поэтому в процессе написания приложения с поддержкой Open. GL ES 2 активно используется статический класс Matrix, содержащий реализации многих полезных методов при работе с матрицами.
Шейдеры Механизм шейдеров предполагает написание разработчиком как минимум двух шедеров – вершинного и фрагментного. Используется язык GLSL. Структура шейдерной программы и синтаксис очень просты и поэтому предлагается для самостоятельного изучения. Сама шейдерная программа – это строка с инструкциями. Пример вершинного шейдера final String vertex. Shader = "uniform mat 4 u_MVPMatrix; n" + "attribute vec 4 a_Position; n" + "attribute vec 4 a_Color; n" + "varying vec 4 v_Color; n" + "void main ()n" + "{n" + " v_Color = a_Color; n" + " gl_Position = u_MVPMatrix * a_Positionn" + "}n";
Пример фрагментного шейдера final String fragment. Shader = "precision mediump float; n" + "varying vec 4 v_Color; n" + "void main()n" + "{n" + " gl_Frag. Color = v_Color; n" + "}n";
Разработка приложения Посмотрим как создать простейшее приложение, которое будет выводить три треугольника и поворачивать относительно геометрического центра. Для начала необходимо создать два класса. Один будет являться наследником от Activity, а другой будет производить рендерер средствами Open. GS ES.
Листинг Activity. Class public class Activity. Class extends Activity { //Ссылка на GLSurface. View private GLSurface. View m. GLSurface. View; @Override public void on. Create ( Bundle saved. Instance. State ) { super. on. Create ( saved. Instance. State ); m. GLSurface. View = new GLSurface. View ( this ); // Проверка поддержики Open. GL ES 2. 0 final Activity. Manager activity. Manager = ( Activity. Manager ) get. System. Service ( Context. ACTIVITY_SERVICE ); final Configuration. Info configuration. Info = activity. Manager. get. Device. Configuration. Info (); final boolean supports. Es 2 = configuration. Info. req. Gl. Es. Version >= 0 x 20000; if ( supports. Es 2 ) { // Запрос на Open. GL ES 2. 0 совместимый контекст m. GLSurface. View. set. EGLContext. Client. Version ( 2 ); // Установка собственного рендерера. Он описан в другом классе m. GLSurface. View. set. Renderer ( new Renderer. Class () ); }
else { // Срабатывает, если необходимо поддерживать версии 2. 0 и 1. x // одновременно return; } set. Content. View ( m. GLSurface. View ); } @Override protected void on. Resume () { // Активность должна уведомлять GLSurface. View о событии on. Resume () super. on. Resume (); m. GLSurface. View. on. Resume (); } @Override protected void on. Pause () { // Активность должна уведомлять GLSurface. View о событии on. Pause () super. on. Pause (); m. GLSurface. View. on. Pause (); } }
Листинг Renderer. Class public class Renderer. Class implements GLSurface. View. Renderer { //Модельная матрица преобразований 4 х4 из float private float[] m. Model. Matrix = new float [16]; //Видовая матрица преобразований 4 х4 из float private float[] m. View. Matrix = new float [16]; //Проекционная матрица преобразований 4 х4 из float private float[] m. Projection. Matrix = new float [16]; //Итоговая матрица преобразований 4 х4 из float //Переводит трехмерные координаты вершин в друхмерные координаты. //Есть произведение: проекционная * видовая * модельная private float[] m. MVPMatrix = new float [16]; //Данные о первом треугольниках private final Float. Buffer m. Triangle 1 Vertices; private final Float. Buffer m. Triangle 2 Vertices; private final Float. Buffer m. Triangle 3 Vertices /**Посредством индентификаторв осуществляется передача данных из приложения в шейдер. */ //Идентификатор итоговой матрицы преобразований в шейдерной программе private int m. MVPMatrix. Handle; //Идентификатор трёхмерных координат вершины в шейдерной программе private int m. Position. Handle; //Идентификатор цвета вершины в шейдерной программе private int m. Color. Handle;
/** Параметры необходимы для Open. GL ES API */ //Количество байт на элемент буфера ( float = 4 байта ) private final int m. Bytes. Per. Float = 4; //Количество байт, отводимое на вершину. //( 3 координаты + 4 цвет ) * размер одного элемента буфера private final int m. Stride. Bytes = 7 * m. Bytes. Per. Float; /** Механизм смещений и размеров необходим шейдерной программе, чтобы интерпретировать данные в пришедших массивах. */ //Смещение в буфере, с которого начинаются координаты private final int m. Position. Offset = 0; //Количество подряд идущих элементов, описывающих координаты вершины private final int m. Position. Data. Size = 3; //Смещение в буфере, с которого начинаются данные о цвете private final int m. Color. Offset = 3; //Количество подряд идущих элементов, описывающих цвет вершины private final int m. Color. Data. Size = 4;
public Renderer. Class () //Инициализация { //Данные описывающие треугольники final float[] triangle 1 Vertices. Data = { // X, Y, Z, // R, G, B, A -0. 5 f, -0. 25 f, 0. 0 f, 1. 0 f, 0. 0 f, 1. 0 f, 0. 5 f, -0. 25 f, 0. 0 f, 1. 0 f, 0. 0 f, 0. 559016994 f, 0. 0 f, 0. 0 f, 1. 0 f, 0. 0 f, 1. 0 f }; final float[] triangle 2 Vertices. Data = { -0. 5 f, -0. 25 f, 0. 0 f, 1. 0 f, 0. 0 f, 1. 0 f, 0. 5 f, -0. 25 f, 0. 0 f, 0. 0 f, 1. 0 f, 0. 0 f, 0. 559016994 f, 0. 0 f, 1. 0 f, 0. 0 f, 1. 0 };
final float[] triangle 3 Vertices. Data = { -0. 5 f, -0. 25 f, 0. 0 f, 1. 0 f, 0. 5 f, -0. 25 f, 0. 0 f, 0. 5 f, 1. 0 f, 0. 0 f, 0. 559016994 f, 0. 0 f, 1. 0 f }; // Выделение буферов, в недрах устройства m. Triangle 1 Vertices = Byte. Buffer. allocate. Direct ( triangle 1 Vertices. Data. length * m. Bytes. Per. Float ). order ( Byte. Order. native. Order () ). as. Float. Buffer (); m. Triangle 2 Vertices = Byte. Buffer. allocate. Direct ( triangle 2 Vertices. Data. length * m. Bytes. Per. Float ). order ( Byte. Order. native. Order () ). as. Float. Buffer (); m. Triangle 3 Vertices = Byte. Buffer. allocate. Direct ( triangle 3 Vertices. Data. length * m. Bytes. Per. Float ). order ( Byte. Order. native. Order () ). as. Float. Buffer (); //Заполнение буферов данными из массивов m. Triangle 1 Vertices. put ( triangle 1 Vertices. Data ). position ( 0 ); m. Triangle 2 Vertices. put ( triangle 2 Vertices. Data ). position ( 0 ); m. Triangle 3 Vertices. put ( triangle 3 Vertices. Data ). position ( 0 ); }
@Override public void on. Surface. Created ( GL 10 gl. Unused, EGLConfig config ) { //Установка цвета заднего фона GLES 20. gl. Clear. Color ( 0. 5 f, 0. 5 f ); /** Для формирования видовой матрицы ( позиция камеры ) можно воспользоваться функцией set. Look. At. M класса Matrix, которой нужны следующие параметры*/ final float eye. X = 0. 0 f; final float eye. Y = 0. 0 f; final float eye. Z = 1. 5 f; final float look. X = 0. 0 f; final float look. Y = 0. 0 f; final float look. Z = -5. 0 f; final float up. X = 0. 0 f; final float up. Y = 1. 0 f; final float up. Z = 0. 0 f; //Получение видовой матрицы Matrix. set. Look. At. M ( m. View. Matrix, 0, eye. X, eye. Y, eye. Z, look. X, look. Y, look. Z, up. X, up. Y, up. Z );
/** Механизм шейдеров предполагает написание двух программ - вершинного шейдера и фрагментного шейдера*/ //По сути листинг вершинного шейдера final String vertex. Shader = "uniform mat 4 u_MVPMatrix; n"//Неизменяемая ( uniform ) переменная с //итоговой матрицей преобразований. + "attribute vec 4 a_Position; n"//Трёхмерная координата очередной + "attribute vec 4 a_Color; n"//Цвет очередной вершины. + "varying vec 4 v_Color; n"// Переменная, связующая вершинный и //фрагментный шейдер. Содержит цвет //который будет интерполироваться //шейдером + "void main() n"//Точка входа в вершинный шейдер + "{ n" + " v_Color = a_Color; n" //Передача цвета во фрагментный шейдер + " gl_Position = u_MVPMatrix n" //Перевод реальных трёхмерных //координаты вершины + " * a_Position; n" //в однородные координаты. Проще //говоря, в координаты + "} n"; //плоского экрана устройства
//По сути листинг фрагментного шейдера final String fragment. Shader = "precision mediump float; n" //Установка точности вычислений //фрагментного шейдера + "varying vec 4 v_Color; n" //Связующая переменная о цвете вершины. //Однако теперь это уже определённый фрагмент, // который прошёл процесс цветовой интерполяции. + "void main() n"//Точка входа во фрагментный шейдер. + "{ n" + " gl_Frag. Color = v_Color; n"//Занесение цвета фрагмента в буфер //кадра ( вывод на экран ). + "} n"; /**Одних листингов мало. Для использования шейдеров необходимо скомпилировать Их и произвести линковку ШЕЙДЕРНОЙ ПРОГРАММЫ, содержащей оба шейдера*/ // Создание заготовки для вершинного шейдера int vertex. Shader. Handle = GLES 20. gl. Create. Shader ( GLES 20. GL_VERTEX_SHADER ); if ( vertex. Shader. Handle != 0 ) { // Помещение в заготовку исходника вершинного шейдера GLES 20. gl. Shader. Source ( vertex. Shader. Handle, vertex. Shader ); // Компиляция исходника GLES 20. gl. Compile. Shader ( vertex. Shader. Handle );
// Проверка удачности компиляции final int[] compile. Status = new int[ 1 ]; GLES 20. gl. Get. Shaderiv ( vertex. Shader. Handle, GLES 20. GL_COMPILE_STATUS, compile. Status, 0 ); // В случае неудачи удаляем вершинный шейдер if ( compile. Status[ 0 ] == 0 ) { GLES 20. gl. Delete. Shader ( vertex. Shader. Handle ); vertex. Shader. Handle = 0; } } if ( vertex. Shader. Handle == 0 ) { throw new Runtime. Exception ( "Error creating vertex shader. " ); } // Создание заготовки для фрагментного шейдера int fragment. Shader. Handle = GLES 20. gl. Create. Shader ( GLES 20. GL_FRAGMENT_SHADER ); if ( fragment. Shader. Handle != 0 ) { // Помещение в заготовку исходника фрагментного шейдера GLES 20. gl. Shader. Source ( fragment. Shader. Handle, fragment. Shader ); // Компиляция исходника GLES 20. gl. Compile. Shader ( fragment. Shader. Handle );
// Проверка удачности компиляции final int[] compile. Status = new int[1]; GLES 20. gl. Get. Shaderiv ( fragment. Shader. Handle, GLES 20. GL_COMPILE_STATUS, compile. Status, 0 ); // В случае неудачи удаляем фрагментный шейдер if ( compile. Status[ 0 ] == 0 ) { GLES 20. gl. Delete. Shader ( fragment. Shader. Handle ); fragment. Shader. Handle = 0; } } if ( fragment. Shader. Handle == 0 ) { throw new Runtime. Exception ( "Error creating fragment shader. " ); } // Создание заготовки под ШЕЙДЕРНУЮ ПРОГРАММУ int program. Handle = GLES 20. gl. Create. Program (); if ( program. Handle != 0 ) { // Прикрепление вершинного и фрагментного шейдеров к ШЕЙДЕРНОЙ ПРОГРАММЕ GLES 20. gl. Attach. Shader ( program. Handle, vertex. Shader. Handle ); GLES 20. gl. Attach. Shader ( program. Handle, fragment. Shader. Handle ); // Назначение идентификаторов переменных с координатами и цветом в недрах // ШЕЙДЕРНОЙ ПРОГРАММЫ GLES 20. gl. Bind. Attrib. Location ( program. Handle, 0, "a_Position" ); GLES 20. gl. Bind. Attrib. Location ( program. Handle, 1, "a_Color" );
// Линковка ШЕЙДЕРНОЙ ПРОГРАММЫ GLES 20. gl. Link. Program ( program. Handle ); // Проверка успешности final int[] link. Status = new int[1]; GLES 20. gl. Get. Programiv ( program. Handle, GLES 20. GL_LINK_STATUS, link. Status, 0 ); // В случае неудачи удаляем ШЕЙДЕРНУЮ ПРОГРАММУ if ( link. Status[ 0 ] == 0 ) { GLES 20. gl. Delete. Program ( program. Handle ); program. Handle = 0; } } if ( program. Handle == 0 ) { throw new Runtime. Exception ( "Error creating program. " ); } // Получение идентификаторов переменных внутри шейдерной программы m. MVPMatrix. Handle = GLES 20. gl. Get. Uniform. Location ( program. Handle, "u_MVPMatrix" ); m. Position. Handle = GLES 20. gl. Get. Attrib. Location ( program. Handle, "a_Position" ); m. Color. Handle = GLES 20. gl. Get. Attrib. Location ( program. Handle, "a_Color" ); // Запуск шейдерной программы GLES 20. gl. Use. Program ( program. Handle ); }
@Override public void on. Surface. Changed ( GL 10 gl. Unused, int width, int height ) { // Установка размеров области рисования GLES 20. gl. Viewport ( 0, 0, width, height ); /** Для формирования проекционной матрицы ( тип камеры: перспективная, рыбий глаз, широкоформатная и т. д. ) можно воспользоваться функцией frustum. M класса Matrix, которой нужны следующие параметры*/ final float ratio = ( float ) width / height; final float left = -ratio; final float right = ratio; final float bottom = -1. 0 f; final float top = 1. 0 f; final float near = 1. 0 f; final float far = 10. 0 f; //Получение проекционной матрицы Matrix. frustum. M ( m. Projection. Matrix, 0, left, right, bottom, top, near, far ); }
@Override public void on. Draw. Frame ( GL 10 gl. Unused ) { GLES 20. gl. Clear ( GLES 20. GL_DEPTH_BUFFER_BIT | GLES 20. GL_COLOR_BUFFER_BIT ); // Треугольники делают полный поворот за 10 секунд long time = System. Clock. uptime. Millis () % 10000 L; float angle. In. Degrees = ( 360. 0 f / 10000. 0 f ) * ( ( int ) time ); /**Перед отрисовкой треугольников их надо повернуть. За поворот будет отвечать модельная матрица у каждого треугольника Процесс передачи матриц и данных о треугольнике из приложения в шейдерную программу заключён в методе draw. Triangle*/ Matrix. set. Identity. M ( m. Model. Matrix, 0 ); Matrix. rotate. M ( m. Model. Matrix, 0, angle. In. Degrees, 0. 0 f, 1. 0 f ); draw. Triangle ( m. Triangle 1 Vertices ); //Отрисовка первого треугольника Matrix. set. Identity. M ( m. Model. Matrix, 0 ); Matrix. translate. M ( m. Model. Matrix, 0, 0. 0 f, -1. 0 f, 0. 0 f ); Matrix. rotate. M ( m. Model. Matrix, 0, 90. 0 f, 1. 0 f, 0. 0 f ); Matrix. rotate. M ( m. Model. Matrix, 0, angle. In. Degrees, 0. 0 f, 1. 0 f ); draw. Triangle ( m. Triangle 2 Vertices ); //Отрисовка второго треугольника Matrix. set. Identity. M ( m. Model. Matrix, 0 ); Matrix. translate. M ( m. Model. Matrix, 0, 1. 0 f, 0. 0 f ); Matrix. rotate. M ( m. Model. Matrix, 0, 90. 0 f, 1. 0 f, 0. 0 f ); Matrix. rotate. M ( m. Model. Matrix, 0, angle. In. Degrees, 0. 0 f, 1. 0 f ); draw. Triangle ( m. Triangle 3 Vertices ); //Отрисовка третьего треугольника }
private void draw. Triangle ( final Float. Buffer a. Triangle. Buffer ) { // Передача параметров о положении из приложения в шейдер // Механизм смещений, размеров и идентификаторов в действии a. Triangle. Buffer. position ( m. Position. Offset ); GLES 20. gl. Vertex. Attrib. Pointer ( m. Position. Handle, m. Position. Data. Size, GLES 20. GL_FLOAT, false, m. Stride. Bytes, a. Triangle. Buffer ); GLES 20. gl. Enable. Vertex. Attrib. Array ( m. Position. Handle ); // Передача цветовой информации из приложения в шейдер // Механизм смещений, размеров и идентификаторов в действии a. Triangle. Buffer. position ( m. Color. Offset ); GLES 20. gl. Vertex. Attrib. Pointer ( m. Color. Handle, m. Color. Data. Size, GLES 20. GL_FLOAT, false, m. Stride. Bytes, a. Triangle. Buffer ); GLES 20. gl. Enable. Vertex. Attrib. Array ( m. Color. Handle ); // Расчёт итоговой матрицы преобразований Matrix. multiply. MM ( m. MVPMatrix, 0, m. View. Matrix, 0, m. Model. Matrix, 0 ); Matrix. multiply. MM ( m. MVPMatrix, 0, m. Projection. Matrix, 0, m. MVPMatrix, 0 ); // Передача итоговой матрицы преобразований из приложения в шейдерную // неизменяемую переменную ( uniform ). Механизм идентификаторов GLES 20. gl. Uniform. Matrix 4 fv ( m. MVPMatrix. Handle, 1, false, m. MVPMatrix, 0 ); // Растеризация треугольника GLES 20. gl. Draw. Arrays ( GLES 20. GL_TRIANGLES, 0, 3 ); } }
Результат
opengles.pptx