7 декабря 2012 г.

OpenGL ES 2.0. Урок 4. Гладкие поверхности. Полигональная сетка.

В этом уроке мы рассмотрим рисование гладких 3D-функций с помощью полигональной сетки.  Пусть наша сетка будет лежать в плоскости XZ, а значение Y будет вычисляться как функция от X и Z, т.е. y=f(x,z). В узлах сетки будут находиться вершины. Обозначим порядковый номер  узла сетки вдоль оси X как i, а вдоль оси Z как j. Номера узлов могут меняться от нуля до imax или jmax соответственно.

13 октября 2012 г.

Мои разработки. Живые обои "Волны на поверхности"

Живые обои "Волны на поверхности" (английский вариант Surface Wave) - это трехмерная математическая модель виртуальной жидкости. Молекулы на поверхности взаимодействуют друг с другом через силу поверхностного натяжения. Прикосновение к экрану аппарата выводит жидкость из состояния равновесия и приводит к образованию поверхностных волн, которые плавно распространяются во все стороны, накладываются друг на друга и отражаются от границ экрана, постепенно затухая. Чтобы поверхность воды не успокаивалась на экран случайным образом падают капли. Посмотрите как это выглядит:


В приложении имеется много разных настроек, которые позволяют Вам управлять параметрами поверхности, выбирать эффекты и изображения. Основным элементом меню настроек является выбор эффекта. Поэтому сначала в программе нужно выбрать эффект. Настройки каждого выбранного эффекта индивидуальны и сохраняются независимо от других эффектов.

OpenGL ES 2.0. Урок третий-Двумерные текстуры

Загрузка текстуры из файла.
Создадим класс, который загружает двумерную текстуру из графического файла. Нам достаточно разместить какой-нибудь графический файл в папку ресурсов (например, в res/drawable-hdpi), чтобы система присвоила ему идентификатор. Идентификатор ресурса - это целое число, которое является ссылкой на данный ресурс. Идентификаторы ресурсов хранятся в  файле R.java, который система создает автоматически. Если известно имя графического файла ресурса, например picture.png , можно получить его идентификатор как R.drawable.picture.
Итак, приступим к созданию класса. Я думаю, что из комментариев будет все понятно.
public class Texture {
        //создаем поле для хранения имени текстуры
        private int name;
        // конструктор двумерной текстуры из ресурса
        //передаем в качестве аргументов контекст 
        //и идентификатор ресурса графического файла
        public Texture(Context context, int idpicture) {
                //создаем пустой массив из одного элемента
                //в этот массив OpenGL ES запишет свободный номер текстуры, 
                // который называют именем текстуры
                int []names = new int[1];
                // получаем свободное имя текстуры, которое будет записано в names[0]
                GLES20.glGenTextures(1, names, 0);
                //запомним имя текстуры в локальном поле класса
                name = names[0];
                //теперь мы можем обращаться к текстуре по ее имени name
                //устанавливаем режим выравнивания по байту
                GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
                //делаем текстуру с именем name текущей
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, name);
                //устанавливаем фильтры текстуры
                GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
                        GLES20.GL_TEXTURE_MIN_FILTER, 
                        GLES20.GL_LINEAR_MIPMAP_LINEAR);
                GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
                        GLES20.GL_TEXTURE_MAG_FILTER,
                        GLES20.GL_LINEAR);
               //устанавливаем режим повтора изображения 
                //если координаты текстуры вышли за пределы от 0 до 1
                GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
                        GLES20.GL_TEXTURE_WRAP_S,
                        GLES20.GL_REPEAT);
                GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
                        GLES20.GL_TEXTURE_WRAP_T,
                        GLES20.GL_REPEAT);
                // загружаем картинку в Bitmap из ресурса
                Bitmap bitmap = 
                        BitmapFactory.decodeResource(context.getResources(), idpicture);
                //переписываем Bitmap в память видеокарты
                GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
                // удаляем Bitmap из памяти, т.к. картинка уже переписана в видеопамять
                bitmap.recycle();
                // Важный момент ! 
                // Создавать мипмапы нужно только
                // после загрузки текстуры в видеопамять
                GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
        }// конец конструктора двумерной текстуры

        //нам будет нужен метод, который возвращает имя текстуры
        public int getName() {
                return name;
        }
}// конец класса
Создать текстурный объект mTexture для картинки picture.png можно следующим образом:
Texture mTexture=new Texture(context, R.drawable.picture);
а получить его имя можно так:
int  mTextureName=mTexture.getName();

8 октября 2012 г.

OpenGL ES 2.0. Урок второй-Освещение в шейдере

В отличие от OpenGL ES 1 в OpenGL ES 2.0 не предусмотрено специальных команд (glLightfv и glMaterialfv) для управления освещением и материалами. Вместе с тем,  OpenGL ES 2.0 обладает более широкими возможностями по сравнению с OpenGL ES 1.
OpenGL ES 1 автоматически рассчитывает освещение вершин и далее интерполирует их освещенность в цвет пикселя. Вмешаться в процесс расчета цвета пикселя в классическом OpenGL ES 1 мы не можем. Поэтому, чтобы получить плавные переходы в освещенности объекта нужно увеличивать количество вершин. В OpenGL ES 2.0 все проще. Расчет освещенности можно перенести в фрагментный шейдер, в котором все операции производятся с конкретным пикселем.
Поговорим немного об видах освещения.
Фоновое (ambient) освещение.
Фоновое освещение освещает объекты одинаково со всех сторон. Оно не зависит от положения источника света и глаза наблюдателя. Задается константой.
Диффузное (diffuse) или рассеянное освещение.
Яркость объекта, освещенного диффузным светом, зависит от положения объекта и от положения источника света. Диффузный свет отражается от поверхности одинаково во все  стороны. Поэтому положение глаза наблюдателя на диффузное освещение не влияет. Яркость диффузного освещения определяют по фактору Ламберта. Вычисляется косинус угла между вектором нормали и вектором, указывающим из точки на источник света. Чем этот угол меньше, тем ярче освещена точка. Если угол = 0, получаем максимальную яркость. Если угол=90 градусов - яркость будет равна нулю. Покажу это на рисунке: 

3 октября 2012 г.

OpenGL ES 2.0. Урок первый-Шейдеры

Введение в шейдеры.
OpenGL ES 2.0 использует шейдеры языка GLSL. Шейдеры бывают двух типов - вершинный и фрагментный. В вершинном шейдере производятся расчеты над вершинами, а в фрагментном - над пикселями. Рассмотрим простой вершинный шейдер:
uniform mat4 u_modelViewProjectionMatrix;
attribute vec3 a_vertex;
attribute vec3 a_normal;
attribute vec4 a_color;
varying vec3 v_vertex;
varying vec3 v_normal;
varying vec4 v_color;
void main() {
         v_vertex=a_vertex;
         vec3 n_normal=normalize(a_normal);
         v_normal=n_normal;
         v_color=a_color;
        gl_Position = u_modelViewProjectionMatrix * vec4(a_vertex,1.0);
и соответствующий ему фрагментный шейдер:
precision mediump float;
varying vec3 v_vertex;
varying vec3 v_normal;
varying vec4 v_color;
void main() {
        vec3 n_normal=normalize(v_normal);
        gl_FragColor = v_color;
}
Теперь разберем по косточкам, какие процессы происходят в этих шейдерах.

6 апреля 2012 г.

OpenGL ES 1. Как наложить несколько текстур на один полигон

Иногда бывает необходимо нанести последовательно несколько текстур на один полигон в виде слоёв с регулируемой прозрачностью. Делается это с помощью смешивания цветов различных текстур. Для решения этой задачи нужно определить несколько материалов с различным значением альфа компоненты отраженного света и загрузить в память несколько текстур. Рассмотрим пример. Предположим, у нас загружены в память три текстуры с именами name1, name2, name3. Изображение первой текстуры представляет из себя красные вертикальные полосы, второй-зелёные горизонтальные полосы, а третьей - синий круг.

5 апреля 2012 г.

OpenGL ES 1. Текстуры в движении

До сих пор мы задавали для вершин полигонов фиксированные координаты текстур. В результате, текстура была как-бы "приклеена" к поверхности полигона. Однако, ничто нам не мешает изменять координаты текстуры вершин при смене кадров. Например, мы хотим чтобы текстура протекала по поверхности полигона. Как это можно реализовать ?
Двумерная текстура имеет две координаты S и T, аналогичные координатам X и Y на плоскости. S-это горизонтальная координата, T-вертикальная. Следует отметить, что разделение на вертикальную и горизонтальную координату условно и зависит какую из вершин полигона мы примем за начало текстурных координат. Кроме того, координаты текстур нормированы, т.е. находятся в диапазоне от 0 до 1. Достаточно с каждым новым кадром добавлять к координате S или T величину, намного меньше единицы, и увидим как текстура "поплывет". При этом рано или поздно текстурная координата выйдет за пределы единицы. Что при этом произойдет.

OpenGL ES 1. Зеркальное отражение при помощи кубических текстур

Кубическая текстура представляет из себя шесть фотографий окружающего пространства: "Правая сторона", "Левая сторона", "Верх", "Низ", "Передняя сторона", "Задняя сторона", которые должны быть подготовлены заранее. Всем шести картинкам присваивается одно имя текстуры и обращение идет к текстуре производится по данному имени. Для создания эффекта зеркального отражения используется автоматическая генерация текстур при помощи команды glTexGeni с параметром GL_REFLECTION_MAP. При этом каждой вершине полигона присваивается координата текстуры, соответствующая зеркальному отражению от поверхности полигона.
Автоматическая генерация текстур несмотря на высокую скорость расчета текстурных координат имеет свои недостатки. В частности, при перемещении камеры зеркальное отражение перемещается в месте с ней и расплывается. Чтобы избавиться от таких нежелательных эффектов, нужно изменять матрицу текстуры. Матрица текстуры - это аналог матрицы модели-вида, применяемый не к координатам вершин X, Y, Z, а координатам текстур S, T, R. Все операции, которые можно применить к матрице модели-вида, можно применять и к матрице текстуры, но действовать они будут на текстурные координаты. По умолчанию матрица текстуры равна единичной матрице. Чтобы зеркальное отражение выглядело корректно нужно компенсировать поворот и перемещение камеры. Для этого записываем в массив текущую матрицу модели-вида с учетом все поворотов и перемещений камеры, получаем из нее обратную матрицу модели-вида, а затем зануляем у обратной матрицы модели-вида элементы, связанные с перемещением. Результат всех этих манипуляций записываем в матрицу текстуры. Перед рисованием следующего полигона матрицу текстуры нужно снова превратить в единичную.

OpenGL ES 1. Как сделать текстуры прозрачными

В этой статье, основанной на предыдущем примере показано как сделать грани пирамиды полупрозрачными, рассмотрен вопрос о сортировке полигонов для правильного отображения эффекта прозрачности. Для этих целей будем использовать режим смешивания цветов при помощи альфа-компоненты цвета материла. Всего у цвета есть четыре компонента: красный, зеленый, синий и альфа. Яркость каждой компоненты изменяется в диапазоне от 0 до 1. Альфа компонент-это четвертый компонент цвета. Он определяет степень непрозрачности объекта. 
Альфа=0 - полностью прозрачный объект. Альфа=1 - полностью непрозрачный объект.

OpenGL ES 1. Текстурирование на примере

В данном примере показано как создать текстурированный многогранник в Android OpenGL ES на примере пирамид. Применен объектно-ориентированный подход. Пирамида разбита на отдельные самостоятельные объекты (полигоны) - треугольники и прямоугольник, внутри которых содержатся собственные свойства полигонов - координаты вершин и текстур, а также определен метод позволяющий вращать полигоны в пространстве.

4 апреля 2012 г.

OpenGL ES 1. Двумерные текстуры

Двумерная текстура представляет из себя прямоугольное графическое изображение, загруженное в память OpenGL. Текстура используется для нанесения изображения на поверхность, составленную из полигонов (многоугольников). Двумерная текстура имеет две координаты, горизонтальную координату принято обозначать буквой S, вертикальную - буквой T. Координаты текстуры нормированы на единицу, т.е. принимают значения от 0 до 1. Левый верхний угол картинки имеет координаты S=0, T=0, правый нижний угол - S=1, T=1:

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

3 апреля 2012 г.

OpenGL ES 1. Освещение и материалы

Освещение очень важно для правильного отображения трёхмерных объектов. Например, без освещения сфера будет выглядеть как круг, а цилиндр как прямоугольник. Чтобы использовать освещение нужно обязательно включить его командой glEnable(GL10.GL_LIGHTING), иначе все источники света будут игнорированы. В OpenGL ES допускается одновременно использовать восемь источников света, которые пронумерованы специальными параметрами состояния GL10.GL_LIGHT0, GL10.GL_LIGHT1, GL10.GL_LIGHT2, и.т.д. до GL10.GL_LIGHT7. Чтобы включить источник света нужно вызвать команду glEnable(номер источника света). Например, чтобы включить первый источник света вводим glEnable(GL10.GL_LIGHT1). По умолчанию включен источник света GL10.GL_LIGHT0. Аналогично можно выключить источник света командой glDisable(номер источника света). Каждый источник света имеет свои атрибуты, которые настраиваются независимо от других источников.

2 апреля 2012 г.

OpenGL ES 1. Применение индексов при обходе вершин

Применение команды glDrawArrays обязывает нас упорядочивать массивы координат вершин, нормалей и текстур в строго определённом порядке в соответствии с выбранным правилом обхода точек GL_TRIANGLES, GL_TRIANGLE_STRIP или GL_TRIANGLE_FAN. Это не всегда бывает удобно. Поэтому в OpenGL существует возможность разделить данные вершин и правила обхода по разным массивам. Специально подготовленный массив, в котором хранится порядок обхода вершин, называют массивом индексов. Рассмотрим применение индексов на простом примере.

1 апреля 2012 г.

OpenGL ES 1. Основы рисования для начинающих

Основным изображаемым объектом в OpenGL является вершина. Вершина-это точка в трёхмерном пространстве. Вершина имеет ряд атрибутов. Главными атрибутами вершины являются ее координаты X, Y, Z. В OpenGL принято, что относительно экрана, на который проецируется изображение, ось X направлена слева направо, ось Y-снизу вверх, ось Z-из глубины экрана к его поверхности. По умолчанию точка с координатами X=0, Y=0, Z=0 находится в центре экрана. Другими атрибутами вершины являются цвет, вектор нормали и координаты текстуры.

31 марта 2012 г.

OpenGL ES в Android, класс GLSurfaceView

Базовым классом для вывода трехмерной графики в Android c использованием OpenGL ES является класс GLSurfaceView. Он содержит в себе встроенный интерфейс GLSurfaceView.Renderer, который управляет отображением трехмерных объектов. Можно представить себе GLSurfaceView как холст для рисования, а GLSurfaceView.Renderer как умение рисовать на холсте. Любой класс, который умеет рисовать на холсте, становится трехмерным художником. Таким образом, чтобы создать такого "художника" мы должны определить собственный класс, реализующий интерфейс GLSurfaceView.Renderer.
Например так:
public class MyClassRenderer implements GLSurfaceView.Renderer{
           //тело класса
}
Реализовав интерфейс GLSurfaceView.Renderer мы обязаны в классе MyClassRenderer переопределить абстрактные методы этого интерфейса. Таких методов три: onDrawFrame, onSurfaceCreated и onSurfaceChanged. В методе onDrawFrame производится рисование трехмерных объектов, метод onSurfaceCreated вызывается при создании экрана, метод onSurfaceChanged - при изменении экрана.