• добавить в избранное •
abyss-group

Основы компьютерной графики. ifcity.info

Фрактальные множества

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

Здесь хочу остановиться на фрактальных множествах Мандельброта и Жюлиа. Изображения этих множеств не имеют каких либо четко очерченных границ. Особенностью фракталов является то, что даже маленькая часть изображения в конечном итоге представлет общее целое, особенно хорошо этот эффект можно пронаблюдать на примере множества Жюлиа. Кстати на основе этого свойства фракталов основано фрактальное сжатие данных, но эту тему разберем как-нибудь позже (когда накопиться достаточно нужного материала).

Итак приступим к самому главному, ради чего мы здесь и собрались. Как же строятся эти удивителные множества ?

Все сводится к вычислению одной единственной формулы.

Zi+1=Zi2+C

Здесь Z и C - комплексные числа. Как видно, формулы по сути представляет собой обычную рекурсию (или что-то вроде многократно примененного преобразования). Зная правила работы с комплексными числами данную формулу можно упростить и привести к следующему виду.

xi+1=xi2-yi2+a
yi+1=2*xi*yi+b

Построение множества Мандельброта сводится к следующему. Для каждой точки (a,b) проводится серия вычислений по вышеприведенным формулам, причем x0 и y0 принимаются равными нулю, т.е. точка в формуле выступает в качестве константы. На каждом шаге вычиляется величина r=sqrt(x2+y2 ). Значением r ,как ни трудно заметить, является расстояние точки с координатами (x,y) от начала координат ( r=sqrt[ (x-0)2+(y-0)2] ). Исходная точка (a,b) считается принадлежащей множеству Мандельброта, если она никогда не удаляется от начала координат на какое-то критическое число. Для отображения можно подсчитать скорость удаления от центра, если например точка ушла за критическое расстояние, и в зависимости от нее окрасить исходную точку в соответствующие цвет. Полное изображение множества Мандельброта можно получить на плоскости от -2 до 1 по оси x и от -1.5 до 1.5 по оси y. Также известно, что для получения примелимой точности достаточно 100 итеарций (по теории их должно быть бесконечно много). Ниже представлен листинг функции реализующей выполнение итераций и определение принадлежности точки множеству Мандельброта, точнее на выходе мы получаем цвет для соответствующе точки. В качестве критического числа взято число 2. Чтобы не вычислять корень, мы сравниваем квадрат расстояния (r2) с квадратом критического числа, т.е. сравниваем (x2+y2) и 4.

Множество Жюлиа получается если зафиксировать в формуле значение комплексной константы (a+ib), которая будет одинакова для всех точек, а начальные значения x0 и y0 принимать равными значениям координатам вычисляемой точки. Листинг для множества Жюлиа приведен ниже.

Ниже приведен листинг функции отображающий данные множества.

Огненные буквы (эфект огня)

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

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

1 2 7
4 9 6
3 8 5

Далее берется какое-то изображение, и к нему применяется этот фильтр следующим образом. Берется часть изображения размером 3х3 (5х5) вокруг текущей точки (x,y)

(x-1,y-1) (x,y-1) (x+1,y-1)
(x-1,y) (x,y) (x+1,y)
(x-1,y+1) (x,y+1) (x+1,y+1)

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

( (x-1,y-1)*1 + (x,y-1)*2 + (x+1,y-1)*7 + (x-1,y)*4 + (x,y)*9 + (x+1,y)*6 + (x-1,y+1)*3 + (x,y+1)*8 + (x+1,y+1)*5)) /9

Здесь под координатами (x,y) понимается цвет соответствующей точки. Полученное значение представляет собой новый цвет точки (x,y) (точка вокруг которой было взято изображение). И так мы ищем цвет для каждой точки изображения.

Существует очень занятный фильтр

0 0 0
1 1 1
1 1 1

Данный фильтр как бы вытягивает вверх все изображение. Именно на основе этого фильтра и строится эффект огня. Т.е. мы рисуем красную линию (источник огня) и применяем к ней этот фильтр который вытягивает ее вверх. Если больше ничего не делать, то изображение вытянется и мы получим просто статическую картину. Чтобы этого не произошло, к изображению огня каждый раз добавляется некоторое количество случайно размещенных черных точек, которые вносят разнообразие в изображение и в совокупности с применением фильтра дает требуемый нам эфект. Если точек будет слишком много, то огонь будет плохо "разгораться", и будет ноборот слишком высоко подниматься, если их будет слишком мало, поэтому количестов случайных точек следует подбирать в каждом конкретном случае самому. Вот и вся теория, а теперь перейдем к практике.

При программировании использовался набор компонентов DGC , а точнее нам потребуется только компонент TDGCScreen. Опустите его на форму и установите свойство DisplayMode в значение dm320x200x8 (экран размером 320x200 256 цветов). Чтобы была возможность выйти из приложения (выход по нажатию Esc) напишем обработчик для формы события OnKeyPress

Теперь нам следует переопределить палитру. Будет ее переопределять следующим образом, от черного постепенно к красному цвету, именно эти два цвета будут использованы при создании огня.

Палитра представляет собой массив из 256 элементов (от 0 до 255). Элементы массива представляют собой объекты типа TPaletteEntry и определяют кокретный цвет в формате RGB (доли красного зеленого и синего). И когда мы указываем какой-либо цвет мы указываем его индекс в палитре а от туда уже выбирается кокретное цветовое представление. Церный цвет это доли всех цветов равно 0, а красный когда доля красного равна 255 а остальных 0. Таким образом мы в цикле заполняем нашу новую палитру, а затем устанавливаем ее с помощью метода SetPalette (см. выше).

Теперь поступим следующим образом, нарисуем какую либо фигуру на экране (линии, текст и т.д.) красным цветом, а затем скопируем весь экран в заранее приготовленный буфер. Тип буфера объявим следующим образом

Теперь Flame2 содержит начальную картинку, а Falme содержит текущее содержимое экрана(но об это позже). А теперь напишем текст на экране(адрес сайта и e-mail адрес) и запомним его в буфере.

Теперь напишем процедуру выводящую снимок огня на экран.

А теперь несколько пояснений к приведенному листингу. Сначала мы на экране (а точнее в буфере, из которого затем выведем на экран) расставляем случайным образом RPoint черных точек. Затем мы копируем в текущий буфер первоначальную картинку из буфера в котором мы ее запомнили, причем мы копируем только саму картинку, т.е. цвета отличные от черного, чтобы не затереть полученный к этому времени огонь. Если постоянно не востанавливать первоначальное изображение, то огонь мигом съест его и перед вами вновь будет черный экран. Далее мы применяем фильтр, фильтр работает с текущим буфером Flame. И теперь получившееся изображение выводим на экран (из буфера Flame). Чтобы вывод происходил быстрее мы получаем с помощью функции GetPointer указатель на область памяти в которой хранится изображение экрана, и пишем все собержимое буфера Flame прямо в память, минуя все инстанции. Когда вы Вызвали функцию GetPointer Windows блокируется, в это время вы свободно пишете в память а затем вызовом метода ReleasePointer восстанавливаете нормальное функционирование. Используя полученный указатель мы обращаемя к нужному участку памяти и пишем туда нужное значение из буфера Flame.

Теперь осталось написать фильтр.

Фильтр работает так, как было рассказано ранее, только все цвета индексом меньши чем 10 мы заменяем на обычный черный, вот и все. Теперь напишем обработчик события OnInitialize компонента TDGCScreen.

Теперь чтобы огонь горел, а не столя на месте опустим на форму компонент TTimer и установим его свойство Enabled в TRUE а Interval присвоим значение 50. И напишем в его обработчике события OnTimer следующий код.

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

0 0 1
0 1 1
1 1 1

При применении такого фильтра огонь будет подниаться не вверх а немного наискось, как будто дует ветер. Можете поэксперементировать, примините также фильтр размером 5х5.

0 0 0 0 0
0 0 0 0 0
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1

При применении этого фильтра пламя будет "пожарче". Попробуйте косой огонь с фильтром 5x5 и т.д. (только не хабывайте менять делитель в фильтре). Поэксперементируйте с палитрой, попробуйте в ней сделать три цвета и т.д.

Несколько графических эфектов

Думаю все Вы не раз использовали популярную программу PhotoShop. Скажите, а нехотелось бы самим написать программу, которая бы делала нечто подобное, в смысле эфектов которые получаются при фильтрации изображения. Если да, то эта статья поможет вам начать ваше нелегкое дело, а дальше ,как говориться, дерзайте сами. Сразу предлагаю всем, кто достаточно искушен в этой области, прислать принцип создания других эфектов, буду Вам очень благодарен. В первых двух эфектах используется принцип фильтрации рассказанный в главе "Огненные буквы (эфект огня)", если вы ее не читали, то советую для начала просмотреть ее, хотя бы то, что касается теории фильтрации.

Обычное размытие изображения

Для размытия может применятся следующий фильтр:

a a a
a 0 a
a a a

где а-это какое-либо целое число, обычно просто 1. При применении этой матрицы к изображению следует разделить полученный результат на величину a*8 (величина a находится в восьми клетках). Если а равно 1 то, можно просто сдвинуть результат на 3 разряда вправо (8 - это третья степень двойки). Для осуществления размытия фильтр применяется последовательно к каждой составляющей цвета изображения (RGB - красный, зеленый, синий), а затем в итоге получаем результирующий цвет. Ниже приведен листинг, который реализует эфект размытия.

Здесь Image1 - это объект TImage содержащий исходное изображение, а Image2 - результирующее. А это обработчик OnCreate для формы.

Для получения большей степени размытия следует применить эфект к результату и так далее, а также можно использовать матрицу не 3х3 а 5х5, но при этом следует учитывать, что если сумму всех элементов матрицы поделить на наш делитель (в примере это 8) должно получиться 1, иначе возможны непредвиденные результаты обработки изображения.

Теснение изображения

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

a a a
a 0 -a
-a -a -a

Делитель остается прежним (8 если а=1). Теперь если сумму элементов матрицы поделить на наш делитель , то получиться 0. Таким образом можете поэкспериментировать над матрицей и делителем, но чтобы в результате подобного вычисления получался именно 0. И еще есть одна особенность, но это будет выделено отдельно в листинге:

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

Размытие по Гаусу

Этот алгоритм был взят из продукта "Советы по Delphi", который вы можете скачать по адресу www.webinspector.com/delphi. Поэтому здесь я не буду приводить каких-либо комментариев, а лишь приведу пример листинга, за разъяснениями обращайтесь по указанному адресу.

Поэкспериментируйет со вторым параметром процедуры GBlur, для получения различных степеней размытия.

• программирование •

• copyright © 2002-2003 «abyss-group» •
Hosted by uCoz