Всем привет! Меня зовут Григорий Дядиченко, я уже что-то разрабатываю на 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 — подписывайтесь на мой блог в телеграм. Я публикую там интересные новости и обзоры на них, свои мысли про бизнес, про фриланс и про разработку. Постараюсь до конца лета дописать серию статей целиком. Плюс лучший показатель того, что надо тема интересна — надо писать. Спасибо за внимание!
ссылка на оригинал статьи https://habr.com/ru/articles/927304/
Добавить комментарий