20 часов назадКартинки можно дополнить текстом.
http://www.chemometrics.ru/materials/textbooks/matrix.htm
2 дня назадМы были на прошлой неделе в Белоруссии проездом, было очень холодно: ночью в Гомеле -32 %) Спасибо за приглашение, у меня есть сильное желание приехать и послушать доклады, интересные темы, но всё же в следующий раз, когда будет потеплее ;)
5 дней назадНаконец нашел что-то путное по теме. Начну разжевывать. Несколько дней уйдет, однако.
1 неделя назадМне пришлось столкнуться с тем, что элементарно не работает комбинация клавиш Ctrl + C и Ctrl + V. Причем переустанавливали Flash и не раз. Может быть, есть здесь те, кто сталкивался с этой проблемой и как-то смог решить ее?
1 неделя назадХотелось бы узнать, Как вы относитесь к пиву? Если положительно, то какое предпочитаете? Если отрицательно, то почему? Просто интересно...

С превеликим запозданием начал я недавно изучать эту технологию. И вот, наконец-то стало что-то получаться, и возникло желание поделиться с такими же начинающими своей радостью. Однако, погуглив по рунету, я обнаружил тут и там давным давно написанные статьи. Посему вместо очередного поста об основах технологии вниманию публики предлагается разбор одного полёта (мысли), а так же грабли особенности технологии, на которые успел наткнуться за это короткое время.
Итак, вы скачали бендер, посмотрели фильтры в комплекте и прочитали заглянули в статьи - пол-дела сделано. Осталась самая малость - сделать что-то с 0-ля своими руками - и можно будет написать статью на флеш риппер 
Значит, запускаем бендер и жмём знакомую комбинацию Ctrl+N. Это магическое действие создаёт простенький шаблон вида
kernel NewFilter
< namespace : "Your Namespace";
vendor : "Your Vendor";
version : 1;
description : "your description";
>
{
input image4 src;
output pixel4 dst;
void
evaluatePixel()
{
dst = sampleNearest(src,outCoord());
}
}это, по сути, уже рабочий исходник, в котором можно до поры забить на всё и сосредоточиться на написании функции evaluatePixel(). Из всего множества функций, которые можно заметить в статьях и примерах, evaluatePixel - единственная работающая во флеше, что на самом деле значительно упрощает жизнь нам, начинающим.
Далее, проверьте настройки - в меню Build Turn on flash player warnings and errors, иначе рискуете потратить кучу времени на написание кода работающего в IDE, но не в вашем swf. Решить эту проблему раз и навсегда можно, заглянув в меню Edit и выбрав пункт Preferences. Там же советую не отходя от кассы зайти в закладку Image Window и расчекать Render on idle - иначе IDE будет впустую тратить вычислительные мощности на измерение fps вышего шедевра, который никак не связан с тем, что вы увидите в swf.
Следующим шагом предстоит определиться с эффектом, который вы хотите запрогать. Начните с чего-нибуть полегче - например, усильте красный цвет в 2 раза:
evaluatePixel()
{
dst = sampleNearest(src,outCoord());
dst.r *= 2;
}Нажимаем кнопку Run и встречаем нашу первую красную мессагу
ERROR: (line 17): 'assign' : cannot convert from 'const int' to 'float'
которая говорит нам о том, что бендер не умеет превращать 2 в 2.0 - через пару дней вы исчерпаете свой запас матерных выражений из-за этой жестокой типизации, ну а пока это всего-лишь интересная особенность. Исправляем, давим кнопку Run - и в бендере появляется выбранное вами изображение с противным розовым оттенком. Хорошо, но впереди ещё долгий путь, а в результате нам светит какая-то фигня, которую легко сделать через ColorMatrixFilter или даже ColorTransform. Облом. Хочется наваять что-то эдакое, чтобы не так пóшло, но тем не менее просто и никто раньше не делал...
На этом месте вам стоит пойти попить чаю и прикинуть варианты. В моём случае выбор пал на треугольник Серпинского.
Глядя на него кажется, что тут не обойтись без какой-то мутной рекyрсии, а из краем уха/глаза услышанного/прочитанного вам уже известно, что никакими вызововами функций в бендере "для флеш" и не пахнет. Не стоит пугаться. Как говорят буржуи, снимать шкуру с кота можно по-разному. В данном случае, воспользуемся достижением теории клеточых автоматов под номером 90. Для тех, кто прогуливал математику, напомню, что клеточные автоматы - это набор значений плюс правило рассчёта этих значений через соседние. В данном случае, набором значений будет строка пикселей, а правилом рассчёта - следующая таблица:
три соседних пикселя в строке 111 110 101 100 011 010 001 000 новое значеине пикселя в центре 0 1 0 1 1 0 1 0
Это уже похоже на то, что вполне можно запрогать в бендере. По сути (которую в деталях понимают лишь хардкорные математики), мы заменяем рекурсию на линейный процесс и, следовательно, для отрисовки треугольника нам придётся применять наш фильтр многократно. Примечательно, что бендер позволяет нам сымитировать это в IDE с помощью графов - XML файлов, в которых можно выстроить фильтры в цепочку нужное количество раз. Однако начинающим я не советую этим пользоваться по двум причинам: 1 графы не экспортируются во флеш - придётся всё время переключаться между графом и фильтром - и 2 графы глотают сообщения об ошибках, заменяя их на Internal error в строке 0. Итак, будем кодить на ощупь.
Рассмотрев вимательно таблицу, можно заметить, что новое значение определяется только крайними пикселями в тройке, и наша функция evaluatePixel() обещает быть довольно простой:
evaluatePixel()
{
float2 at = outCoord ();
pixel4 lt = sampleNearest (src, float2 (at.x + 1.0, at.y - 1.0));
pixel4 rt = sampleNearest (src, float2 (at.x - 1.0, at.y - 1.0));
// сравниваем пиксели сверху слева и справа
if (lt != rt)
// не равны - ставим "1" (чёрный пиксель)
dst = pixel4 (0.0, 0.0, 0.0, 1.0);
else
// равны - ставим "0" (белый пиксель)
dst = pixel4 (1.0, 1.0, 1.0, 1.0);
}Чтобы убедиться, что этот чудо-фильтр работает, можно загрузить белую битмапу с двумя чёрным точками через одну (101) - фильтр заменит их на две точки с тройным интервалом строкой ниже (10001):

Итак, момент истины - идём в меню Филе, выбираем Экспорт - ура, всё прошло гладко. Теперь, в нашей любимой IDE создаём новый AS3 проект и пастим пишем код:
public class Rule90 extends Sprite {
private var b:BitmapData;
private var f:ShaderFilter;
public function Rule90 () {
// делаем белую битмапу
b = new BitmapData (465, 465, false, 0xFFFFFF);
// ставим в верхней строке чёрный пиксель
b.setPixel (233, 0, 0);
// добавляем на сцену
addChild (new Bitmap (b));
// делаем наш фильтр
[Embed(source='90.pbj', mimeType='application/octet-stream')] var F:Class;
f = new ShaderFilter (new Shader (new F));
// вешаем цикл
addEventListener (Event.ENTER_FRAME, loop);
}
private function loop (e:Event):void {
// в котором применяем фильтр к битмапе
b.applyFilter (b, b.rect, b.rect.topLeft, f);
}
}Запускаем, и FUUUUU.... картинка следующего вида:

Почесав в затылке, приходим к выводу, что всё логично: т.к. фильтр применяется ко всей области, на краях слева, справа и сверху вполне ожидаемо возникают глюки благодаря нашим +1 и -1 в координатах, передаваемых в sampleNearest(). Решение очевидно - ограничиваем область в applyFilter():
private function loop (e:Event):void {
var r:Rectangle = b.rect; r.inflate (-1, -1);
b.applyFilter (b, r, r.topLeft, f);
}
Запускаем, и - правильно - FUUUUU....
Чешем в затылке, жмём F1, снова чешем, переписываем по-другому... в конце концов в сердцах пишем
private function loop (e:Event):void {
b.applyFilter (b, new Rectangle, new Point, f);
}после чего понимаем, что познакомились с очередной особенностью технологии - ShaderFilter полностью игнорирует параметры applyFilter().
Совет дня: если не удаётся указать область ввода ShaderFilter-у, не отчаивайтесь - вы всегда можете указать её Shader-у. Для этого придётся вернуться в бендер и дорисовать нашему фильтру параметр:
parameter float4 rect
<
minValue:float4 (0.0, 0.0, 0.0, 0.0);
maxValue:float4 (2048.0, 2048.0, 2048.0, 2048.0);
defaultValue:float4 (0.0, 0.0, 100.0, 100.0);
>;
void
evaluatePixel()
{
float2 at = outCoord ();
if ((at.x >= rect.x) && (at.y >= rect.y) &&
(at.x <= rect.p) && (at.y <= rect.q)) {
// в прямоугольнике - наш простой код
pixel4 lt = sampleNearest (src, float2 (at.x + 1.0, at.y - 1.0));
pixel4 rt = sampleNearest (src, float2 (at.x - 1.0, at.y - 1.0));
if (lt != rt)
dst = pixel4 (0.0, 0.0, 0.0, 1.0);
else
dst = pixel4 (1.0, 1.0, 1.0, 1.0);
} else {
// за периметром - оставляем картинку без изменений
dst = sampleNearest (src, at);
}
}Запустив эту версию в бендере, мы обнаружим четыре ползунка справа от картинки - тягая их туда-сюда, можно "в живую" посмотреть, как изменяется картинка под действием фильра.
Э-э-эх, назад в AS3, дописываем в конструктор волшебную строчку
f.shader.data ["rect"] ["value"] = [0 + 1, 0 + 1, 465 - 1, 465 - 1];
запускаем, и - правильно - FUUUUU....

Hемаловажная особенность технологии заключается в том, что фильтры, правильно работающие в бендеровской IDE, совершенно не обязаны так же работать и в swf. Здесь нет единого рецепта, каждый случай уникален. В моём случае было достаточно заменить if (lt != rt) на if (lt.r != rt.r), но будьте готовы к тому что, возможно, придётся потратить немало времени на танцы с бубном.
Вот и всё на сегодня. Покорённый фильтр можно увидеть здесь, а дочитавшим до сюда полагается какая-нибудь халява... но в голову ничего не приходит, уж звыняйте 
П.с.: вы уже знаете, что breakpoint в этом году станет последним? А я так на него и не съездил ни разу 
П.п.с.: Рост, у тебя не работает OpenId.

Комментарии
Макс, спасибо за великолепную статью! Пожалуйста, проверь сейчас - не побилось ли форматирование кода при публикации?
С ОпенАйди разберусь, спасибо, что предупредил
да вроде нет, но хз... пробуйте сами, сами
Спасибо, отлично
Хороший урок, объяснен неплохо.
Ошибок нету, все получилось.
Макс, ну ты даешь, а это же вроде должны были проходить )
if (lt != rt) на if (lt.r != rt.r)
ты собрался сравнить два указателя, конечно они разные раз указывают на разные пикселя )
это вам C а не ActionScript
A почему в IDE работает? Это не указатели потом, а float4. Вот чувак из Адоба пишет:
there is a bug in the Flash Player runtime which means that comparing two float4s does not work properly. We've logged it, it will get fixed.
if - дорогая операция в шейдерах.
Мне кажется, лучше будет ввести три коэффициента: white, black, dontChange.
Каждый, соответственно, от 0.0 до 1.0.
А точнее или 0.0 или 1.0
А если совсем точнее, то только один из них 1.0, а остальные 0.0
И соответственно:
dst = pixel4 (0.0, 0.0, 0.0, 1.0) * black + pixel4 (1.0, 1.0, 1.0, 1.0) * white + sampleNearest (src, at)*dontChange;Вот как лучше посчитать коэффициенты без if я на вскидку не скажу.
Может быть через bool, может быть через вопросик.
Ммм.... кстати, вопросик, скорее всего, позволит обойтись и без умножения.
классный урок. тоже всё хочу окунуться в этот чудесный мир щейдеров ))) но ноги (руки) не доходят..
Не знал про такую классную штучку. Очень интересно!
Спасибо, многое не знала !))
хороший сайт, добавила в избранное...)