Розробка кастомних шейдерів постобробки графіки
Вбудовані Volume ефекти в URP та HDRP — Bloom, Color Grading, Vignette — закривають базові потреби, але як тільки потрібен нестандартний візуальний ефект (тепловий марево над асфальтом, хроматична аберація з кастомною кривою, pixel art posterization, outline detection по depth buffer) — починається територія кастомних Renderer Feature та повноекранних Blit Shader'ів.
У Unity 2022+ архітектура кастомного постпроцесу змінилася. Старий підхід через OnRenderImage та CommandBuffer.Blit застарів. Новий стандарт — ScriptableRendererFeature + ScriptableRenderPass з Blitter.BlitCameraTexture.
Як влаштований ScriptableRendererFeature в URP
ScriptableRendererFeature — це точка входу для додавання кастомного render pass в URP pipeline. Він реєструє ScriptableRenderPass, який виконується в заданому RenderPassEvent (BeforeRenderingPostProcessing, AfterRenderingPostProcessing тощо).
Усередині ScriptableRenderPass.Execute() правильний паттерн у Unity 6 / URP 17:
Blitter.BlitCameraTexture(cmd, source, destination, material, passIndex);
Це замінює старий cmd.Blit(src, dst, mat). Різниця не тільки синтаксична: Blitter.BlitCameraTexture правильно обробляє VR single-pass instanced rendering та уникає UV flip артефактів у WebGL.
Матеріал для Blit використовує Full Screen Shader Graph (тип графа Fullscreen Shader в URP ShaderGraph). У ньому доступна URP Sample Buffer нода — вона читає Color, Depth, Normal або Motion Vectors з G-Buffer. Це ключове: без цієї ноди неможливо зробити глибиноорієнтовані ефекти (outline по depth, custom depth of field) через ShaderGraph без написання HLSL.
Outline по Depth та Normal
Outline detection — частий запит для стилізованих ігор. Наївний підхід: обводка через другий pass з збільшеними вершинами — працює для solid об'єктів, ламається на складній геометрії та не дає outline по силуету сцени.
Правильний підхід — повноекранний edge detection через Depth та Normal buffers:
Depth-based edge: читаємо глибину в поточному пікселі та сусідніх (4 або 8 сусідів через Sample Texture 2D з offset). Якщо різниця глибини перевищує поріг — піксель на краю. Roberts Cross або Sobel operator — стандартні фільтри. У ShaderGraph: чотири Sample Texture ноди з ручними UV offset'ами (+1/-1 піксель через Texel Size ноду), обчислюємо gradient magnitude.
Normal-based edge: аналогічно по Normal buffer. Дає лінії там, де геометрія резко змінює напрямок нормалі — це ребра та складки поверхні, які depth edge не видить. Комбінація depth + normal edge — стандарт для cel-shading outline.
Проблема depth outline: глибина в буфері не лінійна, вона записана в logarithmic або reversed-Z форматі залежно від платформи. Пряме порівняння depth значень без linearization дає нерівномірну товщину лінії. Потрібно лінеаризувати через LinearEyeDepth(depth, _ZBufferParams) — у ShaderGraph це Linear Eye Depth нода.
Тепловий марево та distortion ефекти
Screen-space distortion для heatwave або magic portal: Blit шейдер, який зміщує UV поточного кадру по Normal map (flowing animated normal texture). Проста ідея, але в ній приховується проблема з render order.
Distortion шейдер повинен працювати з Opaque Texture (снімком екрана до прозорих об'єктів). У URP це _CameraOpaqueTexture. Якщо ефект застосовується як повноекранний pass — він видить весь екран. Якщо потрібен локалізований distortion (тільки над конкретним об'єктом, наприклад гарячим асфальтом) — потрібна Distortion Renderer Feature, яка рендерить distortion objects в окремий render texture, потім застосовує його як screen-space mask у фінальному Blit.
Для HDRP distortion — вбудований Distortion render queue (окремий від Transparent). Об'єкти з Distortion enabled автоматично потрапляють в distortion accumulation pass. Це простіше, ніж URP кастомний підхід.
Типичні помилки при розробці кастомних постефектів
Wrong RenderPassEvent. Постефект поставлений в BeforeRenderingPostProcessing — але повинен бути після Bloom, інакше Bloom застосовується поверх кастомного ефекту. Порядок подій в URP: Opaques → Skybox → Transparents → Post Processing → UI. Вибір RenderPassEvent визначає, де в цій черзі станеме кастомний pass.
Відсутність Depth Priming Mode. На деяких мобільних GPU (особливо Mali) глибинний буфер може бути недоступний при читанні в кастомному pass, якщо Depth Priming Mode в URP Renderer настроєна на Auto замість Forced. Це призводить до чорного екрана або некоректних значень глибини без будь-яких помилок в консолі.
VR incompatibility. Кастомний шейдер постобробки, написаний без підтримки XR, ламається в VR — відрисовується тільки для одного ока або створює double-vision артефакт. Blitter.BlitCameraTexture автоматично обробляє це, але Custom Function ноди з ручним UV розрахунком вимагають UNITY_STEREO_EYE_INDEX_POST_VERTEX macro.
| Тип постефекту | Строк |
|---|---|
| Простий fullscreen ефект (колірна корекція, blur) | 1–3 дні |
| Outline по depth/normal + настройка | 3–5 днів |
| Distortion ефект (heatwave, portal) | 3–6 днів |
| Складний складений ефект (кілька passes) | 1–2 тижні |
Вартість розраховується індивідуально. Потрібно знати рендер-пайплайн, платформу та наявність VR підтримки.





