FlexiPedia Wiki on Adobe Flex
Флэш Потрошитель - Жизнь вокруг технологииЖизнь вокруг технологии

Флэш Потрошитель этот | тот | 1.0

С 09.09.2002
  • Коллекция багов Flash
  • Ссылки для начинающего аниматора
  • Flex для PHP-разработчиков
  • Как вы используете Flash?

Поток сознания

polovaikin ответил на Что такое матрица:

флеш рипер уже не тот ;)

 
 

7 часов назад

Флэшер-аноним ответил на Что такое матрица:

Картинки можно дополнить текстом.

http://www.chemometrics.ru/materials/textbooks/matrix.htm

20 часов назад

makc3d ответил на Еще один способ заработать на играх:

ща потестим

1 день назад

Rost ответил на Еще один способ заработать на играх:

Fixed.

2 дня назад

7a ответил на BAFPUG revolution!:

Мы были на прошлой неделе в Белоруссии проездом, было очень холодно: ночью в Гомеле -32 %) Спасибо за приглашение, у меня есть сильное желание приехать и послушать доклады, интересные темы, но всё же в следующий раз, когда будет потеплее ;)

2 дня назад

Флэшер-аноним ответил на Релиз Alternativa3D 8.5.0 - с поддержкой GPU!:

Большое спасибо, очень интересно.

4 дня назад

Флэшер-аноним ответил на Cоздание мобильного Flash-приложения "Hello World" - урок для начинающих:

Наконец нашел что-то путное по теме. Начну разжевывать. Несколько дней уйдет, однако.

5 дней назад

Флэшер-аноним ответил на Избранные баги Flash:

Мне пришлось столкнуться с тем, что элементарно не работает комбинация клавиш Ctrl + C и Ctrl + V. Причем переустанавливали Flash и не раз. Может быть, есть здесь те, кто сталкивался с этой проблемой и как-то смог решить ее?

1 неделя назад

makc3d ответил на Избранные баги Flash:

beer good. spam bad!

1 неделя назад

Флэшер-аноним ответил на Избранные баги Flash:

Хотелось бы узнать, Как вы относитесь к пиву? Если положительно, то какое предпочитаете? Если отрицательно, то почему? Просто интересно...

1 неделя назад

Более старые 
Главная › Блоги › Блог makc3d

Особенности Pixel Bender для Flash, или как я учился гнуть пиксели


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

Итак, вы скачали бендер, посмотрели фильтры в комплекте и прочитали заглянули в статьи - пол-дела сделано. Осталась самая малость - сделать что-то с 0-ля своими руками - и можно будет написать статью на флеш риппер Smile

Значит, запускаем бендер и жмём знакомую комбинацию 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.... Sad Чешем в затылке, жмём 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), но будьте готовы к тому что, возможно, придётся потратить немало времени на танцы с бубном.

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

П.с.: вы уже знаете, что breakpoint в этом году станет последним? А я так на него и не съездил ни разу Sad

П.п.с.: Рост, у тебя не работает OpenId.

  • Pixel Bender
  • Статьи
- makc3d, чт, 04/03/2010 - 15:15
  • Блог пользователя makc3d
  • В Жуйк

Комментарии

Макс, спасибо за великолепную статью! Пожалуйста, проверь сейчас - не побилось ли форматирование кода при публикации?

С ОпенАйди разберусь, спасибо, что предупредил Smile

Rost - http://rajaka.net/ 15:18 04/03/10

да вроде нет, но хз... пробуйте сами, сами Smile

makc3d 18:01 04/03/10

Спасибо, отлично Smile

7a - http://nucleart.net 20:18 04/03/10

Хороший урок, объяснен неплохо.
Ошибок нету, все получилось.

Сёма (не проверено) 21:36 04/03/10

Макс, ну ты даешь, а это же вроде должны были проходить )
if (lt != rt) на if (lt.r != rt.r)
ты собрался сравнить два указателя, конечно они разные раз указывают на разные пикселя )
это вам C а не ActionScript

Flop_ (не проверено) 21:44 04/03/10

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.

makc3d 22:24 04/03/10

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, может быть через вопросик.

Ммм.... кстати, вопросик, скорее всего, позволит обойтись и без умножения.

Dan 23:06 04/03/10

классный урок. тоже всё хочу окунуться в этот чудесный мир щейдеров ))) но ноги (руки) не доходят..

k0t0vich - http://www.flasher.ru/forum/blog.php?u=21664 09:35 05/03/10

Не знал про такую классную штучку. Очень интересно!

mycarisgood - http://svitter.ru 12:06 10/03/10
3

Спасибо, многое не знала !))
хороший сайт, добавила в избранное...)

pink (не проверено) 04:34 27/03/10
Примечания: Статус документа => в процессе ·
Статьи · Идеальный клип · Персоналии · Глоссарий (уст.) · Что делать? · К началу ↑
© 2002-2012 Ростиславр · О проекте · Подписка на RSS · α-тестировани невероятного
]]>
]]>
Что такое OpenID?
  • Войти по OpenID
  • Скрыть вход по OpenID
  • Регистрация
  • Запросить новый пароль