OpenGL ES 1. Основы рисования для начинающих
Основным изображаемым объектом в OpenGL является вершина. Вершина-это точка в трёхмерном пространстве. Вершина имеет ряд атрибутов. Главными атрибутами вершины являются ее координаты X, Y, Z. В OpenGL принято, что относительно экрана, на который проецируется изображение, ось X направлена слева направо, ось Y-снизу вверх, ось Z-из глубины экрана к его поверхности. По умолчанию точка с координатами X=0, Y=0, Z=0 находится в центре экрана. Другими атрибутами вершины являются цвет, вектор нормали и координаты текстуры.
Aтрибут координат вершины.
В отличие от "большого" OpenGL в "маленьком" OpenGL ES при рисовании многоугольников не существует возможности передачи в графический конвейер координат каждой точки отдельно. Команда glVertex не включена в данный API. Также отсутствует блок рисования glBegin...glEnd. Единственным способом передачи координат вершин в OpenGL ES являются массивы. Однако, массивы чисел с плавающей точкой в OpenGL ES напрямую тоже не передаются. OpenGL ES в качестве координат вершин принимает только найтивные буферы класса FloatBuffer. Поэтому мы должны предварительно записать координаты вершин в буфер, а затем передать этот буфер в OpenGL ES. На первом этапе главным является правильное вычисление размера буфера. Например, нам требуется нарисовать треугольник. Каждой вершине треугольника присвоены три координаты как числа с плавающей точкой, каждой число с плавающей точкой занимает в памяти 4 байта. Итого, получаем 12 байт на точку. Всего для координат вершин треугольника, который имеет три точки, требуется буфер размером 36 байт. В нашем случае подготовка буфера для координат выглядит следующим образом: FloatBuffer vertexBuffer; Приступим к заполнению буфера координатами вершин. Для этого служит метод put класса FloatBuffer. Рассмотрим это на примере прямоугольного треугольника: // задаем координаты первой вершины треугольника Координаты передаются в буфер последовательно X,Y,Z первой точки, X,Y,Z второй точки, и.т.д. Кроме того, важно соблюдать порядок обхода точек. У треугольника есть две стороны - лицевая и обратная. Лицевой стороной считается та, у которой обход вершин выполнен против часовой стрелки. Обратной стороной та, у которой обход выполнен по часовой стрелке. По умолчанию лицевая сторона будет видна нам, а обратная-нет. На практике я выяснил, что метод put является затратным с точки зрения времени выполнения. Чем реже используется метод put, тем быстрее выполняется отрисовка кадра. Поэтому при большом количестве точек целесообразно сначала завести массив чисел, а потом одной командой put "загнать" его в буфер. Например, для треугольника это выглядит так: float [] trianglecoord={0,0,0, 1,0,0, 0,1,0}; После подготовки буфера координат можно приступать к передаче его OpenGL ES. Однако подготовительные мероприятия на этом не закончены. Мы должны сначала разрешить использование массивов вершин командой gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); Теперь начинаем передачу координат вершин OpenGL. Мы должны сообщить OpenGL, что в данный момент для рисования мы будем использовать именно наш буфер координат vertexBuffer, а не какой-нибудь другой. Для установки текущего буфера координат используется команда: gl.glVertexPointer(3,GL10.GL_FLOAT,0,vertexBuffer); Первый аргумент этой команды GL10.GL_TRIANGLES сообщает OpenGL, что нужно соединить все три точки в треугольник и отобразить его со сплошной заливкой цветами вершин. Второй аргумент 0 - это индекс начального элемента. Третий аргумент 3 - количество индексов, т.е. количество отображаемых точек. У треугольника их три.
Треугольник является базовой фигурой для рисования поверхностей в OpenGL. Почему именно треугольник? Потому что все три точки треугольника лежат в одной плоскости. Для расчета освещения нам понадобится вычислить нормаль к поверхности. Для всех точек, лежащих в одной плоскости, нормаль всегда одинакова и определяется однозначно. Если же взять за основу четырехугольник, то совсем не обязательно, что все четыре точки будут находиться в одной плоскости. Поэтому в общем случае нормаль для четырехугольника не определяется однозначно. Однако, всегда можно вычислить четвертую точку исходя из первых трёх, чтобы все четыре точки лежали в одной плоскости. Из треугольников можно составить любую поверхность если сделать эти треугольники достаточно маленькими, чтобы поверхность отображалась ровной. Даже если поверхность при выводе на экран кажется ступенчатым многогранником можно привести её к ровному виду если включить режим сглаживания цветов командой glShadeModel(GL10.GL_SMOOTH). При этом все же нужно обеспечить на поверхности достаточное количество треугольников, иначе изображение будет размыто и потеряет четкость.
Как составить поверхность из треугольников? Вернемся к описанию команды glDrawArrays. При использование аргумента GL10.GL_TRIANGLES каждая тройка точек рисуется как отдельный треугольник. Например, если у нас имеется буфер координат из 9 точек, пронумерованных от 0 до 8, то при выполнении команды glDrawArrays(GL10.GL_TRIANGLES,0,9) будут нарисованы три треугольника: первый - из точек 0-1-2, второй - из точек 3-4-5, третий-из точек 6-7-8.
Если вместо GL10.GL_TRIANGLES использовать аргумент GL10.GL_TRIANGLE_STRIP будет нарисована сплошная лента из связанных треугольников. При этом порядок рисования треугольников будет такой - сначала рисуется треугольник из точек 0-1-2, затем 2-1-3, затем 2-3-4, затем 4-3-5 и.т.д.
При использовании аргумента GL10.GL_TRIANGLE_FAN получаем веер треугольников с общей точкой 0. Порядок рисования при этом изменится: сначала рисуется треугольник с вершинами 0-1-2, затем 0-2-3, затем 0-3-4 и.т.д.
Цвет как атрибут вершины.
Цвет имеет четыре компоненты-красную, зеленую и синюю и альфа. Яркость каждой компоненты можно регулировать в диапазоне от 0 до 1. Всё многообразие цветов можно представить в виде комбинаций красной, зелёной и синей компоненты различной яркости. Последнюю четвертую компоненту цвета называют Альфой. Альфа - это степень непрозрачности цвета. Если Альфа=0 цвет считается абсолютно прозрачным, т.е. невидимым. Если Альфа=1 цвет считается абсолютно непрозрачным. Цвет-это свойство вершины. Как передать цвет вершин в OpenGL? С помощью тех же буферов. Сначала подготовим буфер для цвета. Каждая вершина имеет четыре значения цвета как числа с плавающей точкой. Каждое число с плавающей точкой занимает в памяти 4 байта. Таким образом, цвет одной вершины занимает в памяти 16 байт. Тогда для треугольника нужно создать буфер размером 16*3=48 байт. ByteBuffer bb = ByteBuffer.allocateDirect(48); // цвет первой вершины - красный float red1=1; float green1=0; float blue1=0; float alpha1=1; // цвет второй вершины - зелёный float red2=0; float green2=1; float blue2=0; float alpha2=1; // цвет третьей вершины - синий float red3=0; float green3=0; float blue3=1; float alpha3=1; // поставим текущую позицию в буфере на начало colorBuffer.position(0); // записываем в буфер цвета первой вершины colorBuffer.put(alpha1); // записываем в буфер цвета второй вершины // записываем в буфер цвета третьей вершины // снова поставим текущую позицию в буфере на начало float [] colorArray={1,0,0,1, 0,1,0,1, 0,0,1,1}; colorBuffer.position(0); colorBuffer.put(colorArray); colorBuffer.position(0); Буфер цветов готов. Далее мы должны разрешить использование массивов цветов командой gl.glColorPointer(4,GL10.GL_FLOAT,0,colorBuffer); Первый аргумент 4 - количество цветов на вершину. Каждая вершина имеет четыре компоненты цвета (красный, зеленый, синий и альфа).
Координаты вектора нормали.
Что такое вектор нормали? Это вектор единичной длины, перпендикулярный к поверхности в данной точке этой поверхности. Вектор нормали, как и всякий другой вектор имеет три проекции на оси координат 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) Далее нужно вычислить длину вектора N и его координаты Nx,Ny,Nz поделить на длину, т.е. нормализовать вектор N. Однако мы не будем заниматься нормализацией, т.к. при этом требуется вычисление квадратного корня, а это операция затратная по времени исполнения. За нас нормализацию выполнит OpenGL. Для этого достаточно выполнить команду glEnable(GL10.GL_NORMALIZE), т.е. разрешить автоматическую нормализацию внутри графического конвейера. Предположим, что у нас имеется треугольник, состоящий из вершин с координатами x1,y1,z1 первой вершины, x2,y2,z2-второй вершины, x3,y3,z3-третьей вершины. Присвоение нормалей вершинам производится аналогично координатам точек. Сначала создадим для треугольника буфер необходимой длины: FloatBuffer normalBuffer; Затем вычислим координаты вектора нормали исходя из координат точек треугольника: float nx=(y2-y1)*(z3-z1)-(y3-y1)*(z2-z1); Запишем координаты вектора нормали в буфер. Поскольку нормаль является одинаковой для всех вершин треугольника повторим запись в буфер три раза: normalBuffer.position(0); Далее включаем использование массивов нормалей: При рисовании полигонов нужно обязательно соблюдать правило: порядок обхода вершин при записи в буферы должен быть одинаков для координат вершин, цветов, векторов нормалей и координат текстур. Как уже отмечалось, вектор нормали нужен для правильного расчета освещения поверхности. Освещение, а также текстуры будут рассмотрены отдельно в следующих статьях.
|