Режимы блендинга Photoshop в Unity

Режимы блендинга Photoshop в Unity

Последние пару недель я пытался воспроизвести режимы блендинга Photoshop в Unity. Это непростая задача. Несмотря на достижения современных аппаратных средств для графики, блок блендинга по-прежнему не может быть программируемым и, вероятно, так останется еще некоторое время. Некоторые расширения OpenGL ES реализуют данную функциональность, но большинство аппаратных средств и API-интерфейсов этого не делают. Итак, какие у нас варианты?

1) Копия вторичного буфера

Общий подход заключается в том, чтобы скопировать весь вторичный буфер перед блендингом. Так делает Unity. После этого тривиально реализовать любой нужный блендинг в шейдерном коде. Очевидная проблема заключается в том, что до операции блендинга нужно сделать полную копию вторичного буфера. Конечно, существуют оптимизации, такие как копирование только нужных данных для какой-то меньшей текстуры. Но все усложняется, когда у вас много объектов, использующих режимы блендинга. Еще вы можете сделать только одну копию вторичного буфера и повторно использовать ее, но тогда вы не можете складывать результат блендинга разных объектов друг с другом. В Unity это делается через GrabPass. Это подход, используемый модулем Blend Modes.

2) Улучшение блока блендинга

Современные графические процессоры имеют небольшой блок в конце графического конвейера, называемый Слияние выхода (Output Merger). Это аппаратное обеспечение, ответственное за получение вывода пиксельного шейдера и смешение его со вторичным буфером. Оно не программируется, так как это вызвало бы много сложностей (вы можете почитать об этом здесь) и современные GPU такую возможность не предоставляют.

Формулы режима блендинга были получены здесь и здесь. Используйте их в качестве эталона для сравнения с тем, что я даю. Есть много других источников. Только часто забывают упомянуть, что Photoshop не самом деле использует модифицированные формулы и иное количество зажимов, особенно при работе с альфой. Gimp делает то же самое. Это мой опыт воссоздания режимов блендинга Photoshop исключительно для комбинации блока блендинга и шейдеров. Первые несколько режимов просты, но по мере продвижения, нам придется прибегать к все более хитрым трюкам.

Две оговорки прежде чем мы начнем. Во-первых, режимы блендинга Photoshop выполняются в пространстве sRGB, а это означает, что если вы сделаете это в линейном пространстве, они будут выглядеть неправильно. Как правило, это не проблема. Но из-за количества трюков, которые мы будем делать с этими режимами, многие из значений должны выходить за пределы диапазона 0 – 1. Значит, нам нужен буфер HDR для выполнения вычислений. Unity может сделать это, установив HDR в настройках камеры, а также установив Gamma для цветового пространства в Player Settings. Это явно нежелательно, если вы выполняете вычисления освещения в линейном пространстве. В пользовательском движке вы, вероятно, сможете установить настройки по-иному (чтобы обеспечить линейное освещение).

Если вы хотите испытать код совместно с чтением, скачивайте его тут.

А) Затемнение (Darken)

Формула min(SrcColor, DstColor)
Результат шейдера
color.rgb = lerp(float3(1, 1, 1), color.rgb, color.a);
Блок блендинга Min(SrcColor · One, DstColor · One)

режимы блендинга затемнение

По мере приближения альфа к 0 нам надо устремлять минимальное значение к DstColor, заставляя SrcColor быть максимально возможным цветом float3(1, 1, 1)

B) Умножение (Multiply)

Формула SrcColor · DstColor
Результат шейдера
color.rgb = color.rgb * color.a;
Блок блендинга SrcColor · DstColor + DstColor · OneMinusSrcAlpha

режимы блендинга умножение

C) Затемнение основы (Color Burn)

Формула 1 – (1 – DstColor) / SrcColor
Результат шейдера
color.rgb = 1.0 - (1.0 / max(0.001, color.rgb * color.a + 1.0 - color.a)); // max чтобы не получилась бесконечность
Блок блендинга SrcColor · One + DstColor · OneMinusSrcColor

режимы блендинга затемнение основы

D) Линейный затемнитель (Linear Burn)

Формула SrcColor + DstColor – 1
Результат шейдера
color.rgb = (color.rgb - 1.0) * color.a;
Блок блендинга SrcColor · One + DstColor · One

режимы блендинга линейный затемнитель

E) Замена светлым (Lighten)

Формула Max(SrcColor, DstColor)
Результат шейдера
color.rgb = lerp(float3(0, 0, 0), color.rgb, color.a);
Блок блендинга Max(SrcColor · One, DstColor · One)

режимы блендинга замена светлым

F) Экран (Screen)

Формула 1 – (1 – DstColor) · (1 – SrcColor) = Src + Dst – Src · Dst
Результат шейдера
color.rgb = color.rgb * color.a;
Блок блендинга SrcColor · One + DstColor · OneMinusSrcColor

режимы блендинга экран

G) Осветление основы (Color Dodge)

Формула DstColor / (1 – SrcColor)
Результат шейдера
color.rgb = 1.0 / max(0.01, (1.0 - color.rgb * color.a));
Блок блендинга SrcColor · DstColor + DstColor · Zero

режимы блендинга осветление основы

Вы можете увидеть расхождения между версиями Photoshop и Unity в альфа-блендинге, особенно по краям.

H) Линейный осветлитель (Linear Dodge)

Формула SrcColor + DstColor
Результат шейдера
color.rgb = color.rgb;
Блок блендинга SrcColor · SrcAlpha + DstColor · One

режимы блендинга линейный осветлитель

Здесь тоже продемонстрирована “утечка” цвета по краям. Честно говоря, мне больше нравится правая версия, потому что она выглядит более живой. То же относится к Color Dodge. Но это ограничивает отображение 1-к-1 для Photoshop/Gimp.

Все предыдущие режимы блендинга имеют простые формулы, и так или иначе могут быть реализованы с помощью нескольких команд и правильного режима наложения. Однако некоторые режимы блендинга ведут себя в зависимости от условий или имеют сложные выражения (сложные относительно блока блендинга), и нуждаются в переосмыслении. В большинстве случаев необходим двухпроходный подход (с использованием синтаксиса Pass в вашем шейдере). Двухпроходные шейдеры в Unity имеют ограничение в том, что для данного материала не гарантируется, что два прохода будут выполнены прямо один за другим.

Такие режимы опираются на предыдущий проход, поэтому вы получите странные артефакты. Если у вас есть два дублирующих спрайта (как в 2D-игре, например, в нашем случае), сортировка будет неопределенной. Обходной путь для этого заключается в перемещении свойства Order in Layer, чтобы сделать правильную сортировку.

I) Перекрытие (Overlay)

Формула 1 – (1 – 2 · (DstColor – 0.5)) · (1 – SrcColor), if DstColor > 0.5

2 · DstColor · SrcColor, if DstColor <= 0.5

Проход шейдера 1
color.rgb *= color.a;
float3 A = (4.0 * color.rgb - 1.0) / (2.0 - 4.0 * color.rgb);
float3 B = (1.0 - color.a) / ((2.0 - 4.0 * color.rgb) * max(0.001, color.a));
color.rgb = A + B;
Проход блендинга 1 SrcColor · DstColor + DstColor · DstColor
Проход шейдера 2
color.rgb = (2.0 - 4.0 * color.rgb * color.a) * max(0.001, color.a);
Проход блендинга 2 SrcColor · DstColor + DstColor · Zero

режимы блендинга перекрытие

Требуется пояснить, как я разобрался с Overlay. Мы берем первоначальную формулу и делаем ее аппроксимацию через линейный блендинг:

формула для перекрытия

Упрощаем насколько возможно и получаем вот это

упрощение формулы

Получить DstColor · DstColor мне удалось только путем изоляции члена и вычисления его в два прохода, при этом вычитая с обеих сторон один и тот же фактор:

приведение к нужному виду

Но данная формула не учитывает альфа. По-прежнему необходима линейная интерполяция этой большой формулы для альфа, где альфа = 0 должно возвращать Dst. Тогда

выражение с учетом альфа

Если включить последний член в первоначальную формулу, ее по-прежнему можно решить в два прохода. Надо проявить внимание и зажать значение альфа с max(0.001, a), потому что есть вероятность деления на ноль. Окончательная формула

окончательная формула 1

окончательная формула 2

окончательная формула 3

J) Мягкий свет (Soft Light)

Формула 1 – (1 – DstColor) · (1 – (SrcColor – 0.5)), if SrcColor > 0.5

DstColor · (SrcColor + 0.5), if SrcColor <= 0.5

Проход шейдера 1
float3 A = 2.0 * color.rgb * color.a / (1.0 - 2.0 * color.rgb * color.a);
float3 B = (1.0 - color.a) / ((1.0 - 2.0 * color.rgb * color.a) * max(0.001, color.a));
color.rgb = A + B;
Проход блендинга 1 SrcColor · DstColor + SrcColor · DstColor
Проход шейдера 2
color.rgb = (1.0 - 2.0 * color.rgb * color.a) * max(0.001, color.a);
Проход блендинга 2 SrcColor · DstColor + SrcColor * Zero

режимы блендинга мягкий свет

К Soft Light мы применяем практически те же рассуждения, что к Overlay. Это в результате приводит нас к формуле Pegtop. Оба они отличаются от версии Photoshop тем, что у них нет разрывов. В текущей также есть более темная бахрома при альфа-блендинге.

K) Жесткий свет (Hard Light)

Формула 1 – (1 – DstColor) · (1 – 2 · (SrcColor – 0.5)), if SrcColor> 0.5

DstColor · (2 · SrcColor), if SrcColor <= 0.5

Проход шейдера 1
float3 A = (2.0 * color.rgb * color.rgb - color.rgb) * color.a;
float3 B = max(0.001, (4.0 * color.rgb - 4.0 * color.rgb * color.rgb) * color.a + 1.0 - color.a);
color.rgb = A / B;
Проход блендинга 1 SrcColor · One + DstColor · One
Проход шейдера 2
color.rgb = max(0.001, (4.0 * color.rgb - 4.0 * color.rgb * color.rgb) * color.a + 1.0 - color.a);
Проход блендинга 2 SrcColor · DstColor + SrcColor * Zero

режимы блендинга жесткий свет

Hard Light имеет очень тонкий хак, который позволяет ему работать и делать блендинг с альфой. В первом проходе мы делим на магическое число, только чтобы умножить на него же во втором проходе! Потому что когда альфа = 0, должен получиться DstColor и все окрасится в черный.

L) Яркий свет (Vivid Light)

Формула 1 – (1 – DstColor) / (2 · (SrcColor – 0.5)), if SrcColor > 0.5

DstColor / (1 – 2 · SrcColor), if SrcColor <= 0.5

Проход шейдера 1
color.rgb *= color.a;
color.rgb = color.rgb &gt;= 0.5 ? 1.0 / max(0.0001, 2.0 - 2.0 * color.rgb) : 1.0);
Проход блендинга 1 SrcColor · DstColor + SrcColor · Zero
Проход шейдера 2
color.rgb = color.rgb &lt; 0.5 ? (color.a - color.a / max(0.0001, 2.0 * color.rgb)) : 0.0;
Проход блендинга 2 SrcColor · One+ SrcColor · OneMinusSrcColor

режимы блендинга яркий свет

M) Линейный свет (Linear Light)

Формула DstColor + 2 · (SrcColor – 0.5), if SrcColor > 0.5

DstColor + 2 · SrcColor – 1, if SrcColor <= 0.5

Результат шейдера
color.rgb = (2 * color.rgb - 1.0) * color.a;
Блок блендинга SrcColor · One + DstColor · One

режимы блендинга линейный свет

Источник: http://www.elopezr.com/photoshop-blend-modes-in-unity/

Понравилась статья? Поделиться с друзьями:
Автор natalya
Переводит для Вас самые интересные статьи про разработку игр. По образованию физик-программист. Техническими переводами начала подрабатывать еще на старших курсах и постепенно это переросло в основное занятие. Интересуется гуманитарными технологиями, пробует себя в журналистике.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *