Каждый, кто пользовался Photoshop, видел и пробовал эффект внешнего свечения слоя. В Photoshop есть две техники для создания этого внешнего свечения. Мягкий и точный. Программное обеспечение было не так интересно для меня, но оно выглядело точным — мне было интересно.
Это выглядит следующим образом: линия в один пиксель. Градиент также представляет собой приблизительное расстояние до ближайшего пикселя на изображении. Это расстояние может быть очень вкусным для создания различных эффектов. Всевозможные границы, пользовательские градиенты и
Примеры эффектов, которые могут быть достигнуты благодаря наличию карты расстояний. В этом примере используется OpenGL+GLSL, написанный на Delphi.
Основная проблема этого блеска — вычислительная сложность при больших размерах. Если у вас есть свечение в 100 пикселей, для каждого пикселя в изображении необходимо проверить 100 * 100 соседних пикселей. Например, для изображения размером 800 * 600 это будет 4 800000000 проверок.
Однако Photoshop не страдает от этого и создает идеально точные свечения даже при больших размерах (до 250). Так что решение есть. И мне было интересно его найти. Я не смог найти в Google быстрый алгоритм для такого свечения. Большинство алгоритмов используют размытие для создания свечения, но вы не можете получить такой эффект с линией в один пиксель, как на изображении, она просто размывается.
Поэтому я собираюсь ехать на велосипеде.
Что мы делаем?
Прежде чем начать, давайте определимся с терминами. Создайте свечение размером 100px для png-изображения. Оригинальное изображение будет включено в файл. Мы также залили фон черным цветом, чтобы сделать его объектом съемки. В оригинале это белое изображение на прозрачном фоне, где все пиксели называются присутствующими и отсутствующими. Альфа существующих пикселей больше нуля и фактически участвует в создании свечения.
Шаг 1.
Пиксель за пикселем, на самом деле нет необходимости искать ближайший существующий пиксель в радиусе 100 пикселей. Каждый существующий пиксель просто отбрасывается на пиксель, который почти прилегает к этому свечению. В этом свечении цвет указывает на расстояние до пикселя. Поэтому изображение свечения "рисуется" для каждого существующего пикселя. Затем "вытягивается" только наименьшее значение, чтобы результирующая точка была наиболее близкой. Отбрасывание несуществующих пикселей сократило время рисования свечения, но оно все равно остается огромным. Что же делать дальше?
Шаг второй.
Если вы внимательно посмотрите, то увидите, что уникальные пиксели не отбрасываются, если четыре стороны точки окружены существующими пикселями. Все отредактированные и нарисованные красные точки будут заменены соседними пикселями в каждой пронумерованной полосе. Поэтому, анализируя соседние точки, можно отбросить многие существующие точки. Результатом этого анализа является следующее изображение. Белый — существующие пункты отклонены. Серый — точки, придающие настоящий блеск. Черный — отсутствие очков. В действительности существует только один контур, на основе которого создается свечение.
Таким образом, наш алгоритм на порядки быстрее, но все еще медленный, как черепаха, по сравнению с Photoshop. 100-пиксельное свечение на изображении выше было создано за две-три секунды.
Шаг третий.
Предыдущая процедура была очень простой. Алгоритм работает намного быстрее, но на пикселях по краям все еще наблюдается дикий перебор. Давайте рассмотрим ситуацию, когда три пикселя, которые вы хотите осветить, находятся рядом друг с другом. Фактически, из трех (красных) пикселей, ярко-красный дает только одну акустическую линию, в то время как все остальные перерисовываются, если рендеринг свечения темно-красного пикселя. Я не буду приводить геометрическое доказательство этого, думаю, оно очевидно для читателя. Если таким образом сдвинуть один пиксель в сторону, то "радиус" с левой стороны будет потерян. Остается только один пиксель, который может приближаться к ярко-красному. Есть также совершенно новая область, где вам нужно сбросить свечение с ярко-красного пикселя. Переместите нижний пиксель в сторону и посмотрите, что произойдет. Снова остаются два луча света. После долгих размышлений и тщательного рассмотрения мы пришли к выводу. Если соседних пикселей не более трех, то пиксель выпускает лучи вбок. Примеры соседних пикселей с индексами: луч слева — нет соседних пикселей с индексами 7, 0, 1; луч сверху слева — нет соседних пикселей с индексами 0, 1, 2; пиксель сверху — нет соседних пикселей 1, 2, 3 и т.д. Кроме того, между лучами есть пиксели. Поэтому они присутствуют только при наличии двух соседних лучей. Другими словами, согласно уже упомянутой диаграмме, в правом верхнем углу находится радиус, а в правом нижнем — радиус. Между ними должен быть нарисован пиксель блеска.
Таким образом, овердрафты могут быть значительно сокращены.
Применение.
Я реализовал этот алгоритм в delphi, используя библиотеку визуализации Vampyre. Сначала я делаю мимолетную подготовку с помощью статей, которые уже есть в моей памяти
Они серые. Увеличьте масштаб, чтобы увидеть их.
Светящийся образ Я достаточно хорошо попал в 16 зон. Функция DrawGlowPart в коде умеет рисовать только те зоны, которые строго определены на основе указателя. Мне пришлось вырезать петли для каждого пояса. Функция DrawGlowPart также может нарисовать полосу с индексом 16. Это один пиксель вокруг каждого существующего пикселя, который нужно нарисовать. На этом изображении видно, что этот пиксель находится слева от ярко-красного цвета.
Результат возни вот такая зловещая картинка: И всего за 150 мс на моей машине. Вот это уже по человечески. Данный алгоритм хорошо ложится на GPU, что я и попытаюсь когда-нибудь на досуге сделать, чтобы получить еще больший прирост и возможность действительно в реалтайме строить такие glow. Увы 150мс это пока ниразу не реалтайм время, но это уже приемлимое для load time.
Фотошоп.
Свечение в Photoshop явно реализовано не по моему алгоритму. Меня не удивит, если я проведу серьезную работу над кодом. Если внимательно посмотреть на свечение от пикселей в Photoshop, то на достаточно хорошем дисплее можно увидеть его "многоугольную" форму. Я полагаю, что разработчики используют граничные пиксели для создания границ полигона, а затем смещают их, чтобы получить полигон. Я также думаю, что это еще больше снизит перегрузку. Если у кого-то есть материалы на эту тему — я с удовольствием их прочитаю.
Ссылки на статью.
1. применение алгоритма. Бинарный файл + исходный код. Для компиляции вам понадобится библиотека VampyreImagingLibrary. Первый параметр — входной файл. Второй — размер свечения (в пикселях). 2. примеры результатов. Свечение не генерируется в реальном времени, но используется предварительно сгенерированное свечение. Рендерер использует OpenGL+GLSL