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

9 Янв
2012

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

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


Прежде всего, стоит понимать, что расчёт этих пост-эффектов не может быть выполнен на видеокарте. Связано это с одним замечательным ограничением, за которое мы и любим 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-эффект — высветление картинки и как пощупать буфера.
По материалам Хабрахабр.



загрузка...

Комментарии:

Наверх