Студопедия — OpenGL ES 2.0. Урок первый-Шейдеры
Студопедия Главная Случайная страница Обратная связь

Разделы: Автомобили Астрономия Биология География Дом и сад Другие языки Другое Информатика История Культура Литература Логика Математика Медицина Металлургия Механика Образование Охрана труда Педагогика Политика Право Психология Религия Риторика Социология Спорт Строительство Технология Туризм Физика Философия Финансы Химия Черчение Экология Экономика Электроника

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;

}

Теперь разберем по косточкам, какие процессы происходят в этих шейдерах.

Рассмотрим строчку uniform mat4 u_modelViewProjectionMatrix;

Через униформы (uniform) в шейдеры передаются внешние данные, которые могут быть использованы для расчетов, но не могут быть перезаписаны. Т.е. для обоих шейдеров униформы могут быть использованы только для чтения. Униформы могут быть переданы как в вершинный, так и в фрагментный шейдеры. В нашем случае униформа одна - это матрица модели-вида-проекции u_modelViewProjectionMatrix и передается она в вершинный шейдер. Ключевое слово mat4 означает, что это матрица размером 4х4 состоящая из чисел с плавающей точкой. Униформы никак не связаны с конкретной вершиной и являются глобальными константами. Например, в качестве униформ можно передать в шейдер координаты источника света и координаты глаза (камеры). В дальнейшем для удобства будем обозначать униформы с префиксом u_.

Атрибуты (attribute) - это свойство вершины. У вершины могут быть различные атрибуты. Например, координаты положения в пространстве, координаты вектора нормали, цвет. Кроме того, вы можете передавать в вершинный шейдер какие-либо свои атрибуты. Важно понять, что атрибут - это свойство вершины и поэтому он должен быть задан для каждой вершины. Атрибуты передаются только в вершинный шейдер. Атрибуты доступны вершинному шейдеру только для чтения и не могут быть перезаписаны. Нельзя определять атрибуты в фрагментном шейдере. В дальнейшем для удобства будем обозначать атрибуты с префиксом a_. Итак, определим в вершинном шейдере три атрибута:

координаты вершины в пространстве

attribute vec3 a_vertex;
координаты вектора нормали

attribute vec3 a_normal;
цвет вершины

attribute vec4 a_color;

Ключевое слово vec3 означает, что атрибут является вектором с тремя координатами. У вершины три координаты в пространстве X,Y,Z. У нормали три проекции на оси координат Nx,Ny,Nz. У цвета вершины четыре компоненты - красный, зеленый, синий и альфа, поэтому мы определили цвет как четырехкомпонентный вектор vec4. Важно усвоить, что вершинный шейдер обрабатывает каждую вершину отдельно и не имеет доступа к соседним вершинам. Поэтому вычислить вектор нормали в вершинном шейдере не получится, т.к. для вычисления нормали нужны минимум три вершины. Поэтому нужно рассчитывать нормали на CPU и передавать их в вершинный шейдер в качестве атрибута.

Переменные (varying) - это данные которые при переходе из вершинного во фрагментный шейдер будут вычислены для каждого пикселя путем усреднения данных вершин. Поясню подробнее. В вершинном шейдере мы имеем дело с координатами конкретной вершины. Если передать координаты этой вершины в фрагментый шейдер как varying, то на входе фрагментного шейдера получим координаты в пространстве уже для каждого пикселя, которые будут получены путем усреднения координат вершин. Процесс усреднения называют интерполяцией. Аналогично интерполируются координаты вектора нормали и координаты вектора цвета. Важно, что varying - переменные должны быть обязательно объявлены одинаково в вершинном и фрагментном шейдерах. В дальнейшем для удобства будем обозначать varying - переменные с префиксом v_. Объявим три varying-переменные для пространственных координат, вектора нормали и цвета:

varying vec3 v_vertex;

varying vec3 v_normal;

varying vec4 v_color;

и отправим их на интерполяцию в функции main:

void main() {

v_vertex=a_vertex;
vec3 n_normal=normalize(a_normal);

v_normal=n_normal;

v_color=a_color;

.......
}
Рассмотрим код функции main подробнее:
v_vertex=a_vertex;
Просто копируем атрибут пространственных координат вершины в varying-переменную. В фрагментном шейдере получим значение пространственных координат v_vertex для каждого пикселя.
vec3 n_normal=normalize(a_normal);

В вершинный шейдер мы передаем атрибут вектора нормали a_normal, который не обязательно должен быть нормализованным, поэтому произведем его нормализацию в промежуточный вектор n_normal, который затем отправим на интерполяцию:

v_normal=n_normal;

В фрагментном шейдере получим значение координат вектора нормали v_normal для каждого пикселя.

v_color=a_color;

Копируем атрибут цвета вершины в varying. В фрагментном шейдере получим значение цвета v_color для каждого пикселя.

Следует отметить, что данные в varying - переменные можно записать только в вершинном шейдере, для фрагментного шейдера они доступны только для чтения.

 

Завершает наш вершинный шейдер строка:
gl_Position = u_modelViewProjectionMatrix * vec4(a_vertex,1.0);

Системная переменная gl_Position - это четырех - компонентный вектор, определяющий координаты вершины, спроецированные на плоскость экрана. Переменная gl_Position обязательно должна быть определена в вершинном шейдере, иначе на экране мы ничего не увидим. Сначала преобразуем трехмерный вектор координат вершин в четырехмерный vec4(a_vertex,1.0) с добавлением четвертой компоненты = 1.0, затем помножим униформу мартицы модели-вида-проекции на этот вектор и получим координаты вершины на экране gl_Position.

 

Приступим к рассмотрению фрагментного шейдера.

void main() {

vec3 n_normal=normalize(v_normal);

gl_FragColor = v_color;

}

Напомню, что varying-переменные поступают в фрагментный шейдер для каждого пикселя в интерполированном в виде. В процессе интерполяции на каждый пиксель вектор нормали v_normal перестает быть единичным, поэтому обязательно нужно его повторно нормализовать в промежуточный вектор n_normal, который далее можно использовать при расчете освещения.

Расчет освещения в шейдерах будет рассмотрен в дальнейших уроках.

Конечная цель фрагментного шейдера - это получение цвета пикселя. Рассчитанный цвет пикселя должен быть обязательно записан в системную переменную gl_FragColor. В нашем простейшем примере мы не вычисляем цвет пикселя в фрагментном шейдере, а просто присваиваем значение цвета v_color, полученного путем интерполяции из цветов вершин:

gl_FragColor = v_color;

 

Получение матриц.

Чтобы передать матрицу модели-вида-проекции u_modelViewProjectionMatrix в вершинный шейдер нужно ее получить.
Матрица модели.

Матрица модели описывает собственное движение вершин, из которых состоит модель, в трехмерном пространстве. Простейший случай модели - это одна вершина. Рассмотрим для примера поворот. Например, нам нужно повернуть вершину с координатами x,y,z вершину на угол angle против часовой стрелки относительно вектора с координатами rotateVectorX, rotateVectorY, rotateVectorZ, проходящего через начало координат. Для определения матрицы поворота существует команда Matrix.setRotateM. Получим матрицу поворота modelMatrix.

Определим пустой массив для матрицы модели:

float[] modelMatrix = new float[16];

Применим команду:

Matrix.setRotateM(modelMatrix, 0, angle, rotateVectorX, rotateVectorY, rotateVectorZ);

Получим заполненный массив modelMatrix, соответствующий нашему повороту.

Как получить новые координаты вершины после поворота? Достаточно умножить матрицу модели на вектор координат вершин:

//запишем текущие координаты вершин в четырехкомпонентный вектор
float vertex[]={x,y,z,1};
//создадим пустой массив, в который будут записаны новые координаты

float new_vertex=new float[4];
//умножим матрицу модели на вектор координат
Matrix.multiplyMV(new_vertex, 0, modelMatrix, 0, vertex, 0);
//получили новые координаты после поворота
float new_x=new_vertex[0];
float new_y=new_vertex[1];
float new_z=new_vertex[2];

Команда Matrix.multiplyMV умножает матрицу модели modelMatrix на вектор координат вершины vertex и записывает результат в массив new_vertex. Если модель состоит из множества жестко связанных вершин, совершающих одинаковую трансформацию, нужно умножить матрицу модели на координаты каждой вершины. Если вершин несколько тысяч - это может быть затратно по времени, если трансформации вершин производить на CPU. Поэтому, в этом случае удобнее передать матрицу модели в вершинный шейдер как униформу и преобразования координат вершин выполнять в вершинном шейдере. Если вершин немного, можно оставить расчет модельных трансформаций вне шейдера.


Матрица вида.

Матрица вида однозначно связана с координатами камеры. Зная положение и ориентацию камеры мы можем всегда получить матрицу вида. Для этого в классе android.opengl.Matrix существует специальная функция Matrix.setLookAtM.

Определим пустой массив для матрицы вида:

float[] viewMatrix = new float[16];

Зададим положение камеры пространстве для примера:

float xposition=0.3f;

float yposition=1.7f;

float zposition=1.5f;

Зададим точку в пространстве, на которую смотрит камера. Пусть камера смотрит на начало мировых координат:

float xlook=0;

float ylook=0;

float zlook=0;

Для однозначного определения матрицы вида этих данных мало, т.к. недостаточно установить наблюдателя в точку и правильно направить его взгляд. Нужно еще запретить наблюдателю качать головой. Зададим вектор, который определит, где у камеры верх. Пусть верх будет вдоль оси Y:

float xtop=0;

float ytop=1;

float ztop=0;

А затем вызовем функцию функция Matrix.setLookAtM, которая рассчитает и заполнит массив viewMatrix:

Matrix.setLookAtM(viewMatrix, 0, xposition, yposition, zposition, xlook, ylook, zlook, xtop, ytop, ztop);

Матрица вида готова.

 

Матрица модели-вида.
Часто комбинируют матрицы модели и вида в единую матрицу модели-вида, которую можно получить путем умножения матрицы вида на матрицу модели. Для умножения двух матриц используют команду Matrix.multiplyMM.
float[] modelViewMatrix = new float[16];
Matrix.multiplyMM(modelViewMatrix, 0, viewMatrix, 0, modelMatrix, 0);
Получили заполненный массив modelViewMatrix.

Матрица проекции.

Матрица проекции выполняет проекцию координат вершин на экран аппарата после модельно-видовых трансформаций. Она может получена в методе onSurfaceChanged класса рендерера путем выполнения команды Matrix.frustumM для перспективной проекции. Например, так:

............
float projectionMatrix=new float[16];
............
public void onSurfaceChanged(GL10 unused, int width, int height) {
// устанавливаем glViewport
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
float k=0.055f;
float left = -k*ratio;
float right = k*ratio;
float bottom = -k;
float top = k;
float near = 0.1f;
float far = 10.0f;
// получаем матрицу проекции
Matrix.frustumM(projectionMatrix, 0, left, right, bottom, top, near, far);
}
Матрица модели-вида-проекции.
Умножив матрицу проекции на матрицу модели-вида получаем комбинированную матрицу модели-вида-проекции:
float modelViewProjectionMatrix=new float[16];
Matrix.multiplyMM(modelViewProjectionMatrix,0,projectionMatrix, 0, modelViewMatrix, 0);

Матрица модели-вида-проекции получена. Напомню, что матрица модели-вида-проекции передается в вершинный шейдер как униформа. Вопрос по передаче данных униформ в шейдеры будет рассмотрен позже.

 

Создание шейдерного объекта в OpenGL ES 2.0

Шейдерный объект состоит из кодов вершинного и фрагментного шейдеров и так называемой "программы". Для использования шейдерного объекта нужно получить ссылку на данную программу. Опишем последовательность действий при создании шейдерного объекта.


1. Запишем коды вершинного и фрагментного шейдеров в строковые переменные:
String vertexShaderCode=

"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);"+

"}";

String fragmentShaderCode=

"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;"+

"}";

2. Получим свободный номер вершинного шейдера:

int vertexShader_Handle = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);

В дальнейшем к вершинному шейдеру можно обращаться по этому номеру. Целое число vertexShader_Handle является ссылкой на область памяти, выделенную для хранения вершинного шейдера.


3. Передаем в вершинный шейдер его код:
GLES20.glShaderSource(vertexShader_Handle, vertexShaderCode);

Здесь vertexShader_Handle - это полученная в предыдущем пункте ссылка на вершинный шейдер, а vertexShaderCode-код вершинного шейдера в виде строки.


4. Компилируем вершинный шейдер:
GLES20.glCompileShader(vertexShader_Handle);

При этом код вершинного шейдера переводится в инструкции, понятные видеокарте.


5.Аналогичные операции проводим с фрагментным шейдером:
// получаем ссылку на фрагментный шейдер
int fragmentShader_Handle = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
// загружаем в фрагментный шейдер его код
GLES20.glShaderSource(fragmentShader_Handle, fragmentShaderCode);
// компилируем фрагментный шейдер
GLES20.glCompileShader(fragmentShader_Handle);

6. Получим свободный номер "программы":
int program_Handle = GLES20.glCreateProgram();

В дальнейшем к программе можно обращаться по этому номеру. Целое число program_Handle является ссылкой на программу.


7. Присоединим к программе скомпилированный вершинный шейдер:
GLES20.glAttachShader(program_Handle, vertexShader_Handle);

Первый аргумент program_Handle - ссылка на программу, второй vertexShader_Handle - ссылка на вершинный шейдер.


8. Аналогично присоединим к программе скомпилированный фрагментный шейдер:
GLES20.glAttachShader(program_Handle, fragmentShader_Handle);

9.Компилируем программу:
GLES20.glLinkProgram(program_Handle);

Шейдерный объект готов к работе. Мы можем создать несколько шейдерных объектов и переключаться между ними командой:

GLES20.glUseProgram(program_Handle);

Данная команда указывает, что в данный момент активным является объект со ссылочным номером программы program_Handle и соответственно будут работать его вершинный и фрагментный шейдеры. Поэтому нужно сохранять program_Handle как поле класса.

 

Передача данных униформ в шейдеры.

Напомню, что нам нужно передать униформу матрицы модели-вида-проекции в вершинный шейдер. Какие действия для этого нужно выполнить?

Сначала необходимо выбрать активную шейдерную программу:

GLES20.glUseProgram(program_Handle);

Затем получить внешнюю ссылку на униформу:

int u_modelViewProjectionMatrix_Handle =

GLES20.glGetUniformLocation(program_Handle, "u_modelViewProjectionMatrix");

Первый аргумент - это текущий ссылочный номер программы. Внутри вершинного шейдера матрица модели-вида-проекции называется как u_modelViewProjectionMatrix. Имя униформы передается в команду в качестве второго аргумента в виде строки. В результате выполнения команды получим ссылочный номер u_modelViewProjectionMatrix_Handle, который позволяет нам обращаться к униформе u_modelViewProjectionMatrix извне.

Связываем массив modelViewProjectionMatrix и униформу:

GLES20.glUniformMatrix4fv(

u_modelViewProjectionMatrix_Handle, 1, false, modelViewProjectionMatrix, 0);

Первый аргумент - это ссылка на униформу, которую мы получили через команду glGetUniformLocation. Второй аргумент 1 - это размерность элементов матрицы. Элементы матрицы модели-вида-проекции это одиночные числа с плавающей точкой (в общем случае элементами матрицы могут быть векторы, тогда размерность может быть 2,3, ну это уже лишнее). Третий аргумент false -признак транспонирования. Показывает нужно ли транспонировать матрицу перед передачей в шейдер. Это нам не понадобится. Четвертый аргумент modelViewProjectionMatrix - это источник данных, т.е. наш массив размером 16 элементов, в котором записана матрица модели-вида-проекции. Пятый аргумент 0 - это сдвиг. Никаких сдвигов мы использовать не будем. Все прочие матрицы размера 4х4 (например модели-вида) передаются в шейдер аналогично при помощи команды glUniformMatrix4fv, если в них есть потребность.

Рассмотри другие случаи. Например, нам нужно передать в шейдер координаты источника света xLightPosition, yLightPosition, zLightPosition. Можно сформировать из координат трехэлементный массив и предать его в шейдер:

float [] lightPosition = {xLightPosition, yLightPosition, zLightPosition};

// получаем ссылку на униформу u_lightPosition

int u_lightPosition_Handle=GLES20.glGetUniformLocation(program_Handle, "u_lightPosition");

// связываем наш массив lightPosition с униформой u_lightPosition

GLES20.glUniform3fv(u_lightPosition_Handle, 1, lightPosition, 0);

Само название функции glUniform3fv говорит о том, что мы будем передавать в шейдер векторы из трех компонент. Первый аргумент u_lightPosition_Handle является ссылкой на униформу u_lightPosition. Второй аргумент 1- это количество передаваемых элементов. В нашем случае мы передаем один вектор, поэтому ставим единицу. Можно передавать массив трехкомпонентных векторов (т.е. массив массивов или двумерный массив), но мы этим заниматься не будем. Третий аргумент lightPosition - наш массив, в котором содержатся координаты источника света.

Существует другой способ передачи координат источника света без массива напрямую по компонетам. Например так:

u_lightPosition_Handle=GLES20.glGetUniformLocation(program_Handle, "u_lightPosition");
GLES20.glUniform3f(u_lightPosition_Handle, xLightPosition, yLightPosition, zLightPosition);

Здесь вместо векторной формы команды glUniform3fv используется скалярная glUniform3f. Какую форму использовать - дело вкуса.

Аналогично можно передать в шейдер координаты камеры:

int u_camera_Handle=GLES20.glGetUniformLocation(program_Handle, "u_camera");
GLES20.glUniform3f(u_camera_Handle, xСamera, yСamera, zСamera);

Наконец, рассмотрим самый простой случай - передачи числа с плавающей точкой. Допустим нужно связать число arg с униформой u_arg:

int u_arg_Handle=GLES20.glGetUniformLocation(program_Handle, "u_arg");

GLES20.glUniform1f(u_arg_Handle, arg);

 

Передача атрибутов вершин в вершинный шейдер.

В отличие от униформ атрибуты являются свойствами вершины и должны быть определены для каждой вершины рисуемого объекта. Кроме того, связь массивов данных с атрибутами в вершинном шейдере устанавливается не напрямую, а через буферы типа FloatBuffer. Для примера рассмотрим передачу в шейдер атрибутов банального треугольника. Пусть координаты точки A треугольника будут xa, ya, za, координаты точки B - xb, yb, yb, точки C - xc, yc, zc. Создадим массив, последовательно перечисляющий координаты треугольника в порядке обхода вершин A-->B-->C:

float vertexArray []={xa, ya, za, xb, yb, yb, xc, yc, zc};

Затем перепишем его в буфер vertexBuffer:

ByteBuffer b1 = ByteBuffer.allocateDirect(36);
b1.order(ByteOrder.nativeOrder());
FloatBuffer vertexBuffer = b1.asFloatBuffer();
vertexBuffer.position(0);
vertexBuffer.put(vertexArray);

vertexBuffer.position(0);

Координаты вершин записаны в буфер. Теперь нужно связать этот буфер с соответствующим атрибутом a_vertex внутри шейдера.

// выбираем текущую программу

GLES20.glUseProgram(program_Handle);
// получаем ссылку на атрибут a_vertex в вершинном шейдере
int a_vertex_Handle = GLES20.glGetAttribLocation(program_Handle, "a_vertex");
// включаем использование данного атрибута
GLES20.glEnableVertexAttribArray(a_vertex_Handle);
//связываем наш буфер vertexBuffer с атрибутом a_vertex внутри шейдера
GLES20.glVertexAttribPointer(a_vertex_Handle, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer);

Рассмотрим команду glVertexAttribPointer подробнее. Первый аргумент a_vertex_Handle - это ссылка на атрибут координаты вершины. Второй аргумент 3 - это размерность атрибута, т.е. количество компонент на вершину. Каждая вершина имеет три координаты в пространстве - поэтому размерность будет равна трем. Третий аргумент GLES20.GL_FLOAT указывает что атрибут состоит из чисел с плавающей точкой. Четвертый аргумент false - признак нормализации. Он указывает нужно ли нормализовать вектор из трех координат перед передачей в шейдер. Четвертый аргумент 0 - сдвиг. Пятый аргумент - это наш буфер координат вершин vertexBuffer. Итак, мы установили связь между буфером координат вершин и атрибутом координат вершин в шейдере. Это связь будет сохранена даже если вы будете менять координаты вершин в процессе рендинга. Т.е если вы перезаписываете буфер vertexBuffer в цикле не нужно каждый раз выполнять команду glVertexAttribPointer. Измененные данные атрибута автоматически попадут в вершинный шейдер. Однако, если шейдерная программа будет разрушена или уничтожен объект vertexBuffer связь придется устанавливать заново.

Аналогично установим связь для другого атрибута вершины - вектора нормали. У одиночного треугольника нормаль одинакова для всех трех вершин и рассчитывается как векторное произведение двух векторов: A-->B и A-->C:

float x1=xb-xa;

float y1=yb-ya;

float z1=zb-za;

float x2=xc-xa;

float y2=yc-ya;

float z2=zc-za;

float xn=y1*z2-y2*z1;

float yn=x2*z1-x1*z2;

float zn=x1*y2-x2*y1;

Получаем координаты вектора нормали xn,yn,zn. Cоздадим буфер для хранения координат нормали и установим его связь с атрибутом a_normal:

float normalArray []={xn, yn, zn, xn, yn, zn, xn, yn, zn};

ByteBuffer b2 = ByteBuffer.allocateDirect(36);
b2.order(ByteOrder.nativeOrder());
FloatBuffer normalBuffer = b2.asFloatBuffer();
normalBuffer.position(0);
normalBuffer.put(normalArray);
normalBuffer.position(0);
GLES20.glUseProgram(program_Handle);
int a_normal_Handle = GLES20.glGetAttribLocation(program_Handle, "a_normal");
GLES20.glEnableVertexAttribArray(a_normal_Handle);
GLES20.glVertexAttribPointer(a_normal_Handle, 3, GLES20.GL_FLOAT, false, 0, normalBuffer);

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

float colorArray []={
redA, greenA, blueA, alphaA,
redB, greenB, blueB, alphaB,
redC, greenC, BlueC, alphaC};
ByteBuffer b3 = ByteBuffer.allocateDirect(48);
b3.order(ByteOrder.nativeOrder());
FloatBuffer colorBuffer = b3.asFloatBuffer();
colorBuffer.position(0);
colorBuffer.put(colorArray);
colorBuffer.position(0);
GLES20.glUseProgram(program_Handle);
int a_color_Handle = GLES20.glGetAttribLocation(program_Handle, "a_color");
GLES20.glEnableVertexAttribArray(a_color_Handle);
GLES20.glVertexAttribPointer(a_color_Handle, 4, GLES20.GL_FLOAT, false, 0, colorBuffer);

 

Класс шейдерного объекта.

С точки зрения объектно-ориентированного программирования удобно создать отдельный класс шейдерного объекта, внутри него хранить ссылку на шейдерную программу и методы связывающие униформы и атрибуты с внешними данными. Создадим такой класс.

public class Shader {
//будем хранить ссылку на шейдерную программу внутри класса как локальное поле
private int program_Handle;


/ /при создании объекта класса передаем в конструктор строки кода вершинного и фрагментного шейдера
public Shader (String vertexShaderCode, String fragmentShaderCode){
//вызываем метод, создающий шейдерную программу при этом заполняется поле program_Handle
createProgram(vertexShaderCode, fragmentShaderCode);
}

/ / метод, который создает шейдерную программу, вызывается в конструкторе

private void createProgram (String vertexShaderCode, String fragmentShaderCode){

//получаем ссылку на вершинный шейдер
int vertexShader_Handle =

GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
//присоединяем к вершинному шейдеру его код
GLES20.glShaderSource(vertexShader_Handle, vertexShaderCode);
//компилируем вершинный шейдер
GLES20.glCompileShader(vertexShader_Handle);
//получаем ссылку на фрагментный шейдер
int fragmentShader_Handle =

GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
//присоединяем к фрагментному шейдеру его код
GLES20.glShaderSource(fragmentShader_Handle, fragmentShaderCode);
//компилируем фрагментный шейдер
GLES20.glCompileShader(fragmentShader_Handle);
//получаем ссылку на шейдерную программу
program_Handle = GLES20.glCreateProgram();
//присоединяем к шейдерной программе вершинный шейдер
GLES20.glAttachShader(program_Handle, vertexShader_Handle);
//присоединяем к шейдерной программе фрагментный шейдер
GLES20.glAttachShader(program_Handle, fragmentShader_Handle);
//компилируем шейдерную программу
GLES20.glLinkProgram(program_Handle);
}

//метод, который связывает буфер координат вершин vertexBuffer с атрибутом a_vertex

public void linkVertexBuffer (FloatBuffer vertexBuffer){

//устанавливаем активную программу
GLES20.glUseProgram(program_Handle);

//получаем ссылку на атрибут a_vertex
int a_vertex_Handle = GLES20.glGetAttribLocation(program_Handle, "a_vertex");

//включаем использование атрибута a_vertex
GLES20.glEnableVertexAttribArray(a_vertex_Handle);

//связываем буфер координат вершин vertexBuffer с атрибутом a_vertex
GLES20.glVertexAttribPointer(

a_vertex_Handle, 3, GLES20.GL_FLOAT, false, 0,vertexBuffer);
}

//метод, который связывает буфер координат векторов нормалей normalBuffer с атрибутом a_normal

public void linkNormalBuffer (FloatBuffer normalBuffer){
//устанавливаем активную программу
GLES20.glUseProgram(program_Handle);
//получаем ссылку на атрибут a_normal
int a_normal_Handle = GLES20.glGetAttribLocation(program_Handle, "a_normal");
//включаем использование атрибута a_normal
GLES20.glEnableVertexAttribArray(a_normal_Handle);
//связываем буфер нормалей normalBuffer с атрибутом a_normal
GLES20.glVertexAttribPointer(

a_normal_Handle, 3, GLES20.GL_FLOAT, false, 0,normalBuffer);
}

//метод, который связывает буфер цветов вершин colorBuffer с атрибутом a_color

public void linkColorBuffer (FloatBuffer colorBuffer){
//устанавливаем активную программу
GLES20.glUseProgram(program_Handle);
//получаем ссылку на атрибут a_color
int a_color_Handle = GLES20.glGetAttribLocation(program_Handle, "a_color");
//включаем использование атрибута a_color
GLES20.glEnableVertexAttribArray(a_color_Handle);
//связываем буфер нормалей colorBuffer с атрибутом a_color
GLES20.glVertexAttribPointer(

a_color_Handle, 4, GLES20.GL_FLOAT, false, 0, colorBuffer);
}


// метод, который связывает матрицу модели-вида-проекции

// modelViewProjectionMatrix с униформой u_modelViewProjectionMatrix

public void linkModelViewProjectionMatrix (float [] modelViewProjectionMatrix){
//устанавливаем активную программу
GLES20.glUseProgram(program_Handle);
//получаем ссылку на униформу u_modelViewProjectionMatrix
int u_modelViewProjectionMatrix_Handle =

GLES20.glGetUniformLocation(program_Handle, "u_modelViewProjectionMatrix");
//связываем массив modelViewProjectionMatrix

//с униформой u_modelViewProjectionMatrix

GLES20.glUniformMatrix4fv(

u_modelViewProjectionMatrix_Handle, 1, false, modelViewProjectionMatrix, 0);
}

// метод, который связывает координаты камеры с униформой u_camera
public void linkCamera (float xCamera, float yCamera, float zCamera){
//устанавливаем активную программу
GLES20.glUseProgram(program_Handle);
//получаем ссылку на униформу u_camera
int u_camera_Handle=GLES20.glGetUniformLocation(program_Handle, "u_camera");
// связываем координаты камеры с униформой u_camera
GLES20.glUniform3f(u_camera_Handle, xCamera, yCamera, zCamera);
}

// метод, который связывает координаты источника света с униформой u_lightPosition
public void linkLightSource (float xLightPosition, float yLightPosition, float zLightPosition){
//устанавливаем активную программу
GLES20.glUseProgram(program_Handle);
//получаем ссылку на униформу u_lightPosition
int u_lightPosition_Handle=GLES20.glGetUniformLocation(program_Handle, "u_lightPosition");
// связываем координаты источника света с униформой u_lightPosition

GLES20.glUniform3f(u_lightPosition_Handle, xLightPosition, yLightPosition, zLightPosition);
}

// метод, который делает шейдерную программу данного класса активной
public void useProgram (){
GLES20.glUseProgram(program_Handle);
}

// конец класса
}

 







Дата добавления: 2015-10-19; просмотров: 753. Нарушение авторских прав; Мы поможем в написании вашей работы!



Расчетные и графические задания Равновесный объем - это объем, определяемый равенством спроса и предложения...

Кардиналистский и ординалистский подходы Кардиналистский (количественный подход) к анализу полезности основан на представлении о возможности измерения различных благ в условных единицах полезности...

Обзор компонентов Multisim Компоненты – это основа любой схемы, это все элементы, из которых она состоит. Multisim оперирует с двумя категориями...

Композиция из абстрактных геометрических фигур Данная композиция состоит из линий, штриховки, абстрактных геометрических форм...

Понятие метода в психологии. Классификация методов психологии и их характеристика Метод – это путь, способ познания, посредством которого познается предмет науки (С...

ЛЕКАРСТВЕННЫЕ ФОРМЫ ДЛЯ ИНЪЕКЦИЙ К лекарственным формам для инъекций относятся водные, спиртовые и масляные растворы, суспензии, эмульсии, ново­галеновые препараты, жидкие органопрепараты и жидкие экс­тракты, а также порошки и таблетки для имплантации...

Тема 5. Организационная структура управления гостиницей 1. Виды организационно – управленческих структур. 2. Организационно – управленческая структура современного ТГК...

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

ЛЕЧЕБНО-ПРОФИЛАКТИЧЕСКОЙ ПОМОЩИ НАСЕЛЕНИЮ В УСЛОВИЯХ ОМС 001. Основными путями развития поликлинической помощи взрослому населению в новых экономических условиях являются все...

МЕТОДИКА ИЗУЧЕНИЯ МОРФЕМНОГО СОСТАВА СЛОВА В НАЧАЛЬНЫХ КЛАССАХ В практике речевого общения широко известен следующий факт: как взрослые...

Studopedia.info - Студопедия - 2014-2024 год . (0.011 сек.) русская версия | украинская версия