Всем привет! Меня зовут Григорий Дядиченко, я уже что-то разрабатываю на Unity десять лет. Давно ничего не писал, и тут собрался с силами и решил, что хочу написать про компьютерную графику. А точнее пройтись по её базе в контексте Unity. Если интересуетесь темой — добро пожаловать под кат!

Всего я в разработке 10 лет (даже чуть больше). Но последние 8 лет я занимаюсь разработкой под заказ. Поэтому задачи у меня довольно разноплановые, но довольно часто это связано с крутой графикой на слабых устройствах. Будь то AR, VR или мобилки. Я когда-то писал статью про оптимизированный для мобилок акрил скажем. Так как проектов у меня обычно много, то часто я отдаю какие-то работы на подряд. И если на игровую логику, вёрстку, адаптивные интерфейсы подрядчиков бывает найти не трудно, то что-то действительно сложно по графике могут сделать единицы. Да и многие плавают даже в базе того как работают графические чипы, видеопамять, да и рендер в целом.
Хочется составить набор статей, который покроет основы компьютерной графики в контексте Unity. Свои рендереры на плюсах, CPU рендеринг и подобные темы я разбирать не хочу, а вот что как и почему работает в движке можно обсудить. И для этого я придумал следующий набор тем, чтобы удобно добавить в закладки и база была под рукой:
1. О графическом конвейере (о работе графического конвейера)
2. О видеокартах (архитектура GPU, работа параллельных вычислений, Warps/Wavefronts, branch divergence, texture fetch)
3. О линейной алгебре (матрицы, пространственные преобразования, проекции, системы координат)
4. О цвете и свете (цветовые пространства, модели освещения, тени)
5. О шейдерах (работа с вертексами, развёртки, оптимизация)
6. О compute шейдерах (симуляция частиц, обработка изображений)
7. О оптимизациях (батчинг, SRP батчер, GPU инстансинг)
Если я о чём-то забыл или что-то ещё интересно - напишите в комментариях. Итак, начнём с простого, о чём написано уже много, поэтому эта статья больше вводная.
Что такое графический конвейер?

Каждый кадр в видеоигре, 3D-приложении или даже интерфейсе — это результат работы графического конвейера (rendering pipeline). Это процесс, в котором геометрия сцены превращается в пиксели на экране. Графический конвейер — это последовательность этапов, через которые проходят 3D-объекты, чтобы превратиться в 2D-изображение. С точки зрения компьютера 2д объекты так же проходят через этот конвейер.
Основные этапы
Вершинный шейдер
Обработки вершинГеометрический шейдер (опционально)
Модификация и генерация новых примитивовКлипинг
Отсечение невидимых частей объектовРастеризация
Преобразование геометрии в пикселиФрагментный шейдер
Расчёт цвета пикселейТесты и смешивание
Работа с Depth, Stencil, Alpha Blending
Вершинный шейдер

Вершинный шейдер (Vertex Shader) — это программа, выполняемая на графическом процессоре (GPU) на этапе графического конвейера, которая обрабатывает каждую вершину геометрического объекта. Он применяет преобразования модель → мир → камера → проекция.
// Пример вершинного шейдера в HLSL
v2f vert (appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex); // Локальные → экранные координаты
o.uv = v.uv;
return o;
}
В вершинной части конвейера часто пишутся деформации вроде анимации воды, колышущейся травы, "дыхания" объектов. И для вершинной анимации.
Хитрый трюк связанный с этой частью конвейера. В вебе и на мобилках, особенно старых, очень медленно работает Skinning, поэтому через вершинные анимации можно оптимизировать это. В текстуру записывается смещение вершин, задаётся правило смещения в зависимости от цвета текстуры. И дальше можно последовательно проигрывать смещая вертексы в шейдере по кадрам приводя 3д модель к нужному состоянию. Работает в разы быстрее скининга на старых устройствах.
Shader "Custom/VertexAnimationTexture" {
Properties {
_MainTex ("Albedo", 2D) = "white" {}
_AnimTex ("Animation Texture", 2D) = "black" {} // R32G32B32A32_Float
_AnimLength ("Animation Length", Float) = 1.0
}
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
uint vertexId : SV_VertexID;
};
struct v2f {
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
};
sampler2D _MainTex, _AnimTex;
float _AnimLength;
v2f vert (appdata v) {
v2f o;
// Вычисляем текущий кадр анимации
float time = frac(_Time.y / _AnimLength); // Зацикленная анимация
float2 animUV = float2(
(float)v.vertexId / (float)_AnimTex_TexelSize.z, // X = vertexId
time // Y = время
);
// Читаем позицию из текстуры
float3 animPos = tex2Dlod(_AnimTex, float4(animUV, 0, 0)).xyz;
// Применяем новую позицию
o.pos = UnityObjectToClipPos(float4(animPos, 1));
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target {
return tex2D(_MainTex, i.uv);
}
ENDCG
}
}
}
Геометрический шейдер

Геометрический шейдер (Geometry Shader) — это этап графического конвейера, который работает между вершинным и фрагментным шейдерами и позволяет динамически создавать, изменять или удалять геометрические примитивы (точки, линии, треугольники).
[maxvertexcount(3)] // Максимум 3 вершины на выходе (треугольник)
void geom(triangle v2f input[3], inout TriangleStream<v2f> stream) {
v2f output;
for (int i = 0; i < 3; i++) {
output.vertex = input[i].vertex + float4(0, 0.5, 0, 0); // Сдвигаем вершины вверх
output.uv = input[i].uv;
stream.Append(output);
}
stream.RestartStrip();
}
Важно: В URP/HDRP геометрические шейдеры поддерживаются, но требуют аккуратной настройки.
Применяется для генерации травы и листвы из точек, для визуализации дебаг параметров (например нормали моделей), разбиения меша на части (эффекты разрушаемости).
Клиппинг

Клиппинг (Clipping) — это процесс отсечения частей графических примитивов (треугольников, линий, точек), которые находятся вне области видимости (например, за границами экрана или вне заданного пространства).
Как работает в конвейере?
-
View Frustum Culling:
Unity автоматически отбрасывает объекты вне пирамиды видимости камеры.
-
Clipping Space:
Вершины преобразуются в Clip Space (координаты от
[-1, 1]
).Если вершина вне этого диапазона — она отсекается.
-
Backface Culling:
Треугольники, повёрнутые "спиной" к камере, не растеризуются.
Клиппинг — ключевой этап рендеринга, который отбрасывает невидимые данные для оптимизации. В современных движках (Unity, Unreal) многие виды клиппинга настраиваются автоматически.
Растеризация

Растеризация (Rasterization) — это процесс преобразования векторных графических примитивов (треугольников, линий, точек) в растровое изображение (пиксели) для отображения на экране.
Как работает растеризация?
На вход подаются примитивы (обычно треугольники после преобразования в экранные координаты).
Определяются пиксели, покрываемые примитивом (с учётом их глубины, формы и размера).
Для каждого пикселя генерируется фрагмент (данные для фрагментного шейдера: цвет, глубина, текстурные координаты и др.).
Растеризация — это мост между математическим описанием сцены и её пиксельным представлением. Современные GPU используют сложные алгоритмы для её ускорения, но разработчикам важно учитывать разрешение, плотность полигонов и настройки антиалиасинга для баланса между качеством и производительностью.
Фрагментный шейдер

Фрагментный шейдер (Fragment Shader) — это программа, выполняемая на GPU для каждого потенциального пикселя (фрагмента) примитива (треугольника, линии, точки) в процессе растеризации. Он определяет окончательный цвет, прозрачность и другие свойства пикселя перед записью в буфер кадра.
// Пример фрагментного шейдера в HLSL
fixed4 frag (v2f i) : SV_Target {
fixed4 col = tex2D(_MainTex, i.uv); // Чтение текстуры
return col * _Color;
}
Применяется для реалистичного текстурирования, расчёта освещения (включая PBR-рендеринг), создания визуальных эффектов (например, dissolve, параллакс-маппинг), стилизации графики (toon-шейдинг, пиксель-арт), постобработки (bloom, размытие) и управления прозрачностью (альфа-обрезание, полупрозрачность). Оптимизированная работа фрагментного шейдера критична для производительности, особенно при сложных материалах или на мобильных устройствах.
Тесты и смешивание

Тесты — это этапы проверки фрагментов перед отрисовкой, включающие:
Тест глубины (Z-test) – отсеивание невидимых пикселей (если
gl_FragDepth
не проходит сравнение с Z-буфером).Тест шаблона (Stencil test) – маскирование областей рендера (например, для зеркал или сложных UI).
Альфа-тест – отбраковка прозрачных фрагментов (через
discard
илиclip()
).
Смешивание (Blending) – комбинирование цвета нового фрагмента с уже отрендеренным в буфере кадра, управляется:
Формулами (например,
SRC_ALPHA, ONE_MINUS_SRC_ALPHA
для стандартной прозрачности).Режимами (аддитивное, мультипликативное, наложение света).
Как это работает?
1. Тесты:
Глубина (Z-test): После фрагментного шейдера GPU сравнивает значение
gl_FragDepth
с содержимым Z-буфера. Если фрагмент "дальше" существующего значения — он отбрасывается.Стенсил (Stencil test): Пиксель проверяется по шаблону в stencil-буфере (например, маска для портала). Если тест не пройден — фрагмент не рисуется.
Альфа-отсечение (Alpha test): Фрагменты с альфа-каналом ниже порога (например,
if (alpha < 0.5) discard
) удаляются до смешивания.
2. Смешивание (Blending):
-
Формула: Цвет фрагмента (
src
) комбинируется с цветом в буфере кадра (dst
) по правилу:final_color = src.rgb * src.a + dst.rgb * (1 - src.a)
Режимы: Аддитивный (
src + dst
), умножение (src * dst
), наложение света (для эффектов bloom).
3. Порядок операций:
Фрагментный шейдер вычисляет цвет.
Применяются тесты (stencil → depth → alpha).
Если фрагмент "выжил" — выполняется смешивание с буфером.
Оптимизация:
Ранний тест глубины (Early-Z) пропускает ненужные фрагменты до шейдера.
Для непрозрачных объектов отключайте blending командой
Blend Off
.
Пример (HLSL в Unity):
Blend SrcAlpha OneMinusSrcAlpha // Стандартная прозрачность
ZWrite Off // Отключает запись глубины для полупрозрачных
Этот механизм обеспечивает корректное наложение объектов и эффектов с контролем производительности.
URP vs HDRP vs Built-in: ключевые отличия
Built-in — пайплайн с базовым Forward/Deferred рендерингом, без оптимизаций под современные GPU. URP — оптимизированное решение для мобильных и ПК среднего уровня: Forward+, SRP Batcher, но без сложных эффектов. HDRP — AAA-рендеринг с Deferred, Ray Tracing и физически точным освещением, но требует мощного железа. Выбор зависит от платформы: URP для мобилок и инди-проектов, HDRP — для фотореализма, а Built-in лучше заменить на URP. Главный плюс Built-in в большой базе реализованных ассетов, который со временем нивелируется.
Заключение
Графический конвейер — это основа рендеринга в реальном времени. Понимание его работы помогает:
Писать эффективные шейдеры.
Оптимизировать производительность.
Выбирать правильный рендер-пайплайн в Unity.
Если вам интересны новости Unity разработки и в целом тема Unity - подписывайтесь на мой блог в телеграм. Я публикую там интересные новости и обзоры на них, свои мысли про бизнес, про фриланс и про разработку. Постараюсь до конца лета дописать серию статей целиком. Плюс лучший показатель того, что надо тема интересна - надо писать. Спасибо за внимание!
supremestranger
Хорошая статья.
Ранний тест глубины и stencil работает только при определенных соблюденных критериях:
- нет discard'а
- нет записи в глубину
- возможно еще что-то платформозависимое
DyadichenkoGA Автор
Ага. Спасибо!