Заметки об OpenGL ES в Android

Март 26th, 2012 § 0 comments

Сегодня я хотел бы рассказать о некоторых моментах в работе с OpenGL ES в Android информации по этому поводу не так уж много и большая её часть англоязычная. Первое что хотел бы сразу отметить, старайтесь тестировать на устройстве т.к. очень часто бывает, что отображается на эмуляторе, то может не отображаться на устройстве и наоборот. Первое что мы должны сделать, это имплементировать класс GLSurfaceView и интерфейс GLSurfaceView.Renderer. Базовый код будет выглядеть примерно так.

Класс Run унаследованный от Activity


import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class Run extends Activity {

private SurfaceView glSurfaceView;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
glSurfaceView = new SurfaceView(this);
setContentView(glSurfaceView);
}

/**
* Remember to resume the glSurface
*/
@Override
protected void onResume() {
super.onResume();
glSurfaceView.onResume();
}

/**
* Also pause the glSurface
*/
@Override
protected void onPause() {
super.onPause();
glSurfaceView.onPause();
}

}

Класс SurfaceView, унаследованный от GLSurfaceView


import android.content.Context;
import android.opengl.GLSurfaceView;
import android.view.*;

public class SurfaceView extends GLSurfaceView {
private GlRenderer renderer;
public SurfaceView(Context context) {
super(context);
renderer = new GlRenderer(this.getContext());
this.setRenderer(renderer);
}
}

Класс GlRenderer, имплементирующий интерфейс GLSurafaceView.Renderer


import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.opengl.GLU;
import android.opengl.GLSurfaceView.Renderer;

public class GlRenderer implements Renderer {

private Context     context;
public GlRenderer(Context context)
{
this.context = context;
}

@Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
}

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
if(height == 0) {
height = 1;
}
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glViewport(0, 0, width, height);
gl.glLoadIdentity();
gl.glOrthof(0.0f, width , 0.0f, height, -1.0f, 1.0f);

}

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
}
}

Вот ваш каркас для ваших приложений Android с использование OpenGL ES, если все правильно сделано, то вы получите экран, закрашенный серым цветом. Основная работа происходит в GlRenderer, как видите. Сначала вызывается функция onSurfaceCreated (это происходит каждый раз, когда поверхность экрана создается или пересоздается) в ней мы вызываем функцию gl.glClearColor, это функция устанавливает цвет очистки экрана в формате RGBA.  Далее вызывается функция onSurfaceChanged (это происходит каждый раз когда, размер экрана меняется, к примеру, вы перевернули устройство, и оно перешло из портретного в лендскейп). Первая проверка на то что бы не произошло деление на ноль, здесь оно не используется, может потребоваться позже. Далее мы выбираем, с какой матрицей работать, активная матрица над которой и будут совершаться преобразования в данном случае GL10.GL_PROJECTION отвечает за всю сцену. glViewport устанавливает наш вьюпорт, задаем его во весь экран.

Одна из особенностей OpenGL, которую стоит запомнить точка (0;0) во вьюпорте это нижний левый угол (а не верхний как мы привыкли при рисовании на Canvas). Функция glLoadIdentity устанавливает единичную матрицу для текущей активной матрицы преобразований. Последняя функция, что вызывается это glOrthof в ней собственно и задается тот способ, как мы будем смотреть на нашу сцену. glOrtho используется для отображения двухмерной графики это все еще 3D, но только глубина (координата z) равно по умолчанию 0.0. Здесь задается формат нашей координатной сетки, он может быть любой от -1.0 до 1.0 по x и столько же по y или же, как  в примере от (0;width) по x и (0;height) по y, так же задан наш план видимости по z вы спросите, почему его не установить в (0;0) этого делать не рекомендуют. В функции onDrawFrame вызывается glClear, эти два параметра указывают очистить буфер экрана и буфер глубины. Вот это базовое приложения я постарался разобрать основные моменты код примеров для Eclipse вы можете скачать с bitbucket.org ссылку приложу в конце.

Следующим шагом будет вывод треугольника на экран. Для этого создадим новый класс Triangle


package com.gingray.test;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.opengles.GL10;

public class Triangle {
private FloatBuffer vertexBuffer;
private float[] vertices = {
0.0f,0.0f,0.0f,
319.0f,0.0f,0.0f,
159.0f,479.0f,0.0f};
public Triangle()
{
ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(vertices.length*4);
vertexByteBuffer.order(ByteOrder.nativeOrder());
vertexBuffer = vertexByteBuffer.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
}
public void draw(GL10 gl)
{
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glColor4f(0.0f, 0.5f, 0.5f, 1.0f);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length/3);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}
}

Тут появляется много новых основополагающих вещей, разберем их подробнее. Я не буду приводить код всего проекта, т.к. основным отличием будет всего лишь то, что я задам координаты ортогональной проекции статически, а именно от (0;320) по x (0;480) по y OpenGL автоматически переведет их в физические координаты, об этом нам беспокоится не надо. Последним отличием будет то, что в onDrawFrame будет вызываться код triangle.draw(gl);

Вернемся к нашему классу Triangle, в нем используется java.nio это позволяет нам выделять память не в куче виртуальной машины, а в куче физического устройства. Переменная vertexBuffer собственно и будет хранить точки для рисования. Переменная vertices хранит эти точки для того что бы преобразовать их в нужный нам формат. Как видите, каждая точка задается тремя переменными типа float (x,y,z)  В конструкторе, что происходит мы, создаем ByteBuffer вызываем allocateDirect (выделяет целый блок памяти заданного размера) для каждого значения vertices требуется 4 байта. Выставляем порядок ByteOrder.nativeOrder() (в зависимости от типа платформы будет BIG_ENDIAN или LITTLE_ENDIAN это способ хранения чисел в памяти). Далее присваиваем нашему vertexBuffer’у значения и кладем в него массив после этого устанавливаем позицию в буфере в начало. В функции draw мы вызываем gl.glEnableClientState(GL10.GL_VERTEX_ARRAY) эта команда говорит OpenGL ES, что мы будем рисовать то что имеет координаты (немного бессмысленно т.к. нельзя нарисовать то что координат не имеет). Следующая команда gl.glColor4f устанавливает цвет рисования в формате RGBA. glVertexPointer мы говорим, где находятся наши координаты, первый аргументы функции указывает, сколько чисел представляют вершину в данном случае ставим 3 (x;y;z) если бы наш массив состоял из координат (x;y) то мы бы поставили 2. Следующим параметром идет тип переменной, который мы используем для хранения координат, в данном случае это float. Следующий параметр говорит как удаленны векторы в памяти друг от друга, в данном случае они располагаются последовательно, так что ставим 0 и последний параметр это и будет наш буфер. Наконец мы вызываем gl.glDrawArrays первый параметр указывает, какой примитив мы собираемся нарисовать, вторым параметром задается смещение (он нужен когда мы хотим нарисовать не все примитивы, а только подмножество) и третьим параметром задается количество вершин, которые мы хотим нарисовать. Последняя функция возвращает режим рисования в прежнее состояние.

Тут показано два примитивных примера для работы с OpenGL ES в Android, не рассмотренными остались еще масса моментов, таких как наложение текстур, анимация, освещение и прочее, но это в следующих заметках. Привожу ссылку на bitbucket где выложены рабочие примеры для Eclipse тут

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

Оставить комментарий

Ваш email не будет опубликован. Обязательные поля отмечены *

Вы можете использовать это HTMLтеги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Смотреть фильмы онлайн