Разбор Как работает видеокарта в играх: рендеринг, шейдеры, растеризация и Z-buffer

  • Автор Автор Sysadmin, Tehnari.ru
  • Дата публикации Дата публикации
  • Время прочтения статьи Время прочтения статьи 7 мин. чтения

Как видеокарта рисует графику в играх: от 3D-вершин до пикселей, Z-buffer, антиалиасинг, Ray Tracing и DLSS​


В играх картинка выглядит как «реальный мир», но внутри это поток математики и данных, которые графический процессор (GPU) гоняет десятки раз в секунду. Сцена, похожая на станцию и паровоз из Red Dead Redemption 2, на самом деле может состоять из 2.1 млн вершин, собранных в 3.5 млн треугольников, с 976 цветами/текстурами и виртуальным солнцем, которое освещает всё это хозяйство.

Главное: эти «вершины, треугольники, цвета и свет» — просто нули и единицы, которые GPU превращает в кадр через классический рендер-пайплайн. У него три базовых этапа, которые десятилетиями остаются фундаментом почти любой 3D-графики:
  • Vertex Shading (работа с вершинами и перевод 3D → 2D)
  • Rasterization (определение, какие пиксели покрывает треугольник)
  • Fragment Shading (освещение, блики, тени — «делаем красиво»)

Схема рендера в одном абзаце​

Сначала GPU берёт геометрию 3D-сцены и «проецирует» её на плоскость экрана (vertex shading). Потом выясняет, какие пиксели на дисплее принадлежат каждому треугольнику (rasterization). Затем для каждого пикселя рассчитывает освещение и внешний вид материалов (fragment shading). Параллельно решается «что ближе к камере», чтобы дальнее не рисовалось поверх ближнего — это делает Z-buffer.

GPU render pipeline: основной поток CPU→GPU (Vertex Shading→Rasterization→Depth Test+Z-buffer→Fragment Shading→Post-processing), опционально Ray Tracing, затем DLSS upscaling to 4K перед Display.

Схема GPU render pipeline: CPU/движок отправляет команды на GPU, дальше идут Vertex Shading → Rasterization → Depth Test + Z-buffer → Fragment Shading → Post-processing. Ray Tracing подключается как опциональная ветка (пунктир), а DLSS выполняет апскейл до 4K перед выводом на Display. Сплошные стрелки — основной поток, пунктир — дополнительные стадии.​


Шаг 1. Vertex Shading: как 3D превращается в 2D​

Vertex shading начинается с простой идеи: в 3D-мире есть объекты (модели), есть камера (как глаз игрока), и есть «окно» — плоскость, на которую мы хотим получить 2D-картинку (view screen).

Пример с той же сценой:
  • В сцене 1,100 разных моделей, но поле зрения камеры сокращает количество объектов, которые реально нужно рендерить, до 600.
  • Один локомотив может состоять из 382 тыс. вершин и 762 тыс. треугольников, с 9 материалами (цветами/поверхностями).

Ключевой момент: объект «двигают» не целиком. На практике GPU гоняет каждую вершину отдельно, потому что именно вершины задают форму треугольников.

Чтобы перенести вершину из 3D на 2D-экран, последовательно делаются 3 преобразования:
  • из model space в world space (где модель стоит в мире: позиция/масштаб/поворот)
  • из world space в camera space (где и как повернута камера)
  • из перспективы камеры на view screen (проекция в 2D)

Vertex transform: 3D→2D — путь вершины v(x,y,z,1) через Model Space → World Space → View/Camera Space → Clip Space → Perspective divide (деление на w) → NDC → Viewport transform → Screen Space, выход screen X,Y + depth Z для Z-buffer.

Как Vertex Shading превращает 3D в 2D: вершина проходит цепочку пространств (Model → World → View/Camera → Clip), затем выполняется Perspective divide (деление на w), координаты попадают в NDC и через Viewport transform переводятся в Screen Space. На выходе получаем screen X,Y и depth Z — глубину, которая дальше используется в Z-buffer.​


Это делается через матрицы преобразований: берутся координаты вершины X/Y/Z, параметры модели (позиция/масштаб/поворот), параметры камеры и её поле зрения, всё это перемножается — и на выходе получаются:
  • X и Y — где вершина окажется на «экране»
  • Z (глубина) — насколько далеко это от камеры (это пригодится для Z-buffer)

Когда три вершины преобразованы — один треугольник уже «лежит» на плоскости экрана. Дальше то же самое делается для миллионов вершин, пока вся видимая часть сцены не окажется в 2D.

Почему GPU это тянет​

GPU заточен под огромные объёмы однотипных операций над кучей данных. В примере из источника речь о GPU с 10,000ish вычислительных ядер, способных выполнять до 35 триллионов операций 32-битного умножения и сложения в секунду. За счёт распараллеливания вершин и данных по ядрам сцена может обновляться до 120+ FPS.

Шаг 2. Rasterization: какие пиксели закрасить​

После vertex shading у нас есть треугольники на 2D-плоскости. Теперь нужно понять: какие именно пиксели экрана покрывает каждый треугольник.

Для масштаба:
  • 4K — это 3840×2160, то есть около 8.3 млн пикселей.

Rasterization берёт X/Y координаты трёх вершин треугольника и вычисляет:
  • какие пиксели попадают внутрь этого треугольника
  • какой цвет/текстура должны попасть в эти пиксели (по материалу треугольника)

Так треугольники превращаются во fragments — группы пикселей, относящиеся к одному треугольнику и одному материалу. Когда все треугольники прошли rasterization, в буфере кадра собирается итоговое изображение и отправляется на дисплей.

Z-buffer: почему ближнее перекрывает дальнее​

Если просто «красить пиксели», сцена развалится: дальние поверхности будут рисоваться поверх ближних, или порядок отрисовки начнёт ломать картинку.

Решение — Z-buffer (Depth Buffer). Это дополнительное значение глубины для каждого пикселя:
  • для каждого из 8.3 млн пикселей хранится «насколько это далеко от камеры»
  • когда треугольник rasterize-ится, его глубина сравнивается с тем, что уже лежит в Z-buffer
  • если новый треугольник ближе (меньше Z) — пиксель перерисовывается и Z обновляется
  • если дальше (больше Z) — этот вклад отбрасывается

Важный нюанс: у треугольника вершины имеют разные Z. Поэтому глубина считается для каждого пикселя треугольника, что позволяет корректно показывать пересечения объектов.

Антиалиасинг: откуда «лесенки» и как SSAA их сглаживает​

Проблема: пиксель — это клетка. Если треугольник режет пиксель по диагонали, стандартная rasterization закрасит весь пиксель целиком, из-за чего по краям появляются «ступеньки».

Пример решения из источника — Super Sampling Anti-Aliasing (SSAA):
  • внутри одного пикселя берутся 16 точек выборки
  • если треугольник покрывает, условно, 6 из 16 точек — пиксель получает «частичный» цвет
  • края становятся мягче, «лесенка» уменьшается

Цена — больше вычислений: вы буквально считаете больше выборок на пиксель.

Схема Z-buffer (Depth Test) и SSAA 4x4: слева near/ближе перекрывает far/дальше по правилу new Z < stored Z, справа SSAA считает final color как среднее из 16 samples (покрыто/не покрыто).

Слева показан Z-buffer (Depth Test): для каждого пикселя хранится глубина, и если new Z < stored Z — пиксель перерисовывается (write color) и Z обновляется, иначе вклад отбрасывается (discard). Справа SSAA 4x4: один пиксель разбивается на 16 samples, и итоговый цвет получается как среднее по покрытым/непокрытым выборкам — так сглаживаются “лесенки”.​

Шаг 3. Fragment Shading: свет, тени, блики и «реализм»​

После rasterization у нас есть пиксели, но «просто залить цвет» — значит получить плоскую, нереалистичную картинку. Fragment shading добавляет физику света: яркость зависит от направления света, положения камеры, отражений и теней.

Базовая логика из источника:
  • если поверхность смотрит на источник света — она ярче
  • если поверхность повернута боком или от света — темнее

Чтобы это посчитать, нужны два направления:
  • направление света
  • нормаль поверхности (направление, перпендикулярное плоскости треугольника)

Яркость получается через косинус угла между этими направлениями:
  • cos(θ)=1, если поверхность «в лоб» к свету
  • cos(θ)=0, если поверхность перпендикулярна свету

Дальше масштабируем:
  • умножаем cos(θ) на интенсивность света
  • и на цвет материала (RGB)

Чтобы не получать «провалы в чёрный», добавляют ambient-компоненту (окружающий свет), которую можно делать сильнее днём и слабее ночью.

Если источников света несколько, расчёт повторяется для каждого и складывается. Но много источников — это дорого, поэтому в сценах обычно ограничивают их количество и радиус влияния.

Flat vs Smooth shading: почему «кривое» выглядит кривым​

Если у каждого треугольника одна нормаль, весь треугольник будет одного оттенка — это flat shading. На закруглённых поверхностях это выглядит «гранёно».

Для smooth shading используют нормали вершин:
  • для каждой вершины нормаль берётся как среднее нормалей соседних треугольников
  • внутри треугольника нормали плавно интерполируются через barycentric coordinates
  • в итоге каждый пиксель получает свой «угол к свету» и плавный градиент освещения

Так набор треугольников начинает выглядеть как гладкая поверхность.

Где в этой схеме Ray Tracing и DLSS​

Классический пайплайн (vertex → raster → fragment) делает основную видимость и базовое освещение. А дальше подключаются дополнительные технологии.

Ray Tracing в играх часто используют как добавку для:
  • теней
  • отражений
  • улучшенного освещения
(а не как полный «киношный» рендер каждого кадра).

DLSS — это апскейл: берётся кадр в более низком разрешении и увеличивается до 4K с помощью нейросети (в источнике указано: convolution neural network). Важная привязка: DLSS выполняется после того, как пайплайн (и при необходимости RT) сделал кадр в низком разрешении.

В современных GPU из источника описана идея разделения вычислений по блокам:
  • CUDA / shading cores — рендер-пайплайн
  • RT cores — ray tracing
  • Tensor cores — DLSS

Это позволяет задействовать разные ресурсы одновременно. В источнике приводится мысль: с таким разделением можно играть в 4K и укладываться в менее 10 мс на кадр, тогда как «если бы всё шло только на shading cores», один кадр мог бы занимать около 50 мс.

Нюансы и подводные камни (что ломает ожидания)​

  • 4K — это не “чуть-чуть больше”, а 8.3 млн пикселей. Rasterization и shading резко дорожают, потому что вы считаете больше пикселей.
  • Освещение — это не “покрасить”, а математика на каждый пиксель. Чем сложнее материалы, блики и свет, тем больше работы у fragment shading.
  • Много источников света — дорого. Поэтому игры ограничивают количество/радиус света и иногда «упрощают» вклад дальних источников.
  • Сглаживание краёв тоже стоит ресурсов. SSAA улучшает края, но увеличивает вычисления за счёт нескольких выборок на пиксель.
  • RT и DLSS — отдельные миры. Они не заменяют базовый пайплайн, а дополняют его в определённых местах.

Mini-FAQ​

Почему всё строится на треугольниках?
Треугольники — самый удобный и стабильный «кирпич» для описания поверхности. Любую сложную форму можно приблизить сеткой из треугольников, а GPU исторически оптимизирован под такую геометрию.

Зачем Z-buffer, если “и так видно, что ближе”?
Потому что GPU рисует треугольники пачками, а не «как художник от дальнего к ближнему». Z-buffer решает задачу видимости строго и быстро: ближайшее выигрывает, дальнее отбрасывается.

Почему 4K так сильно бьёт по FPS?
Потому что 4K — это около 8.3 млн пикселей. Rasterization и fragment shading должны обработать гораздо больше точек.

DLSS — это часть рендера или отдельная штука?
Это отдельный этап апскейла: сначала пайплайн (и при необходимости RT) делает кадр ниже разрешением, потом DLSS поднимает его до 4K через нейросеть.

Итог: как держать в голове всю картину​

Если совсем коротко, то рендер в играх — это цепочка:
  • геометрия (вершины/треугольники) → проекция на экран (vertex)
  • треугольники → пиксели (raster)
  • пиксели → свет и материал (fragment)
  • кто ближе — решает Z-buffer
  • края сглаживаются (например, SSAA)
  • Ray Tracing добавляет более дорогие эффекты света/теней/отражений
  • DLSS берёт кадр ниже разрешением и апскейлит до 4K нейросетью

Чек-лист “понял ли я, что происходит”​

  • Я понимаю, что базовый рендер держится на трёх шагах: vertex, raster, fragment.
  • Я понимаю, зачем нужен Z-buffer и что он хранит глубину на каждый пиксель.
  • Я понимаю, почему 4K так тяжело: 8.3 млн пикселей на кадр.
  • Я понимаю, почему «лесенки» появляются и как SSAA их сглаживает (16 выборок на пиксель).
  • Я понимаю, что RT и DLSS не “вместо”, а “в дополнение” к пайплайну, и что у GPU под них могут быть отдельные блоки вычислений.

📚 Читайте также​

Об авторе
Sysadmin
Sysadmin — администратор и автор материалов на компьютерном форуме Tehnari.ru.
Пишет про сборку и апгрейд ПК, выбор комплектующих, оптимизацию Windows и устранение типичных проблем с железом. Предпочитает практику и реальные кейсы вместо теории из буклетов.

Комментарии

Нет комментариев для отображения.

Информация о статье

Автор
Sysadmin, Tehnari.ru
Время прочтения статьи
7 мин. чтения
Просмотры
13
Последнее обновление

Ещё из категории: «Устройство и теория»

Еще от автора: «Sysadmin, Tehnari.ru»

Назад
Сверху