Вы находитесь на странице: 1из 16

Разработка интерактивных систем

на OpenFrameworks

Шейдеры

http://pixelnoizz.files.wordpress.com/2009/08/picture-54.png?w=510&h=259

лекции и объявления:
www.uralvision.blogspot.com

вопросы по проектам и программированию:


perevalovds@gmail.com
УрГУ / ИММ весна 2011
Что такое шейдеры

1. Шейдерами сегодня обычно называют небольшие программы, которые


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

2. Бывают вершинные (геометрия) и фрагментные (пиксельные) шейдеры.

3. Шейдеры выполняются на видеокарте и поэтому не загружают CPU.


Поэтому с их помощью можно выполнять очень интересные и сложные
трансформации и эффекты.
Что такое шейдеры

Шейдеры представляют собой небольшие программы на


языке GLSL.

Их можно хранить в текстовых файлах. При старте вашего


приложения они будут компилироваться и записываться в
видеопамять.

Это удобно тем, что можно менять и настраивать шейдеры


без перекомпиляции самого приложения.
Что требуется для использования
шейдеров в OpenFrameworks
В этой лекции мы рассмотрим, как использовать фрагментные шейдеры
для трансформации изображений в openFrameworks.

Необходимые компоненты:

1. Аддон ofxFBOTexture для рисования в буфер:


ofxFBOTexture.h, ofxFBOTexture.cpp (см. "Рисование в буфер" в лекции
по двумерной графике).

2. Аддон ofxShader для загрузки/выгрузки шейдеров:


ofxShader.h, ofxShader.cpp

Их можно скачать, например, в


http://playbat-common.googlecode.com/svn/trunk/of/
Пример 1. Сглаживание

http://www.youtube.com/watch?v=Nkr4JiU0sF0

Реализуем сглаживание путем наложения друг на друга 9 сдвинутых картинок с разным весом.
Радиус сдвига будет определяться X-координатой мыши.

(Идея взята из примера shaderBlurExample http://forum.openframeworks.cc/index.php?topic=2729.


0)
Текст шейдера
Создаем файл фрагментного шейдера blur.frag в папке bin/data:

#extension GL_ARB_texture_rectangle : enable //настройка


uniform sampler2DRect src_tex_unit0; //внешний параметр
uniform float blurAmount; //внешний параметр - радиус сглаживания

void main( void ) //эта функция будет применяться к каждому пикселу


{
vec2 st = gl_TexCoord[0].st; //st - вектор входных координат пиксела
vec4 color; //цвет, будем его накапливать
for(float i = -4.0; i <= 4.0 ; i++) {
float weight = 5.0 - abs(i);
color += weight * texture2DRect(src_tex_unit0, st + vec2(blurAmount * i, 0));
//взятие цвета пиксела из текстуры src_tex_unit0,
//с координатами x = st[0] + blurAmount * i,
// y = st[1]
}
color /= 25.0;
gl_FragColor = color; //установка выходному пикселу цвета color
}

! Важно: компилятор автоматически не преобразует float <-> int, а выдает про это
предупреждения и шейдер не запускается.
Текст шейдера
Создаем файл вершинного шейдера blur.vert в папке bin/data:

void main() {
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = ftransform();
}

Это вершинный шейдер, который ничего не изменяет.


Просто ofxShader для работы нужен и этот файл.
Текст приложения
#include "ofxFBOTexture.h" //Рисование в буфер
#include "ofxShader.h" //Шейдеры

ofxFBOTexture buffer; //Буфер


ofxShader shader; //Шейдер
ofVideoGrabber grabber; //Захват картинки с камеры

void testApp::setup()
{
ofBackground(255,255,255);
grabber.initGrabber(640, 480); //запуск камеры

buffer.allocate(640, 480, true); //true - автоочистка фоном на каждом шаге

//Загрузка шейдера из файлов blur.frag и blur.vert.


//При старте, если будут ошибки в тексте шейдера, они выведутся на экран,
//с номером строки с ошибкой.
shader.loadShader("blur");
}

void testApp::update()
{
grabber.update(); //обновление картинки с камеры
}
Текст приложения
void testApp::draw()
{
//Сначала заготавливаем картинку - рисуем ее в буфер
buffer.begin();
ofSetColor(255, 255, 255);
grabber.draw(0, 0); //В буфер выводим кадр с камеры
buffer.end();

//Включаем шейдер
shader.setShaderActive(true);
//Устанавливаем параметр шейдера
blurAmount float blurAmount = mouseX / 20.0; //mouseX - текущая координата мыши
shader.setUniformVariable1f("blurAmount", blurAmount); //1f - то есть скаляр типа float

//Рисуем, что требуется на экран, "пропуская" это через шейдер


ofSetColor(255, 255, 255);
buffer.draw(0, 0);

//Выключаем шейдер
shader.setShaderActive(false);
}
Пример 2. Лупа

http://www.youtube.com/watch?v=H-mYdfaku90

Реализуем в точке мыши "лупу".

(Идея взята из примера shaderZoomExample http://forum.openframeworks.cc/index.php?


topic=2729.0)
Текст шейдера
Создаем файл фрагментного шейдера zoom.frag в папке bin/data:

#extension GL_ARB_texture_rectangle : enable


uniform sampler2DRect src_tex_unit0;
uniform vec2 circlePos; //положение лупы
uniform float circleRadius; //радиус лупы
uniform float zoom; //коэффициент увеличения в лупе

void main( void )


{
vec2 st = gl_TexCoord[0].st;
float relX = st.s - circlePos.x;
float relY = st.t - circlePos.y;
float dist = sqrt(relX*relX + relY*relY);
if( dist <= circleRadius && dist > 0.0){ //если пиксел в лупе и не центр (т.к. делим на dist)
float newRad = dist * (zoom * dist/circleRadius);
float newX = circlePos.x + relX / dist * newRad;
float newY = circlePos.y + relY / dist * newRad;
gl_FragColor = texture2DRect(src_tex_unit0, vec2(newX, newY) );
}
else {
gl_FragColor = texture2DRect(src_tex_unit0, st );
}
}
Кроме того, создаем файл вершинного шейдера zoom.vert копированием из blur.vert
Текст приложения
В основе - текст предыдущего примера.
в setup()
shader.loadShader("zoom");

в draw()
//Устанавливаем параметры шейдера
shader.setUniformVariable2f("circlePos", mouseX, 480 - mouseY);
shader.setUniformVariable1f("circleRadius", 120);
shader.setUniformVariable1f("zoom", 1.5);
Операции с цветом

Мы научились осуществлять комбинирование цветов пикселов и производить


геометрическое преобразование изображения.

Как изменять значения цветов:


Цвет - имеет тип vec4, это вектор из 4-х компонент R,G,B,Alpha,
которые принимают значения от 0 до 1.

vec4 color = vec4( 1.0, 0.0, 0, 1.0 ); //создать красный цвет

color[0], color[1], color[2], color[3] - R, G, B, Alpha - компоненты.

Рассмотрим еще один пример.


Пример 3. Лупа с изменением цветов

Модифицированная лупа из предыдущего примера, в которой у цветов внутри лупы


переставлены местами R,G,B компоненты.
Текст шейдера
Файл zoom.frag в папке bin/data:

заменяем строку

gl_FragColor = texture2DRect(src_tex_unit0, vec2(newX, newY) );

на

vec4 color = texture2DRect(src_tex_unit0, vec2(newX, newY) );


gl_FragColor = vec4( color[1], color[2], color[0], color[3] ); //перемешивание компонент цвета
Домашнее задание

1. Вихрь
Сделайте так, чтобы лупа внутри себя закручивала изображение.

Подсказка: поворачивать вектор в зависимости от значения dist/circleRadius.

2. Вода 1

В шейдерном языке есть функции sin, cos, atan.


Сделайте волнообразное искажение изображения на экране, от центра экрана.
Как будто уронили что-то в воду в центре экрана.
Т.е. это должно быть видео, а не статичная картинка.

3. Вода 2
С помощью нажатий мыши имитировать падение чего-то в воду на картинке.
Реализовать это, к примеру, путем моделирования в текстуре амплитуду колебаний
волн.

Оценить