Яндекс.Метрика

    Ни о чём

    Как реализовать пост-эффект Bloom во Flash

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

    Часть первая. Блюр



    Важное ограничение


    Прежде всего, стоит понимать, что расчёт этих пост-эффектов не может быть выполнен на видеокарте. Связано это с одним замечательным ограничением, за которое мы и любим GPU — одновременно рассчитывается цвет нескольких пикселей. Из-за этого, вы не сможете точно узнать цвет «соседнего» пиксела, так необходимого при реализации этих эффектов. Поэтому, рассчитывать окончательную картинку будем на CPU. Это позволяет значительно сгладить неровности и некрасивые рёбра трёхмерной графики, и значительно улучшает картинку. Скорость выполнения пост-эффекта целиком и полностью зависит от размеров view, соответственно наибольшее падение производительности будет наблюдаться при full-screen картинке. Так же, хочется упомянуть, что ни к какому конкретному движку эти эффекты не относятся и могут быть выполнены на любом изображении, с которого можно снять BitmapData. В данной статье будет рассматриваться применение размытия к картинке, полученной при помощи движка alternativa3d. Кто всё ещё читает — приглашаю под хабракат.
    Подготовка картинки


    В первую очередь, нам необходимо попросить видеокарту вернуть нам отрендеренную картинку в удобном для нас виде. Для этого, необходимо вызвать метод Context3D.drawToBitmapData(). Аргументом этот метод принимает объект типа BitmapData, обновляемый с каждым вызовом метода present() того же контекста. Если вы пользуетесь движком alternativa3d — необходимо просто выставить свойство renderToBitmap экземпляра View в true. Тогда отрендеренный bitmapData можно будет получить из свойства canvas того же экземпляра.

    private function itsRenderTime(e:Event):void { //No, dad, no!
    camera.view.renderToBitmap = true;
    camera.render(stage3d);
    realisePostEffect(camera.view.canvas);
    }


    Собственно размытие


    Общепринято использовать следующий алгоритм размытия изображения:

    1. Сначала мы уменьшаем полученную от рендерера картинку, чтобы уменьшить нагрузку на процессор — ему придётся выполнять одну и ту же довольно непростую операцию над каждым пикселом.
    2. Следующим действием нам нужно узнать усреднённый цвет каждого из соседних текущему пикселу и смешать его с цветом текущего в равных долях. Если используется «усиленное» размытие — узнаются цвета соседей соседей; влияние цвета таких пикселей обратно пропорционально расстоянию (возможно даже его квадрату, не вдавался в подробности) до них от текущего.
    3. Пункт 2 может быть повторён несколько раз для усиления качества размытия. Теоретически, он может быть выполнен произвольное количество раз, однако опытным путём наилучшее соотношение качество/производительность было установлено при выполнении второго пункта трижды. Умные статьи говорят, что таким образом достигается наибольшее приближение к размытию по Гауссу, но в этой статье подробностей вам не видать, так что просто поверьте на слово.
    4. Полученная размытая картинка снова растягивается до размеров отрендеренной, чтобы соответствовать.
    5. Размытая и отрендеренная картинка смешиваются с учётом [полу]прозрачности размытой.


    С пунктами 2 и 3 прекрасно справляется встроенный фильтр флеша под названием BlurFilter. 1 и 4 обеспечивается применением матрицы аффиных преобразований при вызове метода draw экземпляра bitmapData. Ну а пункт пятый легко реализовать настройкой параметров конструктора экземпляра ColorTransform. Остаётся только показать картинку на экране, с чем без посторонней помощи справляется добавленный к [ребёнку] Stage экземпляр View библиотеки альтернативы:

    private function realisePostEffect(canvas:BitmapData):void {
    var _scaling:int = 4 //Уменьшаем нагрузку на процессор в 16 (4*4) раз.
    var _power:int = 4 //Просим фильтр учитывать цвет соседей вплоть до четвёртого порядка
    var _alpha:Number = .5 //Размытая и отрендеренная картинки будут смешиваться в равных долях
    var _quality:int = 3 //Размытие каждого пиксела будет произведено трижды, во имя богини красоты текущей религии разработчика

    var _cT:ColorTransform = new ColorTransform(1, 1, 1, _alpha);
    var _bloomData:BitmapData = new BitmapData(canvas.width / _scaling, canvas.height / _scaling, true, 0);
    var matrix:Matrix = new Matrix();
    matrix.scale(1 / _scaling, 1 / _scaling);
    _bloomData.draw(canvas, matrix, _cT);
    _bloomData.applyFilter(_bloomData, _bloomData.rect, new Point(), new BlurFilter(_power, _power, _quality));
    matrix.invert();
    canvas.draw(_bloomData, matrix);
    }


    Изображение без пост-обработки и с ней:


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

    Coming soon: HDR-эффект — высветление картинки и как пощупать буфера.