Upload
denis-perevalov
View
2.465
Download
1
Embed Size (px)
Citation preview
Разработка интерактивных систем на OpenFrameworks
Случайные движения и шум Перлина.Графика: вывод частей изображения
УрГУ / ИММ весна 2011лекции и объявления: вопросы отправляйте на адрес www.uralvision.blogspot.com [email protected]
Случайные движения
Пусть мы рисуем кружок на экране в центре (x,y).Чтобы его "оживить", хорошо бы его рисовать не в (x,y), а в (x+x1, y+y1), где x1 и y1 - некоторые малые добавки, "шум".
Как получить такие x1, y1?
Вариант 1. Использовать генератор случайных чиселx1 = ofRandom(-10,10);y1 = ofRandom(-10, 10);
Объект будет дергаться. Для каких-то целей это хорошо, но обычно нужны плавные движения.
Случайные движенияВариант 2. Сгладить генератор случайных чиселА именно, генерировать скачки редко и плавно двигаться между ними.
float jumpTime = 0; //время последнего прыжка, //от начала работы приложенияfloat x1=0; float y1=0; //текущее состояние шумаfloat x1Target=0; float y1Target=0; //целевое состояние, //к которому плавно двигаться
void testApp::update() { float time = ofGetElapsedTimef (); //берем текущее время if ( time > jumpTime + 0.5 ) { //если предыдущий скачок был давнее 0.5 сек тому назад jumpTime = time; //запоминаем время x1Target = ofRandom( -10, 10 ); //новые координаты, куда идти шуму y1Target = ofRandom( -10, 10 ); } x1 = 0.95 * x1 + 0.05 * x1Target; //плавное движение к (x1Target, y1Target) y1 = 0.95 * y1 + 0.05 * y1Target;
}
Этот вариант более интересный, но все равно движение недостаточно гладкое.
Шум ПерлинаШум Перлина был создан Кеном Перлином (англ.) в 1983 году и впоследствии был назван в честь своего создателя.
Perlin noise (Шум Перлина, — математический алгоритм по генерированию процедурной текстуры псевдо-случайным методом. Используется в компьютерной графике для увеличения реализма или графической сложности поверхности геометрических объектов. Также может использоваться для генерации эффектов дыма, тумана и т.д.
(Более быстрая реализация, с многомерным вариантом - симплекс шум, также изобретена К. Перлином в 2001 г.)
Здесь шум - не случайный, а псевдослучайный. То есть вызовов функций типа Random нет.И при каждом старте при желании можно легко воспроизвести одинаковый шум.
Шум ПерлинаВ одномерном случяе шум Перлина - это функция f( t ).она выглядит как гладкий график некоторой случайной функции.Она определена для всех t и меняется плавно.
В openFrameworks она задается функцией float ofNoise( float t )ее результат - число в [-1, 1]. ofNoise - это просто фиксированная функция, просто с труднопредсказуемым поведением.
Можно сделать чтоб t зависело от времени, тогда ofNoise( t ) будет шумом. Но нам нужно две функции, для обеих координат.А ofNoise( t ) всегда будет возвращать одно и то же число. Как быть?
Шум ПерлинаВозвращаемся к задаче колебания кружка на экране.
Вариант 3. Использовать шум Перлина
float tX, tY; //начальные сдвиги для шума
void testApp::setup(){ tX = ofRandom( 0, 1000 ); //задаем сдвиги случайным образом tY = ofRandom( 0, 1000 );}
void testApp:: update(){ float time = ofGetElapsedTimef();
float velocity = 0.5; //скорость шума float x1 = 10 * ofNoise( tX + time * velocity ); //"гладкий" шум [-10,10] float y1 = 10 * ofNoise( tY + time * velocity );}
void testApp::draw(){ ofCircle( 100 + x1, 200 + y1, 20 ); //рисуем кружок}
Шум ПерлинаПример Мотыльки летают около своих центров по такому алгоритму.http://vimeo.com/27772326
Вывод изображения по кускамПусть есть изображение ofImage, а мы хотим вывести на экран его часть. Как это сделать?
Вариант 1. Просто скопировать нужную часть в новое изображение, используя image.pixels() и image1.setFromPixels(...).
Это будет работать очень медленно, если картинка достаточно большая или нужно вывести много разных частей картинок.
Вариант 2.2. Вывести это средствами OpenGL.Так как изображение представляется в виде текстуры OpenGL, это будет работать очень быстро.Его и рассмотрим далее.
Вывод изображения по кускамПример - Rubber Mirrorhttp://www.youtube.com/watch?v=tk-g-sYb7-I
Тут изображение с камеры рисуется на заднем плане целиком. Кроме того, вырезаются небольшие квадратные кусочки, в которых есть человек. Эти квадратики также рисуются. При этом, координаты квадратиков меняются по силе Гука и силе тяжести. Поэтому кажется, что они резиновые.
Вывод изображения по кускамofTexture & texture = image.getTextureReference(); //берем текстуру изображения
texture.bind(); //делаем связывание - теперь OpenGL будет знать, //что при рисовании текстуры данные брать из texture
//как обычно для изображений, это цвет будет умножаться на выводимую картинку, поэтому надо поставить белый цвет, чтобы картинка выводилась без изменения цветовofSetColor( 255, 255, 255 );
glBegin( GL_QUADS ); //Команды OpenGL : начинаем рисовать 4-угольникиglTexCoord2f( 50, 50 ); //1-й угол, координата в текстуреglVertex2f( 100, 100 ); //координата на экране
glTexCoord2f( 150, 50 ); //2-й уголglVertex2f( 300, 100 );glTexCoord2f( 150, 150 ); //3-й уголglVertex2f( 250, 300 );glTexCoord2f( 50, 150 ); //4-й уголglVertex2f( 150, 250 );//... Тут можно рисовать 2-й, 3-й, и далее 4-угольники, можно в цикле.glEnd(); //конец задания 4-угольниковtex.unbind(); //прекращаем связывание, начатое bindЭтот метод хорош своей понятностью. Но если выводить много 4-угольников, то он будет работать недостаточно быстро. А более быстрый способ - задать массивы 4-угольников и нарисовать их одной командой.
Вывод изображения по кускам (Быстрый метод)
Пусть n - число 4-угольников, и есть массивыvector<float> pointTexture( 4 * 2 * n ); //текстурные координаты, откуда брать картинкуvector<float> pointScreen( 4 * 2 * n ); //экранные координаты, в какое место экрана выводить
В них записаны желаемые многоугольники, в порядкеx1, y1, x2, y2, x3, y3, x4, y4 - первый многоугольник,x1, y1, x2, y2, x3, y3, x4, y4 - второй многоугольник,... n-й многоугольник.
Тогда их можно вывести на экран так:ofTexture & texture = image.getTextureReference(); texture.bind(); ofSetColor( 255, 255, 255 ); glEnableClientState(GL_VERTEX_ARRAY);glEnableClientState(GL_TEXTURE_COORD_ARRAY);glTexCoordPointer( 2, GL_FLOAT, 0, &pointTexture[0] );
glVertexPointer(2, GL_FLOAT, 0, &pointScreen[0]); //первое число 2 значит, что координаты заданы двумерные, x,y (а не трехмерные x,y,z). //Для трехмерного рисования - надо задавать pointTexture( 4 * 3 * n ), pointScreen( 4 * 3 * n ).
glDrawArrays(GL_QUADS, 0, n * 4); //сколько выводить вершин - n * 4glDisableClientState(GL_TEXTURE_COORD_ARRAY);glDisableClientState(GL_VERTEX_ARRAY);
tex.unbind();
Домашнее задание I1. Считать произвольное изображение из файла image.loadImage( "image.png" );
2. Разбить его на маленькие фрагменты и вывести на экран.При этом углы фрагментов зашумить двумерной функцией ofNoise(tX,tY) где tX = 0.1 * time + i;tY = 0.1 * time + j; и (i,j) - координаты угла фрагмента на большом изображении.
Должно получиться колышущееся изображение, отдаленно напоминающее рябь воды.
Домашнее задание IIСгенерировать в центре экрана набор точекvector<ofPoint> p;p.resize( 100 );for (int i=0; i<p.size(); i++) { p[i] = ofPoint( ofRandom( 200, 600 ), ofRandom( 200, 600 ) );}
и для каждой точки хранить ее скорость и массу.vector<ofPoint> v, mass;v.resize( 100 ); // - ее также задать случайной m.resize( 100 ); // - ее также задать случайной
и используя силу тяжести, сымитировать падение этих точек.
В результате на экране должно появиться и опасть облако точек.