Как видеокарта рисует графику в играх: от 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 выполняет апскейл до 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 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): для каждого пикселя хранится глубина, и если 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 под них могут быть отдельные блоки вычислений.
Читайте также
Из чего состоит видеокарта: GPU, VRAM, питание, охлаждение — чтобы связать “что делает рендер” с железом: где живёт VRAM, зачем VRM, и почему охлаждение/питание реально влияют на стабильность и шум.
AMD vs Nvidia в 2026: DLSS/FSR, трассировка, генерация кадров и VRAM — если хочешь понять, что из “RT/DLSS-мира” даёт реальную пользу в играх, а что чаще маркетинг и компромиссы.
Баланс CPU и GPU в 2025: как подобрать связку под 1080p, 1440p и 4K без bottleneck — чтобы не гадать “почему FPS не растёт”: где упор в CPU, где упор в GPU, и почему 4K резко увеличивает цену каждого кадра.