Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OpenGL ES 1и2.docx
Скачиваний:
8
Добавлен:
01.07.2025
Размер:
4.8 Mб
Скачать

Координаты вектора нормали.

Что такое вектор нормали ? Это вектор единичной длины, перпендикулярный к поверхности в данной точке этой поверхности. Вектор нормали, как и всякий другой вектор имеет три проекции на оси координат X,Y,Z, которые называют координатами вектора. Координаты вектора нормали присваиваются каждой вершине отдельно и являются её атрибутом. Вектор нормали очень важен для правильного расчета освещения поверхности, т.к. определяет направление отражённого света. Как можно рассчитать вектор нормали ? Для расчета  нормали необходимо иметь три точки на поверхности. Из трёх точек можно составить два вектора, которые всегда будут лежать в одной плоскости. Проведем из точки 1 в точку 2 вектор A, из точки 1 в точку 3 вектор B. Вектор N, перпендикулярный A и B рассчитывается как векторное произведение N=[AxB]:

Nx=Ay*Bz-By*Az=(y2-y1)*(z3-z1)-(y3-y1)*(z2-z1) Ny=Bx*Az-Ax*Bz=(x3-x1)*(z2-z1)-(x2-x1)*(z3-z1) Nz=Ax*By-Bx*Ay=(x2-x1)*(y3-y1)-(x3-x1)*(y2-y1)

Далее нужно вычислить длину вектора N и его координаты Nx,Ny,Nz поделить на длину, т.е. нормализовать вектор N. Однако мы не будем заниматься нормализацией, т.к. при этом требуется вычисление квадратного корня, а это операция затратная по времени исполнения.

За нас нормализацию выполнит OpenGL. Для этого достаточно выполнить команду glEnable(GL10.GL_NORMALIZE), т.е. разрешить автоматическую нормализацию внутри графического конвейера. 

Предположим, что у нас имеется треугольник, состоящий из вершин с координатами  x1,y1,z1  первой вершины, x2,y2,z2-второй вершины, x3,y3,z3-третьей вершины. Присвоение нормалей вершинам производится аналогично координатам точек. Сначала создадим для треугольника буфер необходимой длины:

FloatBuffer normalBuffer; ByteBuffer bb = ByteBuffer.allocateDirect(36); bb.order(ByteOrder.nativeOrder()); normalBuffer = bb.asFloatBuffer();

Затем вычислим координаты вектора нормали исходя из координат точек треугольника:

float nx=(y2-y1)*(z3-z1)-(y3-y1)*(z2-z1); float ny=(x3-x1)*(z2-z1)-(x2-x1)*(z3-z1); float nz=(x2-x1)*(y3-y1)-(x3-x1)*(y2-y1);

Запишем  координаты вектора нормали в буфер. Поскольку нормаль является одинаковой для всех вершин треугольника повторим запись в буфер три раза:

normalBuffer.position(0); for (int i=1;i<4;i++){           normalBuffer.put(nx);           normalBuffer.put(ny);           normalBuffer.put(nz); } normalBuffer.position(0);

Далее включаем использование массивов нормалей: gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); И передаем наш буфер нормалей normalBuffer в OpenGL: gl.glNormalPointer(GL10.GL_FLOAT,0,normalBuffer);

При рисовании полигонов нужно обязательно соблюдать правило: порядок обхода вершин при записи в буферы должен быть одинаков для координат вершин, цветов, векторов нормалей и координат текстур.

Как уже отмечалось, вектор нормали нужен для правильного расчета освещения поверхности. Освещение, а также текстуры будут рассмотрены отдельно в следующих статьях.

1.3 OpenGl es 1. Применение индексов при обходе вершин

Применение команды glDrawArrays обязывает нас упорядочивать массивы координат вершин, нормалей и текстур в строго определённом порядке в соответствии с выбранным правилом обхода точек GL_TRIANGLES, GL_TRIANGLE_STRIP или GL_TRIANGLE_FAN. Это не всегда бывает удобно. Поэтому в OpenGL существует возможность разделить данные вершин и правила обхода по разным массивам. Специально подготовленный массив, в котором хранится порядок обхода вершин, называют массивом индексов. Рассмотрим применение индексов на простом примере.  Допустим, что у нас имеется два ряда горизонтальных точек. Верхний ряд состоит из точек 0,1,2,3, нижний ряд из точек 4,5,6,7. Обозначим координаты вершин точки 0 как x0,y0,z0, точки 1 - x1,y1,z1, точки 2 - x2,y2,z2 и так далее до точки 7. Предположим, что нам удобно хранить координаты вершин в следующем порядке - 0,1,2,3,4,5,6,7. Сформируем массив координат и передадим его в буфер:

float [] vertexArray={x0,y0,z0, x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4, x5,y5,z5, x6,y6,z6, x7,y7,z7};

ByteBuffer b1 = ByteBuffer.allocateDirect(12*8); b1.order(ByteOrder.nativeOrder()); vertexBuffer = b1.asFloatBuffer(); vertexBuffer.put(vertexArray); vertexBuffer.position(0);

Для примера выберем правило GL_TRIANGLE_STRIP, в соответствии которым лента треугольников формируется в следующем порядке: сначала рисуется треугольник из точек 0-4-1, далее 1-4-5, 1-5-2, 2-5-6,  2-6-3, 3-6-7:

Массив индексов представляет из себя последовательность номеров точек, понятную для выбранного правила обхода. Поскольку мы используем правило GL_TRIANGLE_STRIP, которое само добавляет промежуточные точки, из массива индексов нужно выбросить лишние вершины, т.е. вместо порядка 0,4,1, 1,4,5, 1,5,2, 2,5,6, 2,6,3, 3,6,7  необходимо указать номера точек в следующей последовательности: 0,4,1,5,2,6,3,7. 

Формат номеров точек в индексном массиве может быть только byte или short, целые числа типа int для индексов в OpenGL ES не используются. Определимся с размером буфера индексов. Каждый индекс как число типа short занимает в памяти два байта. Всего у нас 8 точек. Поэтому буфер должен быть размером 16 байт. Сформируем массив индексов и передадим его в буфер: 

short [] indexArray={0,4,1,5,2,6,3,7};

ByteBuffer b2 = ByteBuffer.allocateDirect(16); b2.order(ByteOrder.nativeOrder()); ShortBuffer indexBuffer = b2.asShortBuffer();

indexBuffer.position(0); indexBuffer.put(indexArray); indexBuffer.position(0);

Далее мы должны разрешить использование массивов координат вершин и передать их в OpenGL:

gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); // указываем программе, что буфер с именем vertexBuffer является буфером координат вершин gl.glVertexPointer(3,GL10.GL_FLOAT,0,vertexBuffer);

Для рисования с использованием массивов индексов команда  glDrawArrays не используется, вместо неё применяется другая команда glDrawElements:

gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, 8, 

           GL10.GL_UNSIGNED_SHORT,indexBuffer);

В качестве первого аргумента задается выбранное правило, в нашем примере это GL_TRIANGLE_STRIP. Второй аргумент - это количество элементов в индексном массиве, т.е. восемь. Третий аргумент - формат чисел индексов, в нашем случае short. Последний аргумент буфер, в который записан индексный массив.

Если вместо правила GL_TRIANGLE_STRIP для рисования ленты использовать GL_TRIANGLES, которое рисует каждую тройку вершин как отдельный треугольник, то мы должны указывать в массиве индексов все промежуточные точки. В этом случае количество индексов возрастет до 18 значений:

short [] indexArray={ 0,4,1, 1,4,5, 1,5,2, 2,5,6, 2,6,3, 3,6,7 };

ByteBuffer b2 = ByteBuffer.allocateDirect(2*18); b2.order(ByteOrder.nativeOrder());ShortBuffer indexBuffer = b2.asShortBuffer();

indexBuffer.position(0); indexBuffer.put(indexArray);

indexBuffer.position(0);

и соответственно изменится формат команды glDrawElements:

gl.glDrawElements(GL10.GL_TRIANGLES, 18, 

           GL10.GL_UNSIGNED_SHORT,indexBuffer);   

Практика показывает, что использование индексов заметно увеличивает  скорость отрисовки кадров при большом количестве вершин. Поэтому, по возможности, нужно заменять команду  glDrawArrays на glDrawElements.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]