Статьи

Создайте органический раствор с шумом Perlin

В этом уроке мы будем создавать файл класса, который создает стилизованную маску для любого DisplayObject . Маска будет основана на шуме Перлина и создаст эффект растущих отверстий, которые постепенно «разъедают» DisplayObject до тех пор, пока он не будет полностью скрыт. Попутно мы изучим некоторые из более продвинутых методов, доступных с использованием BitmapData .


Давайте посмотрим на эффект, который мы после. SWF ниже показывает это в виде перехода между изображениями.

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


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

Шум в компьютерной графике — это просто случайное изображение. Если вы откроете Photoshop, создадите новое изображение и заполните холст белым, вы сможете увидеть эффекты обычного шума, выбрав меню « Фильтр»> «Шум»> «Добавить шум…» . Установите значение 100% и при необходимости включите параметр «Монохроматический». Эффект будет выглядеть как статический:

Обычный шум создает статическую текстуру

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

Шум Перлина, с другой стороны, имеет более органичное ощущение, поэтому он был разработан. Вы можете понять это в Photoshop, создав новый документ и выбрав « Фильтр»> «Рендеринг»> «Облака» . Вы сразу увидите разницу:

Рендеринг облаков приводит к шуму Перлина

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

Из-за «контролируемой случайности» шума Перлина, он имеет множество приложений в компьютерной графике, от текстур в трехмерном моделировании до моделирования дыма и огня. Мы воспользуемся «группировкой яркости» шума Перлина, чтобы отобразить дыры в нашей маске.

В Википедии есть небольшая статья о Perlin Noise . В этой статье подробно рассматривается шум в целом и шум Перлина в частности, если вы хотите узнать больше о механике процесса.


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

Начните с простого создания новой папки на жестком диске с именем Perlin Mask для размещения всех файлов, связанных с проектом.


Open Flash Professional (для написания этого руководства использовался CS4, но на шаге 15 вам нужно будет использовать вектор, который доступен только в Flash Pro CS4 +; в конце этого шага я предоставил альтернативу) и создать новый файл ActionScript 3.0 (перейдите в меню « Файл»> «Создать» и выберите « ActionScript 3.0 » из списка).

Сохраните этот файл как PerlinMask.fla в папке вашего проекта.

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


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

Выберите меню «Файл»> «Импорт»> «Импортировать в рабочую область…» , перейдите к изображению на жестком диске и выберите его для переноса во Flash. (Естественно, не стесняйтесь изменить его размер в Photoshop или другом графическом редакторе, прежде чем импортировать его во Flash.)

Мы хотим обратиться к изображению с помощью ActionScript, поэтому нам придется связать его в мувиклип. Выбрав изображение, нажмите F8 и убедитесь, что диалоговое окно «Преобразовать в символ» выглядит следующим образом:

Диалог Преобразовать в символ

Убедитесь, что выбран левый верхний пункт регистрации.

Наконец, дайте экземпляру символа имя экземпляра « image_mc », введя его на панели «Свойства» для изображения.

Установка имени экземпляра

Создайте новый файл AS в папке вашего проекта и назовите его PerlinTest.as . Это будет класс документа для нашего FLA. Эффект маски шума Perlin не будет содержаться в этом файле; скорее, он будет использовать класс, который мы создаем для эффекта, в целях тестирования эффекта при его создании. В конечном итоге это послужит примером того, как использовать эффект в других проектах.

Введите следующий код в файл, который вы только что создали:

1
2
3
4
5
6
7
8
package {
    import flash.display.Sprite;
    public class PerlinTest extends Sprite {
        public function PerlinTest() {
            trace(«PerlinTest»);
        }
    }
}

Вернувшись в файл Flash, убедитесь, что ничего не выбрано, затем введите PerlinTest в поле « Класс» на панели «Свойства», чтобы установить класс PerlinTest в качестве класса документа для FLA.

Установка класса документа

Запустите файл Flash (« Управление»> «Тестировать ролик»> «Тест» или « Command / Control-Enter» ), и вы увидите след на панели «Вывод»:

Привет, PerlinTest

Мы скоро вернемся к классу документов, но мы все еще создаем файлы для нашего маленького проекта.

В папке проекта создайте небольшую иерархию папок, начиная с com . Затем внутри «com» ​​создайте папку activetuts . Наконец, внутри этого создайте папку с именем « эффектами »

Структура папок для класса

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

1
2
3
4
5
6
7
package com.activetuts.effects {
    public class PerlinMask {
        public function PerlinMask() {
            trace(«PerlinMask»);
        }
    }
}

В PerlinTest.as мы создадим экземпляр класса PerlinMask мы только что создали, и протестируем его, чтобы убедиться, что все правильно подключено.

Ниже приведен полный класс PerlinTest с выделенными на этом этапе строками:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
package {
    import flash.display.Sprite;
    import com.activetuts.effects.PerlinMask;
 
    public class PerlinTest extends Sprite {
 
        private var _mask:PerlinMask;
 
        public function PerlinTest() {
            trace(«PerlinTest»);
            _mask = new PerlinMask();
        }
    }
}

Обратите внимание, что нам нужно импортировать класс (строка 3), затем мы объявляем свойство экземпляра (строка 7), чтобы позже мы могли ссылаться на маску. Наконец, мы просто создаем новый экземпляр класса PerlinMask (строка 11).

Запустите фильм снова, и теперь у вас должно быть два следа, указывающих, что мы успешно создаем объект PerlinMask .

Панель «Вывод», объявляющая, что мы успешно создали два объекта.

Теперь мы начинаем серьезно работать над эффектом. Вернитесь к PerlinMask.as, и мы начнем с добавления свойства для хранения объекта DisplayObject мы хотим замаскировать. Вот полный код класса с выделенными новыми строками:

01
02
03
04
05
06
07
08
09
10
11
12
13
package com.activetuts.effects {
 
    import flash.display.*;
 
    public class PerlinMask {
 
        private var _target:DisplayObject;
 
        public function PerlinMask(target:DisplayObject) {
            _target = target;
        }
    }
}

В конечном итоге target будет замаскирована нашей шумовой маской Perlin. Дальнейшее тестирование потребует установки этого свойства, поэтому прежде чем идти дальше, вернитесь в файл PerlinTest.as и обновите строку, в которой мы создаем объект PerlinMask :

1
2
3
public function PerlinTest() {
    _mask = new PerlinMask(image_mc);
}

Мы просто PerlinMask именованный экземпляр на сцене конструктору PerlinMask .

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


Чтобы замаскировать target объект, нам нужен еще один объект DisplayObject . Природа использования шума Перлина в качестве нашей маски означает, что нам придется использовать объект Bitmap в качестве маски. И для работы с Bitmap в коде нам также необходим объект BitmapData . Следующий код поможет нам настроить эти элементы:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.activetuts.effects {
 
    import flash.display.*;
    import flash.geom.*;
 
    public class PerlinMask {
 
        private var _target:DisplayObject;
        private var _mask:Bitmap;
        private var _maskPixels:BitmapData;
 
        private var _rect:Rectangle;
 
        public function PerlinMask(target:DisplayObject) {
            _target = target;
 
            _rect = new Rectangle(0, 0, _target.width, _target.height);
 
            _maskPixels = new BitmapData(_rect.width, _rect.height, true, 0xFF000000);
            _mask = new Bitmap(_maskPixels);
            _mask.x = _target.x;
            _mask.y = _target.y;
            _target.parent.addChild(_mask);
        }
    }
}

Есть дополнительный оператор import поэтому мы можем использовать Rectangle s, и установить три новых свойства. Свойства Bitmap и BitmapData вы знали, пришли, но цель Rectangle теперь не кажется очевидной. Мы будем использовать его позже, и имеет смысл пойти дальше и настроить его сейчас, поскольку мы хотим использовать измерения для объекта BitmapData . Короче говоря, Rectangle будет использоваться для определения области, на которую мы хотим повлиять при выполнении наших эффектов BitmapData . Итак, мы устанавливаем размер нашего target объекта и используем эти размеры.

После того, как мы создали наш объект BitmapData , мы создаем объект Bitmap чтобы мы могли отображать пиксели (здесь я не буду вдаваться в подробности, но объект BitmapData — это просто группа пикселей, или, скорее, данные, представляющие эти пикселей. Bitmap объект — это своего рода объект DisplayObejct предназначенный для отображения пикселей. Поэтому подача Bitmap некоторых пикселей в виде BitmapData является способом просмотра этих пикселей).

Следующие три строки могут показаться немного странными. Но поскольку мы хотим, чтобы Bitmap маскировало target , нам необходимо убедиться, что Bitmap находится в том же месте и в том же контейнере, что и target . Таким образом, мы помещаем их в те же координаты, а затем добавляем Bitmap в список отображения, добавляя его к родительскому target , который помещает его в тот же DisplayObjectContainer что и target . Окончательный результат заключается в том, что два объекта должны быть одинакового размера и в одинаковом положении.

На самом деле, если вы запустите фильм сейчас, вы должны увидеть, что ваше изображение скрыто черным прямоугольником:

Растровое изображение отображается

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

В PerlinMask.as добавьте следующие новые свойства вместе с начальными значениями, установленными в конструкторе (выделены новые строки):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.activetuts.effects {
 
    import flash.display.*;
    import flash.filters.*;
    import flash.geom.*;
 
    public class PerlinMask {
 
        private var _target:DisplayObject;
        private var _mask:Bitmap;
        private var _maskPixels:BitmapData;
 
        private var _rect:Rectangle;
        private var _point:Point;
        private var _blur:BlurFilter;
        private var _operation:String;
 
        public function PerlinMask(target:DisplayObject) {
            _target = target;
 
            _rect = new Rectangle(0, 0, _target.width, _target.height);
 
            _maskPixels = new BitmapData(_rect.width, _rect.height, true, 0xFF000000);
            _mask = new Bitmap(_maskPixels);
            _mask.x = _target.x;
            _mask.y = _target.y;
            _target.parent.addChild(_mask);
 
            _point = new Point(0, 0);
            _blur = new BlurFilter(16, 16, 2);
            _operation = «<«;
        }
    }
}

Мы вернемся к тому, что они делают позже; Достаточно сказать, что мы будем использовать их в методах, вызываемых для объектов BitmapData .

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


Природа нашего эффекта фактически потребует другого объекта BitmapData . Эта вторая BitmapData будет там, где мы создадим шум Перлина. Уже созданный нами объект BitmapData , который отображается объектом Bitmap , в конечном итоге получит пиксели на основе этого другого объекта BitmapData . Сейчас это может показаться излишним, но оно нам понадобится.

На данный момент нам просто нужно объявить свойство, а затем создать новое BitmapData для него в конструкторе:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class PerlinMask {
 
    private var _target:DisplayObject;
    private var _mask:Bitmap;
    private var _maskPixels:BitmapData;
    private var _noise:BitmapData;
 
    private var _rect:Rectangle;
    private var _point:Point;
    private var _blur:BlurFilter;
    private var _operation:String;
 
    public function PerlinMask(target:DisplayObject) {
        _target = target;
 
        _rect = new Rectangle(0, 0, _target.width, _target.height);
 
        _maskPixels = new BitmapData(_rect.width, _rect.height, true, 0xFF000000);
        _mask = new Bitmap(_maskPixels);
        _mask.x = _target.x;
        _mask.y = _target.y;
        _target.parent.addChild(_mask);
 
        _noise = new BitmapData(_rect.width, _rect.height, false, 0xFFFFFF);
 
        _point = new Point(0, 0);
        _blur = new BlurFilter(16, 16, 2);
        _operation = «<«;
    }
 
}

Просто собственность и ее стоимость. Обратите внимание, что этот BitmapData непрозрачен ( false третий параметр); как упоминалось ранее, мы создаем шум Perlin в этом BitmapData , и поскольку сам шум Perlin непрозрачен, нам не нужна прозрачная битовая карта. Вещи будут выполнять прикосновение быстрее с непрозрачными BitmapData а не с прозрачными.


Наконец, мы можем создать шум Перлина! Мы перенесем создание шума в другой метод, называемый reseed() . Этот метод просто создаст шум Перлина. Мы помещаем его в отдельный метод, чтобы его можно было вызывать извне. Каждый раз, когда генерируется шум Перлина, он будет использовать новое случайное начальное число, поэтому эффект будет отличаться каждый раз. Нам нужно убедиться, что этот метод вызывается из конструктора, чтобы убедиться, что шум готов к работе при первом создании объекта.

В классе PerlinMask добавьте вызов reseed() , затем добавьте этот метод:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public function PerlinMask(target:DisplayObject) {
    _target = target;
 
    _rect = new Rectangle(0, 0, _target.width, _target.height);
 
    _maskPixels = new BitmapData(_rect.width, _rect.height, true, 0xFF000000);
    _mask = new Bitmap(_maskPixels);
    _mask.x = _target.x;
    _mask.y = _target.y;
    _target.parent.addChild(_mask);
 
    _noise = new BitmapData(_rect.width, _rect.height, false, 0xFFFFFF);
 
    _point = new Point(0, 0);
    _blur = new BlurFilter(16, 16, 2);
    _operation = «<«;
 
    reseed();
}
 
public function reseed():void {
    var seed:int = Math.random() * 32000;
    _noise.perlinNoise(_rect.width, _rect.height, 2, seed, false, false, 7, true);
    _maskPixels.copyPixels(_noise, _rect, _point);
}

Метод reseed находится в нескольких шагах от завершения. Третья строка метода ( copyPixels ) не является окончательной, она просто используется для того, чтобы мы взяли шум Perlin из второй BitmapData ( _noise ) и скопировали пиксели в первую BitmapData ( _maskPixels ). Это позволяет нам видеть шум Перлина, потому что эти первые BitmapData видны через объект Bitmap .

Идите и протестируйте фильм; вы должны увидеть что-то вроде (но не совсем) следующее:

Шум Перлина

Итак, давайте объясним параметры perlinNoise .

Первые два параметра — это ширина и высота. Это связано с частотой, и эти значения регулируют эту частоту в зависимости от размера изображения. Большие значения будут «распространять» эффект. Если вы _rect.width*10 и _rect.height*10 , вы потеряете много деталей, которые вы видите сейчас:

Более высокие базовые значения

Если вы попробуете _rect.width/10 и _rect.height/10 , вы увидите меньшие «пулы» затенения:

Более низкие базовые значения

Вы можете думать об этом как о каком-то параметре масштаба: чем выше значение, тем выше масштаб шума, чтобы мы были «увеличены» на крошечной части всего шаблона. Нижнее значение сделало нам шаг назад, чтобы мы могли видеть больше паттерна.

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

Третий параметр указан как «октавы» в документации. Он определяет, сколько генераторов шума использовать. Чем выше число, тем больше деталей. Вы увидите более тонкие линии, пронизывающие шум. Это похоже на несколько слоев шума, каждый из которых становится немного более тонким и тонким. Я нахожу, что 2 октавы дают эффект, которого я достиг: меньшее количество более крупных «пулов».

Для быстрого сравнения, вот шум Перлина, генерируемый с 4 октавами:

4 октавы

И 10 октав:

10 октав

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

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

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

Фрактальный шум

Седьмой параметр (где передается число 7) — это параметры канала. Это указывает, какие цветовые каналы использовать. Мое использование 7 является комбинацией каналов RGB. Я решил это путем экспериментов, и это, казалось, дало самый сильный контраст между самым высоким и самым низким значениями яркости в изображении.

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

График шума не в оттенках серого с использованием красного и зеленого каналов

Наконец, последний параметр (который не используется) — это массив точек. Эти точки являются смещением. Существует одно смещение для каждой октавы. Это дает вам несколько интересных вариантов; Обновление значений смещения позволяет плавно прокручивать шум. Прокручивая октавы независимо, вы можете создавать водные эффекты. Однако это не то, что нас интересует, и мы хотим, чтобы наши октавы были объединены в одну текстуру, поэтому мы проверяем, передав одну точку смещения в массиве, что все октавы используют одинаковое смещение.

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


Теперь мы будем использовать другой метод BitmapData чтобы скопировать пиксели из шума Перлина в нашу маску Bitmap , но только определенные пиксели. Метод threshold позволяет нам выполнить сравнение для каждого пикселя в BitmapData и, если условие выполнено, скопировать этот пиксель. Мы установим порог посередине, и вы увидите последствия этого.

Все еще работая в PerlinMask.as , нам нужно удалить строку в reseed() которая копирует пиксели:

1
2
3
4
public function reseed():void {
    var seed:int = Math.random() * 32000;
    _noise.perlinNoise(_rect.width, _rect.height, 2, seed, false, false, 1, true);
}

Затем поместите вызов метода updateMask (мы напишем этот метод далее) в самом конце конструктора:

1
2
3
4
5
6
7
    _point = new Point(0, 0);
    _blur = new BlurFilter(16, 16, 2);
    _operation = «<«;
 
    reseed();
    updateMask();
}

И, наконец, напишите метод updateMask :

1
2
3
private function updateMask():void {
    _maskPixels.threshold(_noise, _rect, _point, _operation, 0xFF666666, 0, 0xFFFFFFFF, false);
}

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

Порог в действии

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

Нижний порог

В конце мы будем анимировать это пороговое значение, чтобы создать наш конечный эффект.

Если вам интересно, вот краткое изложение других параметров.

Первый параметр — это объект BitmapData для которого запускается порог. Мы используем шум Перлина в качестве нашего источника.

Второй и третий параметры являются геометрическими свойствами. Прямоугольник определяет область исходного BitmapData для прохождения через порог. Точка — это местоположение в целевом BitmapData (в данном случае _maskPixels ) для размещения верхнего левого угла этого прямоугольника. Поскольку наш прямоугольник имеет тот же размер, что и наше изображение, и BitmapData шумом Перлина, а точка установлена ​​на (0,0) , мы в основном копируем пиксели очень параллельным способом.

Четвертый параметр — _operation . Эта String сообщает threshold методу, как запустить сравнение. Мы проходим «

Следующий параметр — это пороговое значение, используемое для сравнения. Это целое число ARGB (легче всего представить в шестнадцатеричной записи). Наш источник шума Perlin непрозрачен, поэтому канал «A» должен быть FF . На этом этапе разработки мы жестко кодируем среднее значение для каналов «RGB» (помните, мы генерируем шум в оттенках серого).

Общий эффект между 4-м и 5-м параметрами заключается в том, что если данный пиксель в шуме Перлина меньше (или темнее) серого цвета 0x666666 , то он копируется в _maskPixels . Пиксель копируется в то же место. Но это еще не все, о нет, это еще не все.

Пиксель не просто копируется напрямую. Шестой параметр — это цвет, используемый в копии. По умолчанию 0, и имейте в виду, что это другое значение ARGB, поэтому 0 полностью прозрачный черный. Если мы передадим непрозрачное значение цвета, вы увидите другие эффекты:

Flash AS3 Перлин шумовой эффект

Изображение выше: порог с цветом 0x6600FF00 (полупрозрачный зеленый).

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

Последний параметр — это Boolean значение, указывающее, следует ли копировать пиксели из исходного BitmapData . Вы можете установить это значение true прямо сейчас, чтобы понять, как выглядит шум и как он влияет на то, что копируется. Имейте в виду, что этот параметр предназначен для пикселей, которые не проходят пороговый тест, поэтому вы увидите более светлые пиксели, если установите его в значение true .

Порог с параметром copySource, установленным в true

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

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

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


Мы хотим произвести настройку порога и, следовательно, эффекта маски, так же просто, как установить для свойства percent значение от 0 до 1. Для этого потребуется частное свойство для хранения значения процента, открытый установщик и получатель и некоторая добавленная логика. updateMask .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class PerlinMask {
 
    private var _target:DisplayObject;
    private var _mask:Bitmap;
    private var _maskPixels:BitmapData;
    private var _noise:BitmapData;
 
    private var _rect:Rectangle;
    private var _point:Point;
    private var _blur:BlurFilter;
    private var _operation:String;
 
    private var _percent:Number;
 
    public function PerlinMask(target:DisplayObject) {
        _target = target;
 
        _rect = new Rectangle(0, 0, _target.width, _target.height);
 
        _maskPixels = new BitmapData(_rect.width, _rect.height, true, 0xFF000000);
        _mask = new Bitmap(_maskPixels);
        _mask.x = _target.x;
        _mask.y = _target.y;
        _target.parent.addChild(_mask);
 
        _noise = new BitmapData(_rect.width, _rect.height, false, 0xFFFFFF);
 
        _point = new Point(0, 0);
        _blur = new BlurFilter(16, 16, 2);
        _operation = «<«;
 
        reseed();
        updateMask();
    }
 
    public function reseed():void {
        var seed:int = Math.random() * 32000;
        _noise.perlinNoise(_rect.width, _rect.height, 2, seed, false, false, 7, true);
    }
 
    private function updateMask():void {
        var channelThreshold:uint = 0xFF * _percent;
        var thresh:uint = 0xFF000000 + (channelThreshold << 16) + (channelThreshold << 8) + channelThreshold;
        _maskPixels.threshold(_noise, _rect, _point, _operation, thresh, 0, 0xFFFFFFFF, true);
    }
 
    public function set percent(value:Number):void {
        _percent = value;
        updateMask();
    }
 
    public function get percent():Number {
        return _percent;
    }
 
}

Бизнес свойств должен быть довольно простым: мы объявляем свойство (строка 19) и создаем для него установщик и получатель (строки 53-60). updateMask снова вызывает updateMask , чтобы мы могли перерисовать маску с новым значением процента.

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

Мы берем наше процентное значение и умножаем его на 0xFF или 255 в десятичном виде. Это максимальное значение для одного канала в наших растровых изображениях. Затем нам нужно поместить это одноканальное значение в каналы R, G и B. В то же время нам нужен 0xFF в канале А. Для каналов R, G и B мы берем этот channelThreshold и используем операторы смещения битов, чтобы поместить это значение в каждый из каналов. Затем мы складываем их все и получаем наше фактическое пороговое значение.

Это не совсем интуитивно понятно, поэтому давайте попробуем пройти пример.

Если мы установим percent на .5, при updateMask сначала будет установлено значение updateMask 0xFF * .5 , что равно 0x7F .

Используя bitshift operator take the bits that represent that number, and moves them over to the left by the specified number of places. Thus, 0x7F becomes 0x7F00 , and 0x7F becomes 0x7F0000 . It may be confusing as to why bitshifting 8 places only moves the digits by 2 places, but if you know how binary relates to hexadecimal, this will actually make sense. If it doesn't, I'm sorry, but a further explanation would turn this into a tutorial on base numeral systems. bitshift operator take the bits that represent that number, and moves them over to the left by the specified number of places. Thus, 0x7F becomes 0x7F00 , and 0x7F becomes 0x7F0000 . It may be confusing as to why bitshifting 8 places only moves the digits by 2 places, but if you know how binary relates to hexadecimal, this will actually make sense. If it doesn't, I'm sorry, but a further explanation would turn this into a tutorial on base numeral systems.

(Посмотрите эту презентацию Ли Бримелоу, если вы хотите узнать больше о сдвиге битов.)

Независимо от того, сложение 0xFF000000 + 0x7F0000 + 0x7F00 + 0x7F приводит к 0xFF7F7F7F, который является полностью непрозрачным средне-серым цветом. И затем мы используем это как наш порог, и пиксели, которые темнее, чем те, которые копируются как прозрачный черный, и пиксели, которые светлее, чем те, которые копируются из источника (шум Перлина).

Наконец, проверьте это, перейдя в класс документа ( PerlinTest.as ) и добавив строку для установки percent :

1
2
3
4
public function PerlinTest() {
    _mask = new PerlinMask(image_mc);
    _mask.percent = .3;
}

Вы можете изменить значение на другие значения от 0 до 1 и проверить фильм, чтобы увидеть результаты. Обратите внимание, однако, что один PerlinMask шума PerlinMask , процент от .4 может быть менее очевидным, чем PerlinMask другого PerlinMask от .3. Это часть случайного характера шума. Вы также можете заметить, что около 0,6 или выше, вы начинаете полностью терять шум; то есть весь источник шума уже находится под порогом. Мы обратимся к этому через мгновение.


Мы должны предположить, что мы хотим получить начальное процентное значение при настройке маски. Мы добавим параметр в конструктор, чтобы указать начальное значение для процента. Все еще в PerlinMask , измените конструктор, чтобы добавить параметр и установить свойство.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public function PerlinMask(target:DisplayObject, initialPercent:Number=0) {
    _target = target;
 
    _rect = new Rectangle(0, 0, _target.width, _target.height);
 
    _maskPixels = new BitmapData(_rect.width, _rect.height, true, 0xFF000000);
    _mask = new Bitmap(_maskPixels);
    _mask.x = _target.x;
    _mask.y = _target.y;
    _target.parent.addChild(_mask);
 
    _noise = new BitmapData(_rect.width, _rect.height, false, 0xFFFFFF);
 
    _point = new Point(0, 0);
    _blur = new BlurFilter(16, 16, 2);
    _operation = «<«;
 
    reseed();
    this.percent = initialPercent;
}

В строке 21 мы добавляем параметр со значением по умолчанию, равным 0. Затем в последней строке конструктора (строка 39) я удалил вызов updateMask() и заменил его установкой свойства percent в значение параметра. Так как updateMask из установщика percent , мы можем эффективно заменить его.

Чтобы проверить это, вернитесь на PerlinTest.as и измените код установки:

1
2
3
public function PerlinTest() {
    _mask = new PerlinMask(image_mc, .3);
}

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

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


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

Результатом этого является то, что когда мы устанавливаем процент, скажем, 0,7, у нас уже есть порог, который «утверждает» каждый пиксель, и мы достигаем полностью прозрачную маску немного раньше. Возможно, вы уже заметили это, если вы тестировали различные percent значения.

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

Вопрос в том, как найти наибольшее значение серого. Вероятно, существует множество методов для достижения этой цели, но мы будем использовать метод histogram , предоставленный для объектов BitmapData с Flash 10.

Это требует немного вычислений, но, к счастью, нам просто нужно сделать это один раз для генерации шума Perlin. Итак, в классе PerlinMask обновите метод reseed() следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public function reseed():void {
    var seed:int = Math.random() * 32000;
    _noise.perlinNoise(_rect.width, _rect.height, 2, seed, false, false, 7, true);
 
    var histogram:Vector.<Number> = _noise.histogram()[0];
    var i:Number = histogram.length-1;
    while (i > 0) {
        if (histogram[i] > 0) {
            break;
        }
        i—;
    }
    _maxGrey = i;
}

Вы можете заметить, что последняя строка устанавливает свойство, которое мы еще не определили. Обязательно объявите это с остальными свойствами:

1
2
private var _percent:Number;
private var _maxGrey:Number;

И, наконец, нам нужно использовать это свойство в updateMask :

1
2
3
4
5
private function updateMask():void {
    var channelThreshold:uint = _maxGrey * _percent;
    var thresh:uint = 0xFF000000 + (channelThreshold << 16) + (channelThreshold << 8) + channelThreshold;
    _maskPixels.threshold(_noise, _rect, _point, _operation, thresh, 0, 0xFFFFFFFF, true);
}

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

Метод histogram принимает один параметр, прямоугольник, определяющий область, для которой необходимо выполнить гистограмму для целевых BitmapData (в нашем случае это шум Перлина).В нашем коде (строка 46) мы выполняем histogramна _noise, не потрудившись даже пройти в Rectangleпотому , что по умолчанию использовать всю область изображения.

Более сложная часть заключается в том, что histogramвозвращается многомерный элемент VectorNumbers (a Vectoris a Array, за исключением того, что все элементы в Vectorобъявлены как типизированные с определенным типом данных. Он был введен в Flash Player 10, и, по общему мнению, их следует использовать, когда возможно, так как они быстрее, чем Arrayс).

Этот возврат Vectorимеет в качестве своих элементов четыре элемента Vector, каждый из которых представляет один канал. По порядку, они красные, зеленые, синие и альфа (что немного странно, так как в большинстве других стран мы рассматриваем альфу как первый канал. Хотя с этим ничего не поделаешь). Поскольку мы знаем, что наш шум Перлина имеет оттенки серого, мы можем с уверенностью предположить, что красный, зеленый и синий каналы будут одинаковыми. Итак, мы берем первый элемент из Vectorвозвращенного histogram, и в histogramпеременной у нас есть красный канал.

Этот элемент является другим Vector, и все его элементы Numbers. Есть 256 записей (от 0 до 255). Каждый из этих Numbers представляет значение в этом канале (красный в нашем случае). То есть histogram[0]представляет красное значение 0x00и histogram[255]представляет красное значение 0xFF. Значения, содержащиеся в этих элементах, представляют собой количество пикселей в том, BitmapDataчто имеет это значение красного цвета. Итак, если бы у нас был полностью «черный» красный канал (абсолютно без красного BitmapData), наш Vectorбы выглядел примерно так:

1
2
3
4
histogram[0] = _noise.width * _noise.height;
histogram[1] = 0;
//…
histogram[255] = 0;

Если бы у вас было изображение, где левая половина была заполнена черным, а правая половина — белым, вы получите следующее:

1
2
3
4
5
histogram[0] = _noise.width * _noise.height / 2;
histogram[1] = 0;
//…
histogram[254] = 0;
histogram[255] = _noise.width * _noise.height / 2;

Имейте в виду, что приведенные выше списки кодов являются более иллюстративными; Вы не можете установить гистограмму BitmapData, установив эти Vectorзначения.

Вы видели эти гистограммы в графическом виде, в Photoshop и большинстве других графических редакторов (включая iPhoto):

Образец гистограммы из фотошопа

В этом представлении горизонтальной осью являются значения от 0 до 255 (то есть отдельные слоты в канале Vector), а вертикальная ось представляет «совокупность» этого значения. Приведенное выше изображение является гистограммой, взятой из образца генерации шума Perlin, и вы можете видеть, что все затягивается к верхнему концу. То есть более высокие значения (более яркие пиксели) имеют меньшее количество населения, таким образом, плоская линия.

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

Когда у нас есть этот индекс, мы сохраняем его в _maxGreyсвойстве. Это значение будет зависеть от генерируемого шума Перлина, поэтому оно будет несколько произвольным. Но предположим, что это в конечном итоге 0xA4. Теперь, когда мы запускаем updateMask, мы используем percentзначение on 0xA4, а не 0xFF, и поэтому мы получаем процент между черным и самым ярким пикселем в шуме Перлина. Это динамически генерируемый диапазон, а не просто процент между черным и белым. Нам действительно нужно сделать это только из-за природы шума Перлина, но, надеюсь, вы чему-то научились на этом пути.

Если вам нужно нацелиться на Flash 9, то эта техника не будет работать. Вы можете либо пропустить этот шаг и жить с отключенным процентом, либо самый очевидный подход, который я могу придумать, состоит в том, чтобы по BitmapDataотдельности зациклить пиксели в шуме Перлина , получить их значение и сравнить их вручную, пока не найдете самое высокое значение. Это, безусловно, будет более интенсивным процессом, чем использование histogramподхода, но я приведу его для совместимости с Flash Player 9 (и Flash Professional CS3). Что-то вроде этого:

1
2
3
4
5
6
7
8
9
var w:uint = _noise.width;
var h:uint = _noise.height;
var maxPixel:uint = 0;
for (var j:uint = 0; i < h; j++) {
    for (var i:uint = 0; i < w; i++) {
        maxPixel = Math.max(maxPixel, _noise.getPixel(i, j));
    }
_maxValue = maxPixel >> 16;
}

Я на самом деле не тестировал этот код, так что принимайте его за то, что он стоит.


До сих пор мы делали шум видимым, что удобно, чтобы видеть, что происходит, когда мы развиваемся. Но, в конце концов, мы хотим, чтобы наша _mask Bitmapбыла маской для кого-то другого DisplayObject. Мы уже настроили targetобъект, который в нашем тестовом проекте представляет собой изображение, обернутое в MovieClipвызываемый объект image_mc.

Это не займет много, чтобы получить маску. В PerlinMask.as добавьте выделенные строки в конструктор:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public function PerlinMask(target:DisplayObject, initialPercent:Number=0) {
    _target = target;
 
    _rect = new Rectangle(0, 0, _target.width, _target.height);
 
    _maskPixels = new BitmapData(_rect.width, _rect.height, true, 0xFF000000);
    _mask = new Bitmap(_maskPixels);
    _mask.x = _target.x;
    _mask.y = _target.y;
    _target.parent.addChild(_mask);
 
    _noise = new BitmapData(_rect.width, _rect.height, false, 0xFFFFFF);
 
    _point = new Point(0, 0);
    _blur = new BlurFilter(16, 16, 2);
    _operation = "<=";
 
    reseed();
    this.percent = initialPercent;
 
    _target.cacheAsBitmap = true;
    _mask.cacheAsBitmap = true;
    _target.mask = _mask;
}

Это так же просто, как установить maskсвойство на _target. Но мы также должны установить как _maskи _targetдля уже cacheAsBitmapустановлен true. Когда вы включаете растровое кэширование как для маски, так и для DisplayObjectмаскируемой маски, маска учитывает полупрозрачность.

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

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

Чтобы увидеть последствия этого, протестируйте фильм. В зависимости от значения, переданного в initialPercentin PerlinTest, вы можете увидеть что-то вроде этого:

Изображение маскируется

Измените числовое значение в коде ниже, чтобы увидеть различные эффекты. 0 означает «без маски», поэтому вы увидите все изображение. 1 означает «полная маска», и вы ничего не увидите. Числа между ними обеспечат пятна изображения.


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

Увеличенный по краю маски

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

Мы можем решить обе эти проблемы, применив размытие к битовой карте маски. Вот почему мы создали _blurсвойство ранее.

Идея состоит в том, чтобы применить размытие после копирования пикселей из thresholdметода. Это смягчит эти края, избавит от ступенчатого алиасинга и обеспечит градацию между непрозрачным черным и полностью прозрачным. Это так же просто , как с помощью BitmapData«s applyFilterметод:

1
2
3
4
5
6
private function updateMask():void {
    var channelThreshold:uint = _maxGrey * _percent;
    var thresh:uint = 0xFF000000 + (channelThreshold << 16) + (channelThreshold << 8) + channelThreshold;
    _maskPixels.threshold(_noise, _rect, _point, _operation, thresh, 0, 0xFFFFFFFF, true);
    _maskPixels.applyFilter(_maskPixels, _rect, _point, _blur);
}

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

Второй и третий параметры — это то, к Rectangleчему применяется фильтр, и Pointкуда прямоугольник пикселей будет скопирован в BitmapData. Как обычно (как в нашем приложении, так и в большинстве приложений, связанных с BitmapDataманипуляциями), мы хотим, чтобы весь прямоугольник изображения отображался на (0,0). Поэтому мы используем наши хранимые _rectи _pointобъекты.

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

Идите и протестируйте фильм снова; вы увидите заметную разницу в способе применения маски.

Размытие наносится на маску

На этом наш PerlinMaskкласс закончен (по крайней мере, для целей этого урока). Наши оставшиеся шаги включают в себя обучение тому, как анимировать маску для классного перехода.


Мы будем полагаться на сторонний пакет для управления анимацией. Если у вас уже есть библиотека анимации и вам это удобно, не стесняйтесь ее использовать и пропустите этот шаг. Тем не менее, вам нужно будет выполнить перевод между синтаксисом TweenLite, который вы видите в этом руководстве, и синтаксисом выбранной вами библиотеки анимации.

Перейдите по адресу http://www.greensock.com/tweenlite/, чтобы загрузить версию TweenLite для AS3. Прямая ссылка на почтовый индекс: http://www.greensock.com/as/greensock-as3.zip .

Размер zip-файла составляет около 2 МБ, поэтому при нормальном подключении он должен загружаться довольно быстро. После загрузки распакуйте zip-архив, если ваш браузер не сделает этого автоматически.

В распакованной папке будет файл greensock.swc . Вы можете разместить этот файл в нескольких разных местах.

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

Если у вас настроен глобальный путь к библиотеке, вы можете переместить файл SWC в это место, где он будет доступен для каждого файла Flash, который вы открываете. Если вы хотите его настроить, перейдите в настройки Flash (на Mac: выберите Flash> меню « Настройки» или нажмите Command-U ; на ПК: выберите « Правка»> «Настройки» или нажмите Control-U ). Затем выберите ActionScript из меню слева, затем нажмите кнопку « Настройки ActionScript 3.0…» внизу. Наконец, посередине, где написано « Путь к библиотеке»:нажмите кнопку папки и выберите папку в своей файловой системе, в которую вы поместите глобально доступные SWC. Нажмите OK дважды, чтобы подтвердить ваши предпочтения. И, конечно же, переместите greensock.swc в выбранную вами папку.

Если у вас есть Flash CS3, использовать SWC гораздо сложнее, и обычно проще просто использовать файлы AS вместо SWC. Самое простое, что можно сделать, это просто переместить папку com из загрузки TweenLite в папку вашего проекта.


С TweenLite мы можем начать анимировать маску для перехода. Однако следует обратить внимание на одно соображение: мы не собираемся встраивать анимацию в сам PerlinMaskобъект. У него есть percentсвойство, которое может использоваться в качестве свойства анимации для любого из доступных пакетов анимации. Требование TweenLite / Max, или Tweener, или GTween, или Twease, или Tweensy, или любой другой сторонней библиотеки потенциально может расходиться с любой другой библиотекой анимации, которая может использоваться. Ответственность PerlinMaskзаключается в том, чтобы применить маску и предоставить percentимущество. Если вы хотите, чтобы это было анимировано, это будет ответственностью другого объекта.

Имея это в виду, откройте PerlinTestи импортируйте TweenLite:

1
import com.greensock.*;

А затем измените конструктор:

1
2
3
4
public function PerlinTest() {
    _mask = new PerlinMask(image_mc, 0);
    TweenLite.to(_mask, 1, {percent:1, delay:1});
}

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

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

1
2
3
4
public function PerlinTest() {
    _mask = new PerlinMask(image_mc, 1);
    TweenLite.to(_mask, 1, {percent:0, delay:1});
}

… где мы начинаем маску с 1 и твин до 0, для анимации типа «плавления».

В любом случае, ваш мозг должен более или менее взорваться от удивительного.


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

Для удобства вы можете использовать FLA с именем slideshow.fla, который находится в папке « slideshow-start » пакета загрузки. Если вы хотите начать с нуля, вот что в нем:

  • Есть пять изображений. Для простоты в этом примере эти изображения просто импортируются в документ Flash, каждое из которых состоит из символов и размещается на сцене. Этот эффект может быть легко экстраполирован в систему, которая загружает изображения извне, основываясь на данных, загруженных из XML, но это добавляет уровень сложности, который не имеет ничего общего с PerlinMaskэффектом.
  • Все пять экземпляров изображения имеют имена, на которые будут ссылаться в классе документа для FLA.
  • Все изображения имеют одинаковые размеры и размещены в одном месте.
  • Существует файл с именем SlideShow.as , который пуст, но введен как класс документа FLA.

Скопируйте папку com из исходного тестового проекта в новый проект слайд-шоу, чтобы у нас был доступ к PerlinMaskклассу.

В классе SlideShow добавьте этот код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package {
 
    import flash.display.*;
    import flash.events.*;
    import flash.utils.*;
 
    import com.activetuts.effects.PerlinMask;
    import com.greensock.*;
    import com.greensock.easing.*;
 
    public class SlideShow extends Sprite {
 
        private var _images:Array;
        private var _masks:Array;
        private var _timer:Timer;
        private var _index:uint;
 
        public function SlideShow() {
            _images = [saturn_mc, spiral_mc, mars_mc, hyperion_mc, galacticCenter_mc];
            _masks = [];
            for each (var img:Sprite in _images) {
                _masks.push(new PerlinMask(img, 1));
            }
            _timer = new Timer(5000);
            _timer.addEventListener(TimerEvent.TIMER, onTimer);
            _timer.start();
            _index = 0;
            onTimer(null);
        }
 
        private function onTimer(e:TimerEvent):void {
            var mask:PerlinMask = _masks[_index];
            var image:Sprite = _images[_index];
            setChildIndex(image, this.numChildren-1);
            mask.reseed();
            mask.percent = 1;
            TweenLite.to(mask, 2, {percent:0, ease:Quad.easeOut});
 
            _index++;
            if (_index >= _masks.length) {
                _index = 0;
            }
        }
    }
 
}

После объявлений import и property мы настраиваем все в конструкторе. Сначала мы храним Arrayизображения (изображения являются Spriteобъектами, каждое из которых содержит изображение), а также инициализируем пустой массив для хранения масок объектов. Затем мы зацикливаемся на Arrayизображениях, и для каждого изображения мы создаем связанную маску. В PerlinMaskссылается на данное изображение, а также занимает один и тот же слот в _masksмассиве , как изображение делает в _imagesмассиве.

Следующим шагом является настройка Timer, которая срабатывает каждые 5 секунд. Вначале мы запускаем прослушиватель таймера вручную, чтобы сразу перейти к первому изображению, вместо того, чтобы ждать 5 секунд до срабатывания таймера. Также есть _indexсвойство, которое мы инициализируем равным 0. Это отслеживает положение отображаемого в данный момент изображения в массиве.

Далее, в TIMERобработчике событий, первое, что мы делаем, это собираем ссылки на текущее изображение и маскируем объекты, используя _index. Затем мы помещаем изображение в верхнюю часть стека отображения, чтобы оно исчезло поверх предыдущего изображения. Затем мы просим маску, reseedчтобы мы получили новый переход, удостоверимся, что percentэто 1так, что изображение начинается полностью невидимым, а затем, наконец, запускаем анимацию percentдвижения 0, приводя изображение в действие.

Последняя часть обработчика события увеличивает _indexсвойство, проверяя, находится ли оно в допустимом диапазоне для массива, и переносит его обратно, 0если это необходимо. Это позволяет нашему слайд-шоу идти бесконечно, возвращаясь к началу.


Есть, конечно, тонкости, которые мы могли бы добавить в PerlinMaskкласс. Например, было бы относительно легко добавить некоторые сеттеры и геттеры для степени размытия. Установщик просто установит существующее _blurсвойство blurXи blurYжелаемое значение. Это может быть даже в дополнение к percentдополнительному эффекту.

Кроме того, было бы удобно добавить хотя бы метод получения для _target, чтобы вы всегда могли ссылаться на маскируемый объект из заданной маски. Аналогично, было бы полезно даже добавить установщик для _target, чтобы вы могли переназначить маску среди любого числа DisplayObjects. Мы могли бы, например, не создавать пять маскирующих объектов в проекте слайд-шоу; просто создайте его и переназначьте на текущее изображение при необходимости.

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

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


Все изображения, представленные в пакете загрузки и представленные в изображениях и SWF-файлах на этой странице, предоставлены любезно предоставленными НАСА. Они были отобраны из последних постов на сайте Astronomy Picture of the Day, который можно найти здесь . У них есть RSS-канал , который настоятельно рекомендуется.

НАСА предоставляет свои изображения в открытый доступ, как описано здесь . Для полноты изображения были найдены по следующим URL (где вы также можете прочитать о предмете).


Нам удалось создать довольно крутой способ перехода рисунка из видимого в нет или наоборот. По пути мы исследовали некоторые из более изощренных методов, связанных с BitmapData. Я надеюсь, что вы не только расширили свои знания ActionScript, но и получили эффектный эффект для своего набора инструментов. Спасибо, что пришли поиграть!