Создаём пиксельный взрыв в Фотошоп. Создаём пиксельный взрыв

1. Настраиваем рабочее пространство

Шаг 1

Давайте начнём! Откройте своё исходное изображение в программе Photoshop. Если вы хотите добавить больше пространства к своему изображению, на котором изображена динамика движения, то просто откорректируйте размеры, для этого идём Изображение - Размер холста (Image > Canvas Size). Данное изображение с бегуньей имеет текущие размеры 3.9×5.2 дюймов, поэтому, я собираюсь изменить эти размеры на 6×5.2 дюймов. Дважды щёлкните по слою с бегуньей, в данном случае, это слой с Задним фоном (background layer), чтобы преобразовать его в Слой 1 (layer 1). С помощью инструмента Перемещение

(Move Tool (V)), сместите бегунью в левую часть сцены.

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

2. Создаём взрыв

Шаг 1

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

Примечание переводчика: 1. Вы можете назвать слой с белой заливкой Белый (White) 2. Автор закрывает прозрачные пиксели, образовавшиеся после изменения размеров холста.

Теперь выберите инструмент Волшебная палочка

(Magic Wand Tool (W)). С помощью данного инструмента, выделите белый задний фон. Щёлкните правой кнопкой по слою и в появившемся окне выберите опцию Инверсия (Inverse). Далее, используйте инструмент Прямолинейное лассо

(Polygonal Lasso Tool (L)), чтобы добавить любые участки к выделенной области, которые возможно были обрезаны при выделении, таким образом, полностью выделив объект на фотографии. Когда вы завершите коррекцию выделенной области, нажмите клавиши (Ctrl+J), чтобы продублировать изображение вашей модели на новый слой.

Шаг 2

Ещё раз нажмите клавиши (Ctrl+J), чтобы создать ещё один дубликат изображения с бегуньей. Далее, удалите оригинальный слой с изображением бегуньи на белом фоне. Нажмите клавиши (Ctrl+T) для активации режима свободной трансформации. Значительно увеличьте масштаб изображения бегуньи. Расположите второй увеличенный дубликат бегуньи справа от первого слоя с бегуньей. Продублируйте слой с увеличенным масштабом бегуньи, а затем временно отключите видимость этого дубликата слоя, который мы используем позже.

(Paint Bucket Tool (G)), чтобы залить чёрным цветом слой-маску слоя с увеличенным изображением бегуньи. Заливка слой-маски чёрным цветом скроет изображение бегуньи.

Шаг 3

(Brush Tool (B)). Щёлкните правой кнопкой мыши в любом месте по холсту и появившемся окне настроек кисти щёлкните по маленькому значку в верхнем правом углу панели настроек, чтобы открыть меню. Теперь выберите опцию Квадратные кисти (Square Brushes) из выпадающего меню. Когда появится окно, которое запросит заменить существующие кисти на новые, нажмите кнопку ‘OK’.

Нажмите клавишу (F9), чтобы появилась вкладка Кисть

(Brush). Выберите одну из квадратных кистей, далее, перейдите в настройку Рассеивание (Scattering). Установите Рассеивание (Scatter) на 1000%. Если вы работаете с планшетом, то вы также можете использовать опцию Нажим пера с изменением Непрозрачности (Pen Pressure for Opacity), это сделает края квадратиков светлее, в противном случае, края будут жёсткие.

Шаг 4

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

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

Шаг 5

Теперь включите видимость дубликата слоя с увеличенным изображением бегуньи. Расположите данный дубликат слоя ниже слоя с бегуньей с белой слой-маской. Далее, идём Фильтр - Размытие - Размытие в движении (Filter > Blur > Motion Blur). Установите Угол (Angle) на 47 градусов, а Смещение (Distance) на 762 px.

3. Заключительные пиксели

Шаг 1

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

Конечный результат:

Шаг 1

Откройте фотографию девушки в Фотошопе. Поверните девушку в другую сторону, чтобы взрыв пикселей происходил вправо. Для этого перейдите в меню Image ? Image Rotation ? Flip Canvas Horizontal.

Шаг 2

Для разброса пикселей нужно больше места на правой стороне. Инструментом Crop Tool (C) увеличьте ширину холста.

Шаг 3

Инструментом Rectangular Marquee Tool (M) выделите фотография, не считая чёрного фона.

Шаг 4

Скопируйте выделенную часть на отдельный слой (Ctrl + J) и назовите его «inverted».

Шаг 5

Создайте новый слой между двумя другими и заполните его белым цветом. С его помощью мы скрыли чёрную область.

Шаг 6

Для взрыва пикселей мы создадим свою кисть. Создайте новый документ в Фотошопе (Ctrl + N) размером 512х512 пикселей. Инструментом Rectangular Marquee Tool (M) создайте квадратное выделение, удерживая Shift. Заполните его чёрным цветом.

Шаг 7

Перейдите в меню Edit ? Define Brush Preset, чтобы сохранить кисть.

Шаг 8

Откройте панель кистей (F5) и измените созданную кисть, как показано ниже.

Шаг 9

Сохраните кисть через контекстное меню. Назовите её «Pixel Explosion». Заркойте текущий документ и вернитесь к первому.

Шаг 10

Создайте новый слой в основном документе и выберите инструмент Clone Stamp Tool (S) и кисть, которую мы создали. Определите источник клонирования на руке и нарисуйте квадратные элементы разного размера. Меняйте размер и непрозрачность кисти, чтобы частицы не были одинакового размера.

Шаг 11

Для отделившихся пикселей создайте корректирующий слой Curves, который их затемнит.

Шаг 12

Теперь нужно стереть часть пикселей на девушке. Создайте новый слой и выберите инструмент Brush Tool (B). Белым цветом закрасьте правую сторону девушки той же кистью, которую мы создали.

Шаг 13

Создайте корректирующий слой Curves для всей сцены, чтобы улучшить общую контрастность.

Шаг 14

В завершение создадим виньетирование. Создайте новый слой и залейте его белым цветом. Примените фильтр Lens Correction (Filter ? Lens Correction). Во вкладке Custom настройте секцию Vignette. Установите режим наложения слоя на Multiply.

С помощью данного интересного эффекта Photoshop можно взорвать любое изображение на миллион пикселей. В этом уроке вы узнаете, как с помощью слой-маски и кисти, создать красивый эффект пиксельного взрыва в одно мгновение!

Итоговый результат:

1. Настраиваем рабочее пространство

Шаг 1

Давайте начнём! Откройте своё исходное изображение в программе Photoshop. Если вы хотите добавить больше пространства к своему изображению, на котором изображена динамика движения, то просто откорректируйте размеры, для этого идём Изображение - Размер холста (Image > Canvas Size). Данное изображение с бегуньей имеет текущие размеры 3.9×5.2 дюймов, поэтому, я собираюсь изменить эти размеры на 6×5.2 дюймов. Дважды щёлкните по слою с бегуньей, в данном случае, это слой с Задним фоном (background layer), чтобы преобразовать его в Слой 1 (layer 1). С помощью инструмента Перемещение (Move Tool (V)), сместите бегунью в левую часть сцены.

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

2. Создаём взрыв

Шаг 1

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

Примечание переводчика: 1. Вы можете назвать слой с белой заливкой Белый (White) 2. Автор закрывает прозрачные пиксели, образовавшиеся после изменения размеров холста.

Теперь выберите инструмент Волшебная палочка (Magic Wand Tool (W)). С помощью данного инструмента, выделите белый задний фон. Щёлкните правой кнопкой по слою и в появившемся окне выберите опцию Инверсия (Inverse). Далее, используйте инструмент Прямолинейное лассо (Polygonal Lasso Tool (L)), чтобы добавить любые участки к выделенной области, которые возможно были обрезаны при выделении, таким образом, полностью выделив объект на фотографии. Когда вы завершите коррекцию выделенной области, нажмите клавиши (Ctrl+J), чтобы продублировать изображение вашей модели на новый слой.

Шаг 2

Ещё раз нажмите клавиши (Ctrl+J), чтобы создать ещё один дубликат изображения с бегуньей. Далее, удалите оригинальный слой с изображением бегуньи на белом фоне. Нажмите клавиши (Ctrl+T) для активации режима свободной трансформации. Значительно увеличьте масштаб изображения бегуньи. Расположите второй увеличенный дубликат бегуньи справа от первого слоя с бегуньей. Продублируйте слой с увеличенным масштабом бегуньи, а затем временно отключите видимость этого дубликата слоя, который мы используем позже.

Далее, к каждому слою с бегуньей, добавьте слой-маску. Слой-маска слоя с бегуньей с оригинальными размерами, должна оставаться белой. Выберите инструмент Заливка (Paint Bucket Tool (G)), чтобы залить чёрным цветом слой-маску слоя с увеличенным изображением бегуньи. Заливка слой-маски чёрным цветом скроет изображение бегуньи.

Шаг 3

Далее, мы настроим пиксельную кисть! Выберите инструмент Кисть (Brush Tool (B)). Щёлкните правой кнопкой мыши в любом месте по холсту и появившемся окне настроек кисти щёлкните по маленькому значку в верхнем правом углу панели настроек, чтобы открыть меню. Теперь выберите опцию Квадратные кисти (Square Brushes) из выпадающего меню. Когда появится окно, которое запросит заменить существующие кисти на новые, нажмите кнопку ‘OK’.

Нажмите клавишу (F9), чтобы появилась вкладка Кисть (Brush). Выберите одну из квадратных кистей, далее, перейдите в настройку Рассеивание (Scattering). Установите Рассеивание (Scatter) на 1000%. Если вы работаете с планшетом, то вы также можете использовать опцию Нажим пера с изменением Непрозрачности (Pen Pressure for Opacity), это сделает края квадратиков светлее, в противном случае, края будут жёсткие.

Шаг 4

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

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

Шаг 5

Теперь включите видимость дубликата слоя с увеличенным изображением бегуньи. Расположите данный дубликат слоя ниже слоя с бегуньей с белой слой-маской. Далее, идём Фильтр - Размытие - Размытие в движении (Filter > Blur > Motion Blur). Установите Угол (Angle) на 47 градусов, а Смещение (Distance) на 762 px.

3. Заключительные пиксели

Шаг 1

Для завершения эффекта, добавьте ещё пикселей, чтобы хорошо совместить все элементы сцены. Создайте новый слой поверх слоя с размытием в движении, на этом слое мы применим пиксельную кисть, которую мы применяли ранее. Выберите инструмент Кисть (Brush Tool (B)), используйте клавишу (alt) для отбора образцов цветовых оттенков с изображения бегуньи. Используйте отобранные цветовые оттенки для добавления квадратиков, а также для усиления динамики и усиления взрыва. С помощью инструмента Ластик (Eraser Tool (E)), вы можете удалить лишние пиксели в любой момент.

Результат

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

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

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

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

О причинах "квадратного эффекта" мы поговорим в следующем разделе.

Причины "квадратного эффекта"

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

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

Будем, для определённости, считать, что время, за которое точка из исходной клетки переходит в соседнюю, т. е. длительность шага, составляет 1 секунду. А сторону клетки примем равной 1 метру. Также будем считать, что точка движется в ходе такого перемещения равномерно, а между шагами нет пауз. Тогда возможны 2 варианта.

Если точка переместилась в клетку, имеющую с исходной общую сторону, то её скорость на данном участке пути составит 1м./c. А если исходная клетка имеет с той, в которую попала точка, ровно одну общую вершину, со скорость будет равной уже 2 м./c.

Теперь поговорим о средних скоростях точек на достаточно длинных участках траекторий их движения. Сначала рассмотрим вырожденные случаи.

Если точка движется по траектории, параллельной одной из координатных осей, то её средняя скорость будет равной в точности 1м./c (в этом случае она всегда будет двигаться через середины сторон клеток). А если по траектории, наклонённой к осям под углом 45°, то её средняя скорость составит в точности 2 м./c. (в этом случае она всегда будет двигаться через вершины клеток).

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

1м./c. < v < 2 м./c.

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

Теперь давайте построим 4 луча, исходящие из центра взрыва, наклонённые по углами 45° к осям координат. На рисунке ниже центр взрыва находится в клетке красного цвета, а клетки, через которые проходят лучи, закрашены чёрным цветом.

Лучи делят холст на 4 области. На рисунке они пронумерованы. Давайте посмотрим, как будет проходить пиксельный взрыв отрезка, находящегося в 1-й области и параллельного оси абсцисс. Здесь под отрезком будем понимать набор точек, находящихся на одной линии. Это не отрезок в геометрическом смысле.

Отрезок состоит из 11 точек. Клетки, в которых они находятся, закрашены в синий и голубой цвета. В начальном положении (см. рисунок) клетки "прижимаются" друг к другу сторонами. Через 6 шагов точки оказываются в клетках, между некоторыми из которых имеются зазоры (см. рисунок). Наконец, ещё через 6 шагов никакие клетки, содержащие точки, не будут иметь общих сторон (см. рисунок).

Однако можно заметить, что на 6-м шаге точки, как и в начальном положении, имеют одинаковые ординаты. То же касается и 12-го шага. Можно утверждать, что это будет справедливо для любого шага.

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

Таким образом, все точки, в начальном положении лежащие на одной линии, параллельной оси абсцисс, и находящиеся в 1-й области, на любом шаге будут также находиться на линии, параллельной оси Ox (кстати, за пределы 1-й области они не выйдут).

Сказанное можно естественным образом переформулировать для оставшихся трёх областей. Заметим также, что скорость любой точки, находящейся в 1-й и 3-ей областях, будет постоянной относительно оси абсцисс, а точки, находящейся в 2-й и 4-ей областях - постоянной относительно оси ординат. Скорость эта составляет 1м./c. Если точка лежит на луче (но не в центре взрыва), то её скорость будет постоянной и равной 1м./c. относительно каждой из осей.

Итак, можно сказать, что в ходе взрыва точки разбегаются от его центра "отрезками", параллельными координатным осям, причём длины этих отрезков (т. е. расстояния между крайними точками) увеличиваются.

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

Решение проблемы

Представим себе взрыв снаряда в воздухе. Разве скорости разлёта осколков зависят от системы координат, которую мы вводим для описания процесса взрыва? Разумеется, нет. Но, как было показано выше, в нашем алгоритме средние скорости точек как раз-таки зависят от угла наклона траекторий их движения к координатным осям. Так что решением проблемы, можно, очевидно, считать выравнивание скоростей точек.

Зададимся целью сделать так, чтобы средние скорости всех точек были, хотя бы примерно, одинаковыми. Но как мы можем управлять скоростями, если, фактически, точки перемещаются не плавно, как мы, для удобства полагали в предыдущем разделе, а дискретно?

А управлять мы можем только одним способом. Как мы помним, на каждом шаге точка "перепрыгивает" (такой термин выбран, чтобы подчеркнуть дискретность процесса) из одной клетки в другую. Так вот, в наших силах не позволять некоторым особо "ретивым" точкам выполнять такие перепрыгивания на конкретном шаге. Именно так мы и сможем регулировать средние скорости точек.

Теперь поговорим об этом способе более подробно. В качестве "эталонной" средней скорости мы возьмём скорость точек (тоже будем называть их "эталонными"), траектории которых параллельны координатным осям. Она равна 1м./c. Сделаем так, чтобы средние скорости остальных точек на любом участке пути были максимально близки к этой скорости, но не превышали её.

Ясно, что расстояние, пройденное любой эталонной точкой после выполнения n -го шага, равно n метров. Будем действовать так. Как обычно, перед n -м шагом для каждой точки найдём координаты новой клетки, в которую должна переместиться точка в ближайшее время. Если точка является эталонной, то на n -м шаге переместим её в клетку с найденными координатами. В противном случае вычислим сначала расстояние от клетки, в которой точка находилась в начальный момент времени, до новой клетки.

Если расстояние не превышает n , то на n -м шаге выполним перемещение точки в новую клетку. В противном случае делать этого не будем. Точка "пропускает ход" (т. е. остаётся в "старой" клетке) до следующего шага. Можно легко показать, что в этом случае на шаге № n + 1 перемещение обязательно должно состояться, т. е. никакая точка не пропускает ход 2 раза подряд.

Очевидно, что таким образом мы добьёмся ограничения средней скорости любой точки значением 1м./c.

Изменения в алгоритмах

Первое изменение коснётся структуры fly_point , определённой в файле fly_poin.h. Вот её новая версия:

1. typedef struct 2. { 3. int x0; //Начальные 4. int y0; //координаты точки 5. int x; //Текущие 6. int y; //координаты точки 7. bool new; //true, если координаты обновлены 8. color col; //Цвет точки 9. int dx1; 10. int dy1; //1-й соседней точки ("наискосок") 11. int dx2; //Смещения для получения координат 12. int dy2; //2-й соседней точки ("вплотную") 13. int a; //Коэффициенты общего 14. int b; //уравнения прямой, по которой 15. int c; //движется точка 16. } fly_point;

Что изменилось? Во-первых, появились поля x0 и y0 , предназначенные для хранения начальных координат точки (стр. 3, 4). Во-вторых, поле pnt заменено полями x и y (стр. 5, 6). Я решил, что удобнее хранить текущие координаты в двух отдельных полях переменной-точки, а не в одном. Третье изменение - появление поля new (стр. 7). Поговорим о его назначении подробнее.

Обратите внимание на 6-й шаг нашего алгоритма (см. предыдущий раздел). Если расстояние от точки с новыми координатами до начального положения превышает эталонное, то мы просто помещаем переменную точку во вторую очередь и переходим к обработке очередной точки. Но ведь были уже вычислены и новые координаты. Зачем при построении следующего кадра вычислять их повторно?

Чтобы избежать повторного вычисления координат, перед тем, как поместить обрабатываемую переменную-точку во вторую очередь, сохраним координаты новой точки в её полях x и y . А поле new установим в true . Теперь, при следующем извлечении этой переменной-точки из очереди, мы, считав значение поля new и выяснив, что оно равно true , не будем больше ничего вычислять, а сразу же нанесём точку с координатами, хранящимися в полях x и y , на фоновое изображение. Действительно, ведь если точка "пропустила ход" на предыдущем шаге, то на этом она его точно пропускать не должна.

Таким образом, значение true поля new означает, что наша точка ещё не "перескочила" в клетку с координатами, содержащимися в полях x и y , а только ещё "собирается" это сделать. А значение false , напротив, говорит о том, что точка в данной клетке уже находится.

Второе изменение (впрочем, как и все остальные), затронет файл pixel_explosion.c, а именно, определение функции create_queue() . Ниже приведена новая версия этой функции.

1. qnode* create_queue(image *img, bool use_bcolor) 2. { 3. int xc= img->cur_pnt.x; 4. int yc= img->cur_pnt.y; 5. int r = img->cur_col.red; 6. int g = img->cur_col.green; 7. int b = img->cur_col.blue; 8. if (!use_bcolor) 9. r = -1 ; 10. qnode *queue = 0 ; 11. for (uint y = 0 ; y < img->height; y++) 12. for (uint x = 0 ; x < img->width; x++) 13. { 14. if (x == xc && y == yc) 15. continue ; 16. color col = get_color(img, x, y); 17. if (r == col.red && g == col.green && b == col.blue) 18. continue ; 19. fly_point *fpnt = malloc(sizeof (fly_point)); 20. if (!fpnt) 21. { 22. puts("Нехватка памяти. Работа программы прервана" ); 23. exit(1 ); 24. } 25. fpnt->x0 = fpnt->x = x; 26. fpnt->y0 = fpnt->y = y; 27. fpnt->new = false ; 28. fpnt->col = col; 29. int delta_x = x - xc, abs_delta_x = abs(delta_x); 30. int delta_y = y - yc, abs_delta_y = abs(delta_y); 31. fpnt->dx1 = delta_x ? delta_x > 0 ? 1 : -1 : 0 ; 32. fpnt->dy1 = delta_y ? delta_y > 0 ? 1 : -1 : 0 ; 33. if (abs_delta_x == abs_delta_y) //Если траектория наклонена 34. fpnt->dx2 = fpnt->dy2 = 0 ; //под углом 45гр.к осям 35. else 36. if (delta_x && delta_y) //Если траектория не параллельна ни 37. { //одной из осей 38. fpnt->a = delta_y; 39. fpnt->b = -delta_x; 40. fpnt->c = x * yc - xc * y; 41. if (abs_delta_x > abs_delta_y) 42. { 43. fpnt->dx2 = fpnt->dx1; 44. fpnt->dy2 = 0 ; 45. } 46. else 47. { 48. fpnt->dx2 = 0 ; 49. fpnt->dy2 = fpnt->dy1; 50. } 51. } 52. queue = enqueue(queue, fpnt); 53. } 54. return queue; 55. }

Появилась инициализация новых полей x0 , y0 и new переменных-точек, чьи адреса помещаются в очередь (стр. 25-27). И ещё: у каждой переменной-точки вместо поля pnt теперь инициализируются поля x и y (стр. 25, 26).

Но самое существенное изменение (уже третье по счёту) мы вносим в функцию create_frame() . Вот как она будет выглядеть теперь:

1. qnode* create_frame(image *img, qnode *queue) 2. { 3. static int distance = 0 ; 4. distance++; 5. int w = img->width; 6. int h = img->height; 7. qnode *nqueue = 0 ; 8. while (queue) 9. { 10. fly_point *fpnt = dequeue(&queue); 11. int x = fpnt->x; 12. int y = fpnt->y; 13. if (!fpnt->dx1) //Если траектория параллельна оси ординат 14. y += fpnt->dy1; 15. else 16. if (!fpnt->dy1) //Если траектория параллельна оси абсцисс 17. x += fpnt->dx1; 18. else 19. { 20. if (fpnt->new) //Если новые координаты уже вычислены 21. { 22. set_color(img, x, y, fpnt->col); 23. nqueue = enqueue(nqueue, fpnt); 24. fpnt->new = false ; 25. continue ; 26. } 27. if (fpnt->dx2 == fpnt->dy2) //Если траектория наклонена 28. { //под углом 45гр. к осям 29. x += fpnt->dx1; 30. y += fpnt->dy1; 31. } 32. else //Все оставшиеся случаи 33. { 34. int x1 = x + fpnt->dx1; 35. int y1 = y + fpnt->dy1; 36. int x2 = x + fpnt->dx2; 37. int y2 = y + fpnt->dy2; 38. if (abs(fpnt->a * x1 + fpnt->b * y1 + fpnt->c) > 39. abs(fpnt->a * x2 + fpnt->b * y2 + fpnt->c)) 40. { 41. x = x2; 42. y = y2; 43. } 44. else 45. { 46. x = x1; 47. y = y1; 48. } 49. } 50. } 51. if (fpnt->dx1 && fpnt->dy1 && //Если траектория не параллельна ни одной из осей, 52. (hypot(fpnt->x0 - x, fpnt->y0 - y) > distance)) //но "делать шаг" рано, 53. { //то повторяем предыдущее 54. set_color(img, fpnt->x, fpnt->y, fpnt->col); //положение точки, 55. if (x != -1 && x != w && y != -1 && y != h) //а если её новое положение 56. { //находится внутри холста, 57. fpnt->new = true ; //то откладываем шаг на будущее 58. fpnt->x = x; //и сохраняем координаты нового положения 59. fpnt->y = y; //в полях переменной-точки 60. nqueue = enqueue(nqueue, fpnt); 61. } 62. else //В противном случае 63. free(fpnt); //удаляем переменную-точку 64. continue ; 65. } 66. //Если шаг делать можно (для любых траекторий) 67. if (x != -1 && x != w && y != -1 && y != h) //Если новое положение точки 68. { //находится внутри холста, 69. set_color(img, x, y, fpnt->col); //то делаем шаг 70. fpnt->x = x; 71. fpnt->y = y; 72. nqueue = enqueue(nqueue, fpnt); 73. } 74. else //А если вне холста, 75. free(fpnt); //то удаляем переменную-точку 76. } 77. return nqueue; 78. }

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

Её значение будет сохраняться перед вызовами функции, а после выполнения инкремента в 4-й строке в distance будет содержаться номер вызова функции. Как мы уже замечали, он будет совпадать с текущим эталонным расстоянием.

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

  1. Если траектория точки параллельна одной из координатных осей, то сразу же вычисляем новые координаты (стр. 13-17). Далее выясняем, находится ли новая точка внутри холста. Если находится, то рисуем её, присваиваем полям x и y переменной-точки новые координаты и заталкиваем адрес переменной во вторую очередь (стр. 67-73). В противном случае удаляем переменную-точку (стр. 74-75).
  2. Если траектория точки не параллельна ни одной из осей, но, при этом, её новое положение уже было вычислено ранее, но не нанесено на холст (т. е. флаг new установлен в true ), то действуем следующим образом. Без всяких дополнительных проверок наносим точку с новыми координатами на холст, сбрасываем флаг new в false и заталкиваем адрес переменной во вторую очередь (см. стр. 20-26).
  3. Если траектория точки не параллельна ни одной из осей, но, при этом, её новое положение ещё не вычислялось ранее, то вычисляем его прямо сейчас (см. стр. 27-49 ).

    Далее, если расстояние от новой точки до начального положения превышает эталонное (стр. 51-52), то наносим на холст "старую" точку (стр. 54). Если, при этом, точка с новыми координатами находится внутри холста, то устанавливаем флаг new в true , сохраняем новые координаты в переменной-точке и заталкиваем её адрес во вторую очередь (см. стр. 55-61). А если вне холста - то удаляем переменную-точку (стр. 62-63).

    Если же расстояние от новой точки до начального положения не превышает эталонное, то выясняем, находится ли новая точка внутри холста. Если находится, то рисуем её, присваиваем полям x и y переменной-точки новые координаты и заталкиваем адрес переменной во вторую очередь (стр. 67-73). В противном случае удаляем переменную-точку (стр. 74-75).

Заметим, что в функции create_frame() алгоритм, описанный в предыдущем разделе, реализован не "дословно", а с некоторыми изменениями, диктуемыми соображениями рациональности.

Как мы видим, сами функции create_queue() и create_frame() изменились, но их заголовки остались прежними. Поэтому никаких корректив в функцию main() вносить не требуется.

Четвёртое и последнее изменение, вносимое в программу, является чисто техническим. Ранее программа содержала 5 версий функций get_image() и 2 версии функции main() . Причём, в одну из функций main() были включены 2 версии одной строки, так что можно говорить даже о 3-х версиях функции main() ). Чтобы перейти от одной трансформации к другой, нужно было определённые фрагменты кода "превратить" в комментарии, а некоторые закомментированные фрагменты, наоборот, "превратить" в действующий код.

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

1. #define SQUARE //Чёрный квадрат 2. //#define RHOMBUS //Чёрный ромб 3. //#define CIRCLE //Чёрный круг 4. //#define TEXT //Надпись "Пиксельный взрыв" 5. //#define ONE_LANDSCAPE //Пейзаж на белом фоне 6. //#define TWO_LANDSCAPE //Пейзаж на фоне пейзажа

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

Как мы видим, данный фрагмент соответствует пиксельному взрыву чёрного квадрата. Если же мы хотим реализовать, например, взрыв пейзажа на белом фоне, то должны закомментировать 1-ю строку и раскомментировать 5-ю.

Я не буду приводить фрагменты кода, содержащие директивы условной компиляции. Если читателю интересна её реализация, то он может скачать весь исходный код по ссылке в конце статьи и просмотреть его.

Результаты выполнения программы

С помощью усовершенствованной программы были реализованы новые версии трансформаций, описанных во второй статье цикла (всего 6 штук). Ниже приведены отдельные кадры трансформаций; для сравнения опубликованы также старые кадры, взятые из предыдущей статьи.

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

Итак, смотрим.

Взрыв чёрного квадрата

Вот что было раньше:

Вот, что стало:

Взрывающийся чёрный квадрат (одинаковые скорости точек) Взрывающийся чёрный ромб (одинаковые скорости точек)

Видеоролик:

Взрыв надписи

Видеоролик:

Взрыв пейзажа на белом фоне

Видеоролик:

Взрыв одного пейзажа на фоне другого

Видеоролик:

Заключение

Наша задача выполнена! Никакого "квадратного эффекта" в ходе взрывов теперь не наблюдается. Более того, если раньше круг, взрываясь, становился "немного квадратным", то теперь квадрат во время взрыва стремится приобрести круглую форму.

Конечно же, с выровненными скоростями взрывы смотрятся более эффектно.

Как обычно, ниже приведена ссылка для скачивания исходного кода. Необходимо помнить о том, что для реализации некоторых трансформаций необходимо наличие в директории исполняемого файла графических файлов в формате BMP. Эти файлы называются in.bmp, image1.bmp, image2.bmp. Ссылки на скачивание моих версий этих файлов приведены в предыдущей статье. На всякий случай, продублирую их здесь: