logo
Android

3.1 Вывод треугольника с помощью OpenGl es

Базовой фигурой 3D графики является треугольник, т.к. любой трёхмерный объект можно представить в виде набора треугольников. Поэтому рассмотрим простейший пример о том, как вывести треугольник при помощи OpenGL ES на Android.

Создаём в Eclipse новый Android проект. Назовём его TriangleDemo. В поля Package name, Activity name и Application name впишите com.goodroid.triangledemo, TriangleDemo, и TriangleDemo соответственно. Для примера нам понадобится файл GLSufaceView.java из Android SDK (лежит он в папке samples\ApiDemos\src\com\example\android\apis\graphics).

Создадим в нашем проекте пакет с именем com.example.android.apis.graphics и скопируем в него GLSufaceView.java. Структура проекта показана на рисунке 5.

Рис. 5 Структура проекта TriangleDemo

Класс GLSufaceView позволяет нам не задумываться о том, как инициализировать OpenGL, он делает это сам. Нам остаётся только написать класс, имплементирующий интерфейс GLSurfaceView.Renderer, который будет заниматься отрисовкой или рендерингом (кому как больше нравится). Назовём его Renderer.

Для этого, в Eclipse, надо нажать правой кнопкой мыши в окне Package Explorer на пакете com.goodroid.triangledemo и в открывшемся меню выбрать New - Class. Откроется помощник создания нового класса. В поле Name напишем Renderer, а в поле Interfaces добавим интерфейс com.example.android.apis.graphics.GLSurfaceView.Renderer и нажмем кнопку Finish. После этих манипуляций Eclipse создаст наш Renderer с пустыми методами.

Код класса Renderer:

package com.goodroid.triangledemo;

import javax.microedition.khronos.opengles.GL10;

public class Renderer implements

com.example.android.apis.graphics.GLSurfaceView.Renderer {

@Override

public void drawFrame(GL10 gl) {

// TODO Auto-generated method stub

}

@Override

public int[] getConfigSpec() {

// TODO Auto-generated method stub

return null;

}

@Override

public void sizeChanged(GL10 gl, int width, int height) {

// TODO Auto-generated method stub

}

@Override

public void surfaceCreated(GL10 gl) {

// TODO Auto-generated method stub

}

}

О назначении пустых методов будет указано чуть позже. Теперь откройте файл TriangleDemo.java.

import com.example.android.apis.graphics.GLSurfaceView;

Добавим новую переменную

private GLSurfaceView glView;

А в методе onCreate заменим строчку

setContentView(R.layout.main);

следующим кодом:

glView = new GLSurfaceView(this);

glView.setRenderer(new Renderer());

setContentView(glView);

Откроем файл Renderer.java. И сделаем некоторые изменения.

Первым делом при инициализации OpenGL ES контекста вызывается метод getConfigSpec().Он задаёт конфигурацию OpenGL ES, которая представляет из себя список (массив типа int) пар атрибут - значение и завершается атрибутом EGL_NONE. Если какой-то атрибут не указан в списке, то его значение принимается равным по умолчанию.

Для нашего примера будет достаточно конфигурации по умолчанию, но вернуть атрибут EGL_NONE мы всё равно должны. Поэтому, заменим пустышку getConfigSpec() следующим кодом:

public int[] getConfigSpec() {

int[] configSpec = {

EGL10.EGL_NONE

};

return configSpec;

}

Следующим по очереди вызывается метод surfaceCreated(GL10 gl).

Вызывается он при создании поверхности. Здесь можно сделать инициализацию каких-нибудь данных. Например, загрузить текстуры. Нам пока этого не требуется, поэтому оставим его пустым.

Потом вызывается метод sizeChanged(GL10 gl, int width, int height). Как видно из названия метода, вызывается он и при изменении размера экрана, например, при повороте.

Добавим в него код настройки области просмотра (т.е. viewport) и типа проекции:

public void sizeChanged(GL10 gl, int width, int height) {

gl.glViewport(0, 0, width, height);

 

float ratio = (float) width / height;

gl.glMatrixMode(GL10.GL_PROJECTION);

gl.glLoadIdentity();

gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);

}

Пусть координаты вершин треугольника мы записали в массив rectangle.

Java реализация OpenGL ES работает с буферами из пакета java.nio. Поэтому массив rectangle надо предварительно поместить в буфер соответствующего типа (в нашем случае это FloatBuffer).

Для этого добавим в раздел импорта строчку:

import java.nio.*;

а в код класса Renderer переменную

FloatBuffer triangleBuffer;

и конструктор следующего содержания:

public Renderer() {

ByteBuffer bb = ByteBuffer.allocateDirect(36);

bb.order(ByteOrder.nativeOrder());

triangleBuffer = bb.asFloatBuffer();

triangleBuffer.put(triangle);

triangleBuffer.position(0);

}

Строка

ByteBuffer bb = ByteBuffer.allocateDirect(36);

Создаёт direct-буфер или прямой буфер размером 36 байт (3 вершины по 3 координаты по 4 байта каждая). Он создаётся в системной куче. Делается это для того чтобы сборщик мусора не мог его никуда переместить.

bb.order(ByteOrder.nativeOrder());

Для многобайтовых типов данных таких как: short, int, float, задаёт порядок байт в буфере как в системе.

triangleBuffer = bb.asFloatBuffer();

Метод asFloatBuffer создаёт экземпляр класса FloatBuffer, ссылающийся на данные из байтового буфера. Т.е. дальше мы будем работать с байтовым буфером как с вещественным буфером.

triangleBuffer.put(triangle);

Запишем наш треугольник в буфер.

triangleBuffer.position(0);

Установим указатель на начало буфера.

Подготовка окончена, пора приступать к рисованию.

За рисование отвечает метод drawFrame(GL10 gl).

public void drawFrame(GL10 gl) {

gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

gl.glMatrixMode(GL10.GL_MODELVIEW);

gl.glLoadIdentity();

gl.glTranslatef(0, 0, -3.0f);

 

gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

 

gl.glVertexPointer(3, GL10.GL_FLOAT, 0, triangleBuffer);

gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);

}

Строка

gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

отвечает за очистку экрана. Параметр GL10.GL_COLOR_BUFFER_BIT означает, что нужно очистить буфер цвета.

gl.glMatrixMode(GL10.GL_MODELVIEW);

говорит о том, что мы будем работать с матрицей модели.

gl.glTranslatef(0, 0, -3.0f);

перемещает наш треугольник по оси Z в глубь экрана в точку (0, 0, -3). Если этого не сделать, то мы бы не увидели треугольник.

gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

включает режим рисования массива вершин.

gl.glVertexPointer(3, GL10.GL_FLOAT, 0, triangleBuffer);

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

gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);

эта строка наиболее интересна, т.к. она выводит наш треугольник на экран. Параметр GL10.GL_TRIANGLES задаёт способ вывода вершин из массива. Вывод начнётся с вершины 0. Будет выведено 3 вершины.

Вот собственно и всё. Можно запускать программу. Результат работы программы представлен на рисунке 6.

Рис. 6. Результат работы программы