Рендеринг, материалы и шейдеры
URP, Material vs Shader, Shader Graph и почему "разноцветные кубики" дорого.
Чем вообще рисуется кадр
Unity отдаёт сцену в GPU через рендер-пайплайн — последовательность проходов (passes): тени, opaque, transparent, postprocess. Конкретные шаги зависят от выбранного пайплайна (см. главу про стек):
- Built-in RP — старый, монолитный. Forward или Deferred рендеринг по выбору.
- URP — Forward+ по умолчанию, кросс-платформа. Это рекомендованный выбор сейчас.
- HDRP — Deferred + Forward, физически-корректный, для High-end ПК и консолей.
WebGL/WebGPU draw call — это вызов отрисовки буфера вершин с шейдером. Браузер за вас не делает почти ничего: вы сами собираете батчи, посылаете uniforms.
Unity за вас собирает draw call’ы из сцены, сортирует, объединяет (batching), вызывает шейдеры. Вы видите в Frame Debugger список вызовов и можете оптимизировать.
Mesh, Material, Shader — три кита
- Mesh — геометрия: массив вершин, нормалей, UV-координат, индексов. Импортируется из FBX/OBJ/glTF.
- Shader — программа на GPU, описывающая, как из вершин и текстур получаются пиксели.
- Material — экземпляр Shader с конкретными параметрами (текстуры, цвета, числа).
Один Mesh + один Material = один draw call (упрощённо). Если у вас 100 объектов с одинаковым материалом, Unity может слить их в один draw call (Static / Dynamic / GPU Instancing). Если у каждого свой Material — 100 draw call’ов.
Стандартный приём оптимизации: объединить текстуры в один атлас, чтобы все объекты использовали один материал. Это даёт автоматический batching и снижает overhead.
Material Inspector — что вы крутите
Откройте материал — увидите параметры активного шейдера. Для URP/Lit (стандарт):
- Surface Type — Opaque или Transparent. Transparent дороже и требует сортировки.
- Workflow — Metallic или Specular (физически-корректные PBR).
- Base Map — основная цветная текстура (albedo).
- Metallic Map + Smoothness — насколько объект металлический и гладкий.
- Normal Map — карта нормалей для имитации мелкого рельефа без увеличения полигонов.
- Emission — самосветящиеся участки.
- Tiling / Offset — повтор и сдвиг UV.
// Поменять цвет материала из кода (Built-in RP / Standard shader: свойство _Color)
var renderer = GetComponent<Renderer>();
renderer.material.color = Color.red; // создаст instance!
// Для URP/Lit основное свойство называется _BaseColor — .color может не работать ожидаемо:
renderer.material.SetColor("_BaseColor", Color.red);
// Через property block — без создания нового материала, без потери batching
var block = new MaterialPropertyBlock();
renderer.GetPropertyBlock(block);
block.SetColor("_BaseColor", Color.red);
renderer.SetPropertyBlock(block);
Обращение к renderer.material создаёт уникальную копию материала для этого объекта. Это
ломает batching и течёт памятью, если делается часто. Если меняете часто — используйте
MaterialPropertyBlock (как в примере выше) — он не создаёт новый материал.
Shader Graph — визуальный шейдер
Если HLSL вам пока неудобно, в URP/HDRP есть Shader Graph — визуальный редактор шейдеров. В Project: Create → Shader Graph → URP → Lit Shader Graph. Открывается граф, где вы соединяете ноды: текстуры, операторы, output на Surface.
Shader Graph под капотом генерирует HLSL и подходит для большинства gameplay-эффектов: пульсирующее свечение, hologram, dissolve, water surface. Сложные постпроцессы тоже можно собирать.
Render Graph (URP 17, Unity 6)
В Unity 6 / URP 17 рендерер URP по умолчанию работает через Render Graph — декларативный API, где каждый pass описывает свои inputs/outputs (textures, buffers), а движок сам строит граф зависимостей и оптимизирует execution (merge passes, reuse render targets, parallel where possible).
Это влияет на вас, если:
- Пишете custom Renderer Features в URP — старое API (
ScriptableRenderPass.Execute) deprecated; нужно перейти на новыйRecordRenderGraph(). - Используете built-in passes — ничего не делаете, Render Graph под капотом.
- Работаете с HDRP — там Render Graph был с 2021, но в Unity 6 единая модель.
Старый non-Render-Graph путь остаётся как “Compatibility Mode” в URP Asset, но deprecated.
Batching и draw call optimization
Чтобы уменьшить количество draw call’ов, Unity предлагает несколько механизмов в порядке исторической давности:
- Static Batching — для не-движущихся объектов. Их меши при загрузке сцены сливаются в один большой меш. Включается флажком “Static” в Inspector.
- Dynamic Batching — Unity на лету сливает маленькие меши (до ~300 вершин) с одинаковым материалом. Дорогой CPU-проход, в URP отключён по умолчанию.
- GPU Instancing — одним draw call’ом рисуется N экземпляров одного меша с разными матрицами. Включается в материале флажком “Enable GPU Instancing”.
- SRP Batcher — для URP/HDRP. Объединяет рендер объектов с шейдерами, совместимыми с SRP Batcher (большинство URP-шейдеров такие). По умолчанию включён.
- GPU Resident Drawer (Unity 6, главная новинка) — следующий шаг после SRP Batcher. На базе BatchRendererGroup автоматически батчит тысячи повторяющихся мешей (декорации, скалы, листва) без ручной настройки MultiMesh. Значительно снижает CPU-нагрузку на сценах с большим количеством объектов.
Как включить GPU Resident Drawer
Требования жёсткие — пропуск любого пункта приводит к silent fail:
- URP Asset → Rendering → Rendering Path =
Forward+(обязательно; Forward / Deferred не поддерживаются). - URP Asset → Rendering → GPU Resident Drawer =
Instanced Drawing. - SRP Batcher включён в URP Renderer (включён по умолчанию).
- Project Settings → Graphics → Shader Stripping → BatchRendererGroup Variants =
Keep All. Иначе шейдеры урезаются в билде, и GRD молча не работает. - Шейдеры объектов должны быть DOTS Instancing-совместимы:
#pragma target 4.5+ объявленныйDOTS_INSTANCING_ONkeyword. Стандартные URP/Lit/Unlit удовлетворяют.
BatchRendererGroup — низкоуровневый Unity API, на котором GRD построен. GRD прозрачно работает
и со статичной геометрией (Static-флажок), и с динамическими MeshRenderer-инстансами — главное,
чтобы шейдер поддерживал DOTS Instancing.
Вместе с GPU Occlusion Culling (тоже Unity 6) даёт огромный прирост в open-world сценах: скалы за горизонтом не идут в draw call’ы вообще, видимые батчатся в десятки call’ов вместо тысяч.
Frame Debugger
Window → Analysis → Frame Debugger. Останавливает кадр и показывает все draw call’ы пошагово.
Понимать, что туда упёрлось — половина успеха в оптимизации рендера.
Post-processing (Volume Framework)
В URP/HDRP вместо отдельного компонента используется Volume. Это GameObject с компонентом
Volume и привязанным Volume Profile — ассетом с настройками эффектов.
Эффекты:
- Bloom — свечение от ярких пикселей.
- Tonemapping — преобразование HDR в LDR (ACES, Neutral, …).
- Color Adjustments — экспозиция, контраст.
- Vignette — затемнение по краям.
- Depth of Field — размытие вне фокуса.
- Motion Blur — размытие при быстром движении.
- Chromatic Aberration — разделение цветовых каналов по краям.
Volume бывает Global (применяется везде) или Local (с триггер-зоной — эффект только в этой области). Удобно: вход в пещеру — Local Volume с другим tonemapping и vignette.
Bloom + Motion Blur + Vignette + Depth of Field — типичный набор инди-разработчика, который “хочет как в фильме”. На мобильных это легко съест половину бюджета кадра. Включайте только то, что работает на ваш визуальный язык.
В следующей главе — освещение, без которого даже идеальные материалы выглядят плоско.