флеш урок
Create a Retro CRT Distortion Effect Using RGB Shifting
В этом уроке вы научитесь, как отделять три разных цветных канала у изображения для того, чтобы создать эффект RGB -сдвига. Я также покажу вам несколько графических приемов для имитации старого CRT дисплея.
Просмотр конечного результата
Здесь пример эффекта, который мы будем создавать:
Основная цель этого урока - это достичь эффекта сдвига, но я также продемонстрирую, как создать CRT строки развертки, шум, и гистограммы roll bar.
Шаг 1: О RGB для изображений
Каждое изображение, высвечиваемое на экране вашего компьютера, использует красный, синий и зеленый цвета. При смешивании этих трех цветов в разных соотношениях ваш компьютер может создавать другие цвета спектра.
Если три цветных канала неправильно выровнены, картинка может быть составлена некорректно, и вы начнете видеть края отдельных каналов, размытые за пределами контуров картинки .
Этого в точности мы хотим достичь в этом уроке; разделить картинку на ее три цветных канала и затем трансформировать каждый индивидуально для того, чтобы создать эффект искажения. Давайте сделаем это!
Шаг 2: Создаем Title Screen
Вам нужно создать графику, для которой мы будем применять эффект. Я решил создать заголовок на экране для видео-игры , но вы можете сделать ту графику , которую вы хотите.
Создайте новый мувиклип, назовите его ‘titleScreen’ и поместите ваш title screen (или другую графику) внутри.
Я думаю, что-то ретро-тематическое лучше всего работает с таким эффектом, так как он напоминает мне старые неисправные дисплеи. Я создал свой title screen с фонтом Commodore 64 Pixeled. Я добавил фильтр Glow к тексту, чтобы он выглядел жирным и пористым на CRT.
Итак вы закончили с дизайном, добавьте мувиклип titleScreen на сцену и дайте ему инстанс имя ‘titleScreen’.
Шаг 3: Создаем класс RGBShift
Создайте новый Actionscript файл, назовите его ‘RGBShift.as’. Сохраните этот файл в той же директории, где ваш основной файл Flash. Добавьте этот код, чтобы создать оболочку класса:
package {
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.display.BitmapDataChannel;
import flash.display.BlendMode;
import flash.events.Event;
import flash.geom.Point;
public class RGBShift extends Sprite {
private var _centerX:Number;
private var _centerY:Number;
// конструктор
public function RGBShift(dObj:DisplayObject) {
}
}
}
Вы чувствуйте себя некомфорто с кодом, основанным на понятии класса?
Этот код в действительности не делает еще ничего. Первые 10 строк - импорт внешних классов, которые нам будут нужны. У меня есть две приватные пременные, названные ‘_centerX’ и ‘_centerY’ (я использую подчеркивание, чтобы обозначить приватные переменные). Эти две переменные будут хранить x и y координаты центра нашей графики.
Замечу, что функция конструктора (пустая сейчас) принимает DisplayObject. Это позволит нам использовать любой тип DisplayObject для этого эффекта (MovieClip, Sprite, Bitmap, и т.д.) мы собираемся использовать мувиклип titleScreen на сцене, но можно использовать этот класс применительно к любому дисплей объекту для дальнейшего использования.
Шаг 4: Добавляем функцию createBMD
Мы сделали наш класс доступным для приема любого DisplayObject, но в действительности нам нужен объект BitmapData, чтобы создать эффект RGB -сдвига. Давайте создадим функцию, которая может создавать BitmapData из DisplayObject.
Добавьте эту функцию к вашему RGBShift классу ниже конструктора:
private function createBMD(dObj:DisplayObject):BitmapData {
// создаем новый BitmapData объект размером с наш DisplayObject
var bmd:BitmapData = new BitmapData(dObj.width, dObj.height,
true, 0xFF000000);
// рисуем display object в bitmap data
bmd.draw(dObj);
return bmd;
}
Давайте посмотрим, что делает эта функция. Первая строка использует ширину и высоту дисплей объекта для создания нового прозрачного объкта BitmapData такого же размера, что и DisplayObject. Далее, она рисует DisplayObject в BitmapData. В завершение она возвращает BitmapData.
Шаг 5: Добавляем фунцию createRGB
Именно здесь имеет место отделение цвета. Добавьте эту функцию к вашему классу:
private function createRGB(dObj:DisplayObject):Array {
var bmd:BitmapData = createBMD(dObj); // создаем bitmapData из display object
// создаем новый bitmap data объект для каждого цветного канала
var r:BitmapData = new BitmapData(bmd.width, bmd.height, true, 0xFF000000);
var g:BitmapData = new BitmapData(bmd.width, bmd.height, true, 0xFF000000);
var b:BitmapData = new BitmapData(bmd.width, bmd.height, true, 0xFF000000);
// копируем данные из каждого канала в соответствующую битмапдату
r.copyChannel(bmd, bmd.rect, new Point(),
BitmapDataChannel.RED, BitmapDataChannel.RED);
g.copyChannel(bmd, bmd.rect, new Point(),
BitmapDataChannel.GREEN, BitmapDataChannel.GREEN);
b.copyChannel(bmd, bmd.rect, new Point(),
BitmapDataChannel.BLUE, BitmapDataChannel.BLUE);
// возвращаем массив с битмапдатами для трех цветных каналов
return [r, g, b];
}
Эта функция также принимает DisplayObject. Она затем передает его в функцию createBMD(), которую мы написали в предудущем шаге, которая преобразовывает его в BitmapData. Далее мы создаем три новых прозрачных объкта BitmapData; отдельно для каждого цвета. Мы создаем их точно такого же размера, как наш исходный объект BitmapData (из DisplayObject).
Затем мы используем метод для битмапдаты copyChannel() , чтобы скопировать один цветной канал из исходной битмапдаты в каждый из трех новых объектов BitmapData .
Последняя строка просто возвращает три новых объекта BitmapData в виде массива.
Шаг 6: Используем функцию createRGB в конструкторе
Теперь, когда мы имеем createBMD и createRGB в классе, работающие вместе, давайте разместим их для использования. Добавим это первой строкой кода в функцию конструктора для класса RGBShift:
var rgbBMD:Array = createRGB(dObj);
Эта строка передает DisplayObject в функцию createRGB(). createRGB() использует функцию createBMD() для преобразования его в BitmapData и затем разделяет ее на три отдельных объекта BitmapData (объект для каждого канала). В завершение она возвращает массив этих трех объектов в наш локальный массив rgbBMD . Понятно? Отлично.
Шаг 7: Создаем битмапы из каналов RGB
Теперь у нас есть массив из трех объектов BitmapData. Нам нужно создать Bitmap из каждого, чтобы высветить их на экране. Добавим цикл for в функцию конструктора RGBShift ниже последней строки, которую мы только что добавили:
for (var i:int=0; i < rgbBMD.length; i++) {
var bmp:Bitmap = new Bitmap(rgbBMD[i]);
bmp.smoothing = true;
var container:Sprite = new Sprite(); // контейнер спрайт
container.addChild(bmp); // добавляем Bitmap в дисплей лист спрайта
// найдем центр
_centerX = bmp.width/2;
_centerY = bmp.height/2;
// центр картинки
bmp.x = 0 - _centerX;
bmp.y = 0 - _centerY;
container.x = _centerX;
container.y = _centerY;
addChild(container); // добавляем спрайт в дисплей лист
}
Большинство из этого очень просто. Давайте рассмотрим это.
- Для каждого объекта BitmapData в нашем массиве rgbBMD мы создаем новый битмап Bitmap.
- Мы устанавливаем сглаживание равным true, таким образом мы можем масштабировать и поворачивать его без пикселизации. (строка 23)
- Далее мы создаем контейнер Sprite и добавляем новый битмап в дисплей лист контейнера. (строки 25 и 26)
- Сейчас мы начнем использовать переменные _centerX и _centerY. Мы установим каждую в центр битмапа, разделив ширину и высоту пополам.
- Мы используем эту центральную точку, чтобы сместить битмап внутри контейнера Sprite, и затем, чтобы переместить контейнер Sprite на сцене. Я объясню почему в следующем шаге.
- Окончательно, мы добавляем контейнер Sprite на сцену (запомните, это контейнер для каждого из наших трех цветных каналов).
Шаг 8: Почему мы используем контейнер Sprite?
Вы могли бы создать этот эффект без контейнера Sprite, добавив битмапы прямиком на сцену. Я люблю заключать их в контейнер, потому что это дает возможность легко контролировать точку трансформации, когда вы делаете вещи, подобные масштабированию и повороту.
Это нормально, когда вы производите масштабирование или поворот объекта, эта трансформация происходит относительно точки (0,0) для этого объекта. Обычно я хочу, чтобы трансформация была применена относительно центра объекта.
Замечу, что в последней секции мы устанавливаем x и y для битмапов равными минус половине ширины и высоты. Это размещает битмап так, что его центральная точка находится в точке 0,0 в контейнере Sprite. Если вы производите какие-либо преобразования с контейнером Sprite , оно будет производиться относительно точки 0,0 контейнера, которая сейчас - центр нашего битмапа.
Единственная проблема - это то, что нижний угол нашего битмапа сейчас виден, поэтому я установлю для контейнера Sprite x и y равными половине высоты и ширины битмапа, чтобы возвратить все обратно на правильную позицию.
Шаг 9: Класс RGBShift
Здесь RGBShift класс так выглядит на данный момент :
package {
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.display.BitmapDataChannel;
import flash.display.BlendMode;
import flash.events.Event;
import flash.geom.Point;
public class RGBShift extends Sprite {
private var _centerX:Number;
private var _centerY:Number;
// конструктор
public function RGBShift(dObj:DisplayObject) {
var rgbBMD:Array = createRGB(dObj);
for (var i:int=0; i < rgbBMD.length; i++) {
var bmp:Bitmap = new Bitmap(rgbBMD[i]);
bmp.smoothing = true;
var container:Sprite = new Sprite(); // контейнер спрайт
container.addChild(bmp); // добавляем Bitmap в дисплей лист спрайта
// находим центр
_centerX = bmp.width/2;
_centerY = bmp.height/2;
// center the image
bmp.x = 0-_centerX;
bmp.y = 0-_centerY;
container.x = _centerX;
container.y = _centerY;
addChild(container); // добавляем спрайт в дисплей лист
}
}
private function createBMD(dObj:DisplayObject):BitmapData {
// создаем новый объект BitmapData размером с наш DisplayObject
var bmd:BitmapData = new BitmapData(dObj.width,
dObj.height, true, 0xFF000000);
// рисуем display object в bitmap data
bmd.draw(dObj);
return bmd;
}
private function createRGB(dObj:DisplayObject):Array {
var bmd:BitmapData = createBMD(dObj); // оздаем bitmapData из display object
// создаем новый объект bitmap data для каждого цветного канала
var r:BitmapData = new BitmapData(bmd.width, bmd.height,
true, 0xFF000000);
var g:BitmapData = new BitmapData(bmd.width, bmd.height,
true, 0xFF000000);
var b:BitmapData = new BitmapData(bmd.width, bmd.height,
true, 0xFF000000);
// копируем данные из каждого канала в соответствующую bitmap data
r.copyChannel(bmd, bmd.rect, new Point(),
BitmapDataChannel.RED, BitmapDataChannel.RED);
g.copyChannel(bmd, bmd.rect, new Point(),
BitmapDataChannel.GREEN, BitmapDataChannel.GREEN);
b.copyChannel(bmd, bmd.rect, new Point(),
BitmapDataChannel.BLUE, BitmapDataChannel.BLUE);
// возвращаем массив с bitmap data для 3 цветных каналов
return [r, g, b];
}
}
}
Шаг 10: Создаем класс основного документа
Итак, мы имеем класс RGBShift, но как мы используем его? Начнем создавать новый Actionscript файл, назовем его Main.as, далее добавим этот код:
package {
import flash.display.MovieClip;
public class Main extends MovieClip {
public function Main() {
var rgb = new RGBShift(titleScreen); // создаем новый RGBShift из titleScreen
removeChild(titleScreen); // удаляем оригинал title screen со сцены
// добавляем его на сцену
addChild(rgb);
}
}
}
Здесь мы создаем новый экземпляр RGBShift класса и передаем ему мувиклип titleScreen со сцены. мы больше не нуждаемся в этом мувиклипе, поэтому мы удаляем его со сцены и добавляем новый экземпляр RGBShift вместо него.
Теперь мы должны связать этот класс с нашим Flash документом. Вернитесь во Flash и установите класс документа 'Main'.
Шаг 11: Тестируем
Вы имеете сейчас возможность протестировать ваш Flash файл (Control -> Test Movie) без получения ошибок и замечаний.
Хмм, что-то смотрится не очень правильно, не так ли?
Что здесь происходит? Мы имеем слоями три цветных канала, один поверх другого, но все они не комбинируются и не смешивают цвета, поэтому мы только видим верхний слой (синий). Давайте сейчас это пофиксим.
Шаг 12: Изменяем Blend Mode
Чтобы получить цветные каналы правильно смешанными, нам нужно изменить их BlendMode на SCREEN. Мы хотим только изменить режим смешивания для второго и третьего слоев. Мы оставим первый (нижний) слой нормальным, а смешаем другие два слоя в нем.
Добавим этот код в цикл for в функции конструктора класса RGBShift :
if(i>0) {
// установим режим смешивания SCREEN для второго и третьего изображения
bmp.blendMode = BlendMode.SCREEN;
}
Здесь проверка, чтобы убедиться, что текущее изображение - не первая картинка (0) и затем устанавливается свойство blendMode равным SCREEN.
Шаг 13: Тестируем снова
Протестируйте ваш мувик снова, и вы сможете увидеть что-то, что выглядит идентично вашему мувиклипу titleScreen.
Я знаю, что вы думаете; 'Было проделано много работы, чтобы заново создать ту же самую графику, что мы уже имели здесь.'
Но теперь графика создана из трех объектов, которые мы можем трансформировать индивидуально, чтобы создать наше искажение. Поэтому оставьте свое нытье и давайте продолжим...
Шаг 14: Загружаем библиотеку Tweener
Мы собираемся использовать библиотеку Tweener для создания нашей анимации. Загрузите ее
Чтобы использовать Tweener, поместите основную папку 'caurina' в ту же самую директорию, где ваш файл Flash и добавьте эти операторы импорта вверху в классе RGBShift:
import caurina.transitions.Tweener;
Шаг 15: Добавляем файл randRange
Я использую эту функцию randRange просто, чтобы генерировать рандомные целые числа внутри заданного диапазона. Вы можете добавить эту функцию в класс RGBShift, но я использую эту функцию так часто, что мне нравится хранить ее в отдельном файле, чтобы легко ее использовать в разных проектах.
Создайте новый файл Actionscript, назовите его 'randRange.as' в той же папке, где ваш основной файл Flash. Добавьте этот код:
package {
// возвращается рандомное число из заданного диапазона (инклюзивного)
public function randRange(min:int, max:int):int {
var randomNum:int = Math.floor(Math.random() * (max - min + 1)) + min;
return randomNum;
}
}
Как вы можете видеть, здесь только одна функция, обернутая в package. Сейчас мы можем использовать эту функцию так, как если бы она была частью нашего класса.
Шаг 16: Добавляем функцию distort()
Здесь-то и происходит магия. Добавьте эту функцию в класс RGBShift:
private function distort(img:Sprite):void {
Tweener.addTween(img, {
y: randRange(_centerY-3, _centerY+3), // рандомизируем сдвиг по y
time:randRange(1,2) /10, // рандомизируем время
alpha: randRange(8,10) /10, // рандомизируем альфа
transition:"easeInOutSine",
onComplete:distort, // когда закончится, начинаем искажение заново
onCompleteParams:[img]
}
);
}
Мы собираемся выполнять эту функцию distort() в каждом из наших цветных каналов отдельно, чтобы создать эффект искажения.
Функция принимает Sprite (один из наших контейнеров цветных каналов). Затем она стартует анимацию Tweener в канале, используя случайное значение Y (между -3 и 3), и рандомную длину времени (между 1 и 2 секундами). Это заставит каждый канал смещаться вверх и вниз на разные величины с разными скоростями.
Замечу, что я использую переменную _centerY здесь снова , чтобы сдвинуть значение Y. Мы также будем использовать твин к рандомному значению alpha (между .8 и 1) чтобы заставить каждый канал немного мерцать. Когда твин закончится, мы используем свойство onComplete, чтобы вызвать ту же самую функцию distort() снова. Используя onCompleteParams мы посылаем ей тот же самый спрайт цветного канала. Это заставляет функцию искажения выполняться в цикле снова и снова для наших цветных каналов.
Смотрите, что я вам рассказал..? Магия!
Чтобы стартовать этот цикл искажений, нам нужно вызвать ее один раз в каждом из наших спрайтов цветных каналов. Добавим эту строку в конец цикла for в функции конструктора RGBShift:
distort(container); // стартует искажение битмапа
Шаг 17: Эксперимент
Сейчас вы имеете возможность протестировать ваш мувик и посмотреть эффект искажения в действии.
Лично мне нравится еле различимый сдвиг по Y , что мы получили здесь, но вы можете делать много различных сумасшедших искажений теперь, вы можете анимировать отдельно каналы.
Чтобы поэкспериментировать с искажением, вы можете просто модичфицировать свойства и значения при вызове Tweener в функции distort. Посмотрите документацию по Tweener, чтобы иметь полное представление о свойствах твинов.
Здесь пример простого искажения, которое я создал, просто добавив еще несколько свойств при вызове Tweener:
Посмотрите на функцию distort(), которая создает этот эффект:
private function distort(img:Sprite):void {
Tweener.addTween(img, {
y: randRange(_centerY-3, _centerY+3), // рандомизируем сдвиг по y
x: randRange(_centerX-10, _centerX+10),
time:randRange(1,2) /10, // рандомизируем время
scaleX: randRange(9,11)/10, //рандомизируем масштаб по x
alpha: randRange(5,10) /10, // рандомизируем alpha
transition:"easeInOutSine",
onComplete:distort, // когда закончится, стартуем икажение снова
onCompleteParams:[img]
}
);
}
Шаг 18: Усиление CRT эффекта
Вы можете остановиться здесь, если хотите. На этот момент разделение RGB и искажение должны работать.
Чтобы усилить CRT эффект, я подумал, что нам нужно добавить еще несколько графических эементов. В следующих нескольких шагах мы добавим линии развертки, черный переливающийся bar, некоторую статику, и блестящее отражение.
Шаг 19: Добавляем линии развертки
Создайте новый мувиклип на сцене, назовите его 'lines'. Внутри него нарисуйте горизонтальную линию в 1 пиксел которая охватывает всю ширину вашего мувика. Установите цвет обводки в черный с альфа 40% .
Теперь копируем и вставляем эту линию сверху , двигаем ее вниз на 2 пиксела каждый раз, до тех пор, пока все ваши линии не покроют всю высоту вашего мувика. Если вы захотите эффект с линией в 1 пиксел, тогда расстояние в 1 пиксел пред следующей линией.
Шаг 20: Добавляем Rolling Bar
Теперь мы добавим черный Rolling bar. Создайте новый мувиклип, назовите его 'bar'. Внутри нарисуйте черный сплошной прямоугольник, который охватывает всю ширину вашего мувика. Сделайте его около 40 пикселов высотой. Установите Color Style мувиклипа Alpha = 30%.
Шаг 21: Анимируем Rolling Bar
Создайте новый мувиклип, назовите его 'animatingBar' и поместите ваш клип бара внутри. Создайте короткий motion tween - движение бара от верха вашего мувика к низу. Эта анимация будет повторяться, чтобы дать нам эффект крутящегося бара.
Поместите клип animatingBar на сцену. Выделите его и добавьте фильтр blur. Отсоедините X и Y blur установки и установите Blur Y =20 , а Blur X = 0.
Установите режим смешивания = Overlay. Он подобен режиму смешивания Screen, который мы использовали ранее, но не точно то же самое.
Шаг 22: Создаем статичную картинку
Создайте новый файл в Photoshop с тем же размером, что и ваш мувик. Заполните слой background нейтральным серым цветом (#808080). Выберите Filter > Noise > Add Noise...
Установите фильтр 100%, Gaussian, Monochromatic.
Сохраните картинку как 'noise.jpg'. Если у вас нет Photoshop, вы можете получить мой 'noise.jpg' из файла-исходника .
Шаг 23: Анимируем статику
Импортируйте картинку noise.jpg в ваш файл flash. Создайте новый мувиклип, назовите его 'noise' и добавьте картинку в него. Создайте новый ключевой кадр в кадре 2 (F6) и поверните картинку на 180 градусов. Создайте другой ключевой кадр в кадре 3 и переверните картинку горизонтально (Modify > Transform > Flip Horizontal). Создайте четвертый ключевой кадр в кадре 4 и снова поверните картинку на 180 градусов. Теперь у нас есть 4-ех кадровая анимация мерцающей статики.
Вы можете сгенерировать этот эффект шума , используя ActionScript, но это выходит за рамки этого урока.
Шаг 24: Добавляем отражение
Создайте новый мувиклип на сцене, назовите его 'shine'. Внутри него нарисуйте большой овал, который занимает половину высоты вашего мувика и расположен вверху. Выделите верхнюю часть овала и удалите ее.
Измените заливку на линейный градиент и установите ее так, чтобы она была от белого 20% alpha сверху до белого 5% alpha снизу. Захватите верх шейпа и потяните его немного вверх, чтобы придать ему легкой кривизны.
Шаг 25: Пофиксим слои элементов
Если вы протестируете сейчас ваш мувик, вы не увидите никакой новой графики, которую мы только что добавили, потому что слои RGB были добавлены сверху всего. Чтобы пофиксить это, перейдите в класс Main и измените эту строку:
addChild(rgb);
На эту:
addChildAt(rgb, 0);
Это добавит объект RGBShift в самый низкий слой дисплей листа, ниже всей другой графики.
Перевод kedicik
Источник
Это сообщение отредактировал MOHCTEP - Сегодня 00:04