Статьи

Разработка игр для Android — отображение графических элементов (примитивов) с помощью OpenGL ES

Это вторая часть серии Android OpenGL ES. В предыдущей статье мы рассмотрели, как настроить проект Android для использования представленного представления OpenGL с нашим рендерером. Вы можете использовать проект из этой статьи в качестве шаблона для этого.

Прежде чем мы начнем показывать вещи, мы должны знать несколько основных понятий трехмерного программирования, а также ознакомиться с терминологией. Я базовая геометрия на самом деле.

3D графика происходит в декартовой системе координат .

Это означает, что используемая система координат имеет три измерения. X , Y и Z

Традиционно X идет слева направо, Y снизу вверх, а Z, так сказать, от меня на экран.

В то время как мы имеем дело с отображаемыми объектами (например, робот или автомобиль), OpenGL имеет дело с компонентами этих объектов. Каждый объект создается из примитивов, которые в случае OpenGL являются треугольниками . У каждого треугольника есть лицо и задняя поверхность .

Треугольник определяется 3 точками в пространстве. Точка называется вершиной ( вершины множественного числа).

Следующая диаграмма показывает 2 вершины. А и Б.

вершины

Я нарисовал эту диаграмму, чтобы показать, как мы будем различать 2D и 3D. Вершина определяется своими координатами X, Y и Z. Если мы используем 0 для компонента Z все время, у нас есть 2D. Вы можете видеть, что вершина A является частью плоскости, определенной X и Y. Вершина B находится дальше от координаты Z. Если вы думаете о Z как о линии, перпендикулярной экрану, мы даже не увидим B.

Треугольник называется примитивом . Примитив — это самый простой тип, который OpenGL понимает и может представлять графически.

Это очень просто. 3 вершины определяют треугольник. Есть и другие примитивы, такие как квадроциклы, но мы будем придерживаться основ. Каждая фигура может быть разбита на треугольники.

Мы упоминали лицо треугольника раньше.

Почему это важно? В 3D у вас будут объекты с частями, обращенными к вам, игроком и частями, обращенными от вас. Чтобы сделать рисование эффективным, OpenGL не будет рисовать треугольники, обращенные от вас, так как в этом нет необходимости, потому что они все равно будут скрыты треугольниками, обращенными к вам. Это называется отбором задних поверхностей .

Как OpenGL определяет это? Это определяется порядком вершин при рисовании треугольника. Если ордер против часовой стрелки, то это грань (зеленый треугольник). Порядок по часовой стрелке вершин означает, что это задняя сторона (красный треугольник). Это настройка по умолчанию, но, конечно, ее можно изменить. Следующая диаграмма иллюстрирует это.

Выбраковка

Красный треугольник не будет нарисован.

Создание и рисование треугольника

После того, как вся теория понятна, давайте создадим треугольник и нарисуем его.

Треугольник определяется 3 вершинами. Координаты вершин не измеряются в пикселях. Мы будем использовать float для представления значений, и они будут относительно друг друга.

Если длина стороны равна 1,0f, а длина другой стороны равна 0,5f , то это означает, что вторая сторона равна половине длины первой стороны. Насколько большой он будет отображаться, зависит от того, как настроен видовой экран. Вообразите область просмотра как камеру. Когда мы используем 2D, то это означает, что камера ортогональна к экрану. Если камера находится очень близко, треугольник будет казаться большим, если он далеко, то треугольник будет маленьким.

Давайте создадим класс Triangle .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package net.obviam.opengl;
  
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
  
import javax.microedition.khronos.opengles.GL10;
  
public class Triangle {
  
    private FloatBuffer vertexBuffer;   // buffer holding the vertices
  
    private float vertices[] = {
            -0.5f, -0.5f,  0.0f,        // V1 - first vertex (x,y,z)
             0.5f, -0.5f,  0.0f,        // V2 - second vertex
             0.0f,  0.5f,  0.0f         // V3 - third vertex
    };
  
    public Triangle() {
        // a float has 4 bytes so we allocate for each coordinate 4 bytes
        ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(vertices.length * 4);
        vertexByteBuffer.order(ByteOrder.nativeOrder());
  
        // allocates the memory from the byte buffer
        vertexBuffer = vertexByteBuffer.asFloatBuffer();
  
        // fill the vertexBuffer with the vertices
        vertexBuffer.put(vertices);
  
        // set the cursor position to the beginning of the buffer
        vertexBuffer.position(0);
    }
}

Строка 11 определяет FloatBuffer, который будет содержать вершины для нашего треугольника. Нам нужно использовать пакет java.nio, так как это очень интенсивный ввод.

Массив vertices [] содержит фактические координаты для вершин.

Нарисованный нами треугольник представлен на следующей диаграмме. Мы рассчитываем все от происхождения.

Треугольник

В конструкторе мы инициализируем треугольник из этого массива vertices [] .

Что мы делаем, мы заполняем vertexBuffer координатами и устанавливаем позицию курсора в начало буфера. Мы будем использовать этот буфер в вызове OpenGL для отображения треугольных полос. В настоящее время у нас есть только один.

Давайте посмотрим на рендер. GlRenderer

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package net.obviam.opengl;
  
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
  
import android.opengl.GLU;
import android.opengl.GLSurfaceView.Renderer;
  
public class GlRenderer implements Renderer {
  
    private Triangle    triangle;   // the triangle to be drawn
  
    /** Constructor */
    public GlRenderer() {
        this.triangle = new Triangle();
    }
  
    @Override
    public void onDrawFrame(GL10 gl) {
        // clear Screen and Depth Buffer
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
  
        // Reset the Modelview Matrix
        gl.glLoadIdentity();
  
        // Drawing
        gl.glTranslatef(0.0f, 0.0f, -5.0f);     // move 5 units INTO the screen
                                                // is the same as moving the camera 5 units away
        triangle.draw(gl);                      // Draw the triangle
  
    }
  
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        if(height == 0) {                       //Prevent A Divide By Zero By
            height = 1;                         //Making Height Equal One
        }
  
        gl.glViewport(0, 0, width, height);     //Reset The Current Viewport
        gl.glMatrixMode(GL10.GL_PROJECTION);    //Select The Projection Matrix
        gl.glLoadIdentity();                    //Reset The Projection Matrix
  
        //Calculate The Aspect Ratio Of The Window
        GLU.gluPerspective(gl, 45.0f, (float)width / (float)height, 0.1f, 100.0f);
  
        gl.glMatrixMode(GL10.GL_MODELVIEW);     //Select The Modelview Matrix
        gl.glLoadIdentity();                    //Reset The Modelview Matrix
    }
  
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    }
}

Мы создаем треугольник в конструкторе.

OnDrawFrame (GL10 gl) представляет для нас наибольший интерес.

OpenGL работает с переменными состояния. Каждый метод, который мы вызываем в контексте OpenGL, меняет свое внутреннее состояние.

Следуя методу onDrawFrame, мы видим, что каждый раз, когда рисуется кадр, буферы очищаются, матрица ModelView перезагружается (не беспокойтесь, если вы не понимаете этого в данный момент), камера отодвигается на 5 единиц (мы Здесь мы имеем дело с единицами измерения, а не с пикселями), и вызывается метод draw () треугольника.

С другой стороны, onSurfaceChanged переводит контекст OpenGL между несколькими состояниями. Сначала он устанавливает в окне просмотра текущую ширину и высоту поверхности (так что он работает с состоянием GL_PROJECTION ), затем он переводит состояние в GL_MODELVIEW, чтобы мы могли работать с нашими моделями — в нашем случае с треугольником. Это будет иметь смысл позже, не волнуйтесь.

Давайте проверим метод рисования для треугольника:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public void draw(GL10 gl) {
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
 
    // set the colour for the triangle
    gl.glColor4f(0.0f, 1.0f, 0.0f, 0.5f);
 
    // Point to our vertex buffer
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
 
    // Draw the vertices as triangle strip
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
 
    //Disable the client state before leaving
    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}

Поскольку мы храним координаты вершин треугольника в FloatBuffer, нам нужно разрешить OpenGL читать из него и понимать, что это треугольник. Строка 02 делает именно это.

Линия 05 устанавливает цвет для сущности (в нашем случае треугольник), которая будет нарисована. Обратите внимание, что значения rgb являются числами с плавающей точкой и находятся в диапазоне от 0,0 до 1,0.

gl.glVertexPointer (3, GL10.GL_FLOAT, 0, vertexBuffer); скажет OpenGL использовать vertexBuffer для извлечения вершин из.

Первый параметр (значение = 3) представляет количество вершин в буфере. Второй позволяет OpenGL знать, какой тип данных содержит буфер.

Третий параметр — это смещение в массиве, используемом для вершин. Поскольку мы не храним дополнительные данные, наши вершины следуют друг за другом и смещения нет.

Наконец, последний параметр — наш буфер, содержащий вершины.

gl.glDrawArrays (GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3); говорит OpenGL рисовать треугольные полосы, найденные в ранее предоставленном буфере, начиная с первого элемента. Он также позволяет узнать, сколько существует вершин.

Вот и все. Запустите проект, и вы сможете увидеть свой первый ускоренный треугольник. Именно так:

Загрузите источник здесь (obviam.opengl.p02.tgz) :

Я был вдохновлен кодом из портов nehe android . Чтобы изучить принципы OpenGL, я настоятельно рекомендую эти руководства .

Далее мы увидим, как мы можем создавать базовые 3D-объекты и вращать их. Мы также узнаем, как мы можем использовать текстуры на элементах.

Справка: OpenGL ES Android — отображение графических элементов (примитивов) от нашего партнера по JCG Тамаса Яно из блога « Против зерна ».

Не забудьте проверить нашу новую Android игру ArkDroid (скриншоты ниже) . Ваш отзыв будет более чем полезным!
Статьи по Теме: