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

Весь код возьмите из урока Практика: Hello World!, будем его изменять.

Сначала вершинный шейдер, файл minimal.vert:

void main(void)
{
vec4 v = vec4(gl_Vertex);
v.z = 0.0;gl_Position = gl_ModelViewProjectionMatrix * v;
}

 

В этом коде мы «сплющили» 3D-модель, сделав все её z-координаты раными нулю.
Обратите внимание, мы скопировали переменную gl_Vertex в локальную переменную. Attribute-переменная gl_Vertex — это переменная, предоставляемая шейдеру OpenGL, и для вершинного шейдера она read-only.
Поэтому чтобы изменить координаты вершин, сначала нам нужно скопировать их в локальную переменную «v».

Пикселный шейдер просто устанавливает цвет, так что с урока «Hello World!» он останется неизменным.

Написанный нами вершинный шейдер приравнивает координаты каждой вершины к нулю. Когда это происходит с нашим чайником, он становится плоским, примерно как на следующем изображении:

Так, теперь давайте ещё немного поиграем с шейдерами, на этот раз применим к координате z функцию sin() (синус) от x-координаты, таким образом чайник станет «волнистым»:

void main(void)
{
vec4 v = vec4(gl_Vertex);
v.z = sin(5.0*v.x )*0.25;gl_Position = gl_ModelViewProjectionMatrix * v;
}

 

И наконец, в завершение этого простого примера, добавим некоторую вершинную анимацию. Для этого нам понадобится переменная для хранения времени, или счетчик кадров. Но вершинные шейдеры не могут передавать переменные между разными вершинами, не говоря уж о кадрах. Поэтому нам понадобится обьявить переменную-счетчик в приложении OpenGL, и передать её шейдеру как uniform-переменную. Назовём счетчик кадров «time», и получим в шейдере uniform-переменную с этим именем:

uniform float time;

void main(void)
{
vec4 v = vec4(gl_Vertex);

v.z = sin(5.0*v.x + time*0.01)*0.25;

gl_Position = gl_ModelViewProjectionMatrix * v;
}

 

Как описано в секции b]uniform-переменные, в программе OpenGL нужно будет предпринять два шага:

  • Инициализация: получение адреса uniform-переменной
  • Рендер: обновление значения uniform-переменной

Фаза инициализации всего лишь: loc = glGetUniformLocationARB(p,»time»);

Где p — дескриптор программы, и «time» — имя uniform-переменной, используемой вершинным шейдером.
Переменная «loc» имеет тип GLuint и должна быть обьявлена так, чтобы также быть доступной функции рендера.

Теперь напишем саму программку. Код взят из первого практического урока, изменяем файл main.cpp:

/////////////////////////////////////////////////////////////////////
//
// При инициализации переменных выкинем позицию источника света
// и добавим адрес переменной:

GLuint v,f,f2,p; // Дескрипторы программ и шейдеров
GLint loc; // Адрес переменной в памяти
float a = 0; // Хранит угол вращения

// В самом конце SetShaders() получим адрес переменной в памяти:
loc = glGetUniformLocation(p,»time»);

// И изменяем RenderScene():
void RenderScene()
{

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

// Устанавливаем взгляд
gluLookAt(0.0,5.0,5.0,
0.0,0.0,0.0,
0.0f,1.0f,0.0f);

// Записываем в адрес loc значение a
glUniform1fARB(loc, a);

// Рисуем чайник (для этого и подключили glaux)
auxSolidTeapot(1);

// Увеличиваем значение ‘a’
a+=0.1;

SwapBuffers(g_hDC);
}

 

Исходные коды к уроку