Компьютерная графика
Растеризация и пиксели
1995 год. Pixar выпускает Toy Story - первый полнометражный CGI-фильм. Каждый кадр рендерился 4-13 часов на кластере из 117 Sun SPARCstation. 2024 год: RTX 4090 рендерит тот же кадр за секунды. Но принцип не изменился ни на бит. Toy Story: 800 000 полигонов. Spider-Man: No Way Home: 250 миллионов. Всё что видишь на экране - треугольники. И всё что с ними происходит - rasterization pipeline, z-buffer, шейдеры. Это не магия и не «3D-графика». Это целочисленная арифметика и умные трюки с памятью.
- **Unreal Engine 5 (Nanite):** виртуальная геометрия - система растеризует до миллиарда полигонов за кадр, динамически выбирая уровень детализации на GPU
- **Chrome (Skia / Dawn):** каждый div, тень, текст - проходят GPU-растеризацию. На M1 Mac весь UI рендерится в Metal командах, которые собирает Skia
- **Apple Core Animation:** каждый слой UIKit - отдельный framebuffer texture. Прокрутка на 120 Hz требует swap буфера каждые 8.3 мс без tearing
- **Stable Diffusion (SDXL):** финальный шаг диффузии - это CUDA-тензор 1024×1024×4 float16. Отобразить его на экране - значит растеризовать float в RGBA8888 пиксели
Растеризация: из геометрии в пиксели
Экран - это сетка квадратов. GPU - это машина для закрашивания этих квадратов. Всё остальное - детали. Процесс перевода математической геометрии (линий, треугольников, кривых Безье) в набор конкретных пикселей называется **растеризация** - и это самая горячая петля в графическом pipeline: в AAA-игре GPU растеризует 10-100 миллионов треугольников каждые 16 миллисекунд.
**Растеризация** (rasterization) - преобразование векторной геометрии (отрезки, треугольники, полигоны) в растровое изображение (сетку пикселей). Это основа реального времени графики: GPU растеризует миллионы треугольников 60+ раз в секунду.
Простейшая задача растеризации: нарисовать отрезок от (x₁, y₁) до (x₂, y₂). Наивный путь - вычислять y = kx + b для каждого x: умножение, деление, округление float. Медленно. Брезенхэм в 1962-м придумал лучше: накапливать целочисленную ошибку и принимать решение о сдвиге по y через простое сравнение. Никаких float - только сложения и вычитания.
Алгоритм Брезенхэма (1962)
Джек Брезенхэм разработал алгоритм для плоттера IBM 1401. В эпоху, когда деление стоило сотни тактов, целочисленный алгоритм с только сложениями и сдвигами был прорывом. 60+ лет спустя тот же принцип используется в GPU.
Главное преимущество алгоритма Брезенхэма перед наивным y = kx + b:
Пиксели и цветовые модели
Пиксель - это число в памяти. Точнее - три числа: Red, Green, Blue. Или четыре, если есть прозрачность. В VRAM они упакованы плотно подряд, и GPU читает их тысячами за такт. Цветовая модель - это просто договорённость о том, что эти числа означают.
**RGB** - аддитивная цветовая модель. Каждый канал (Red, Green, Blue) хранит интенсивность от 0 до максимума. **Bit depth** определяет точность: 8 бит = 256 уровней на канал (16.7 млн цветов), 10 бит = 1024 уровня (1.07 млрд цветов).
| Формат | Бит/пиксель | Каналы | Применение |
|---|---|---|---|
| RGB565 | 16 | R:5, G:6, B:5 | Мобильные устройства, embedded |
| RGB888 | 24 | R:8, G:8, B:8 | Стандартный (JPEG, BMP) |
| RGBA8888 | 32 | R:8, G:8, B:8, A:8 | UI, игры (с прозрачностью) |
| RGB101010 | 30 | R:10, G:10, B:10 | HDR мониторы, кино |
| RGBA16F | 64 | 4×16bit float | HDR rendering, compositing |
**Альфа-канал** - четвёртое число. A = 255: пиксель непрозрачный. A = 0: пиксель прозрачный, не рисуем ничего. A = 128: блендинг. Формула alpha blending: `result = src * α + dst * (1 - α)`. Каждое полупрозрачное окно, каждая тень, каждый tooltip в вашем UI - это эта формула, применяемая миллионы раз за кадр. В GPU она реализована как аппаратная операция ROP (Render Output Unit).
Сколько уникальных цветов может представить формат RGBA8888?
Framebuffer и Double Buffering
После растеризации пиксели уходят в **framebuffer** - это просто непрерывный массив в VRAM. Монитор читает его строка за строкой слева направо, сверху вниз - с частотой refresh rate. При 60 Hz это происходит 60 раз в секунду. При 120 Hz - 120 раз. GPU должен успеть нарисовать следующий кадр до следующего прохода монитора - иначе пользователь увидит «разрыв».
**Framebuffer** - непрерывный блок памяти (video RAM), хранящий растровое изображение экрана. Для разрешения W×H с C байтами на пиксель: размер = W × H × C байт. Пример: 1920×1080 RGBA = 8,294,400 байт ≈ 8 МБ.
**Tearing** - монитор читает framebuffer в момент, когда GPU туда же пишет. Верхняя половина экрана - новый кадр. Нижняя - старый. Горизонтальная линия разрыва прыгает по экрану. В шутерах это болезненно заметно. Решение - не рисовать в тот буфер, который монитор сейчас читает. Два буфера, swap между ними.
| Метод | Tearing | Задержка | FPS |
|---|---|---|---|
| Single buffer | Да (видимые разрывы) | Минимальная | Неограничен |
| Double buffer (без VSync) | Возможен | Минимальная | Неограничен |
| Double buffer + VSync | Нет | +1 кадр (16мс при 60Hz) | ≤ частота монитора |
| Triple buffer | Нет | Меньше чем VSync | ≤ частота монитора |
**Triple buffering** - третий буфер разрывает жёсткую связку: GPU рисует в один back buffer, второй ожидает VSync, третий - перед монитором. GPU никогда не ждёт монитора. Технологии G-Sync (NVIDIA) и FreeSync (AMD) идут дальше - они динамически синхронизируют refresh rate монитора под текущий FPS. Монитор подстраивается под GPU, а не наоборот.
Double buffering + VSync устраняет tearing. Какова цена?
Разрешение и Aspect Ratio
Разрешение - это размер framebuffer. Больше пикселей = больше памяти = больше работы растеризатора. Это линейная зависимость: удвоил площадь экрана - удвоил нагрузку. Переход с 1080p на 4K - это четырёхкратный прирост пикселей, и именно поэтому RTX 3060 «не тянет» 4K в требовательных играх, хотя в 1080p выдаёт 100+ FPS.
**Разрешение** - количество пикселей по горизонтали и вертикали. **Aspect Ratio** - соотношение ширины к высоте. 1920×1080 → 16:9. Больше пикселей → больше деталей, но GPU растеризует пропорционально больше.
| Название | Разрешение | Aspect Ratio | Мегапиксели | Framebuffer (RGBA) |
|---|---|---|---|---|
| 720p (HD) | 1280×720 | 16:9 | 0.92 МП | 3.5 МБ |
| 1080p (Full HD) | 1920×1080 | 16:9 | 2.07 МП | 7.9 МБ |
| 1440p (2K) | 2560×1440 | 16:9 | 3.69 МП | 14.1 МБ |
| 4K (UHD) | 3840×2160 | 16:9 | 8.29 МП | 31.6 МБ |
| Ultrawide | 3440×1440 | 21:9 | 4.95 МП | 18.9 МБ |
**PPI** (pixels per inch) - плотность, а не разрешение. iPhone 15 при 2556×1179 на диагонали 6.1" даёт 461 PPI - пиксели физически не видны на нормальном расстоянии просмотра. 27" монитор при том же 4K разрешении - 163 PPI. Именно поэтому Apple называет Retina дисплеем дисплей от 220 PPI, а не конкретное разрешение: важна плотность, а не абсолютное число пикселей.
Toy Story в 1995-м рендерился 4 часа на кадр. Сегодня - секунды. Но математика та же: треугольники, растеризация, framebuffer. Брезенхэм написал свой алгоритм для плоттера IBM в 1962-м, не зная о GPU. 60 лет спустя его идея - в каждом NVIDIA, AMD и Apple Silicon. Принципы меняются медленно. Железо меняется быстро.
Ray tracing полностью заменил растеризацию в современных играх и GPU
Растеризация остаётся доминирующим методом рендеринга в real-time. Ray tracing используется гибридно: основная геометрия растеризуется, а RT добавляет отражения, тени и глобальное освещение. Чистый ray tracing слишком медленный для 60+ FPS
Растеризация - O(площадь треугольника в пикселях): GPU параллельно проверяет тысячи пикселей за такт. Ray tracing - O(W × H × глубина сцены): каждый луч обходит BVH-дерево всей геометрии сцены. При 4K и 200M треугольников чистый RT не выходит за 5-10 FPS даже на RTX 4090. Поэтому NVIDIA DLSS и AMD FSR: рендерить в 1080p через гибрид растеризации и RT, потом апскейлить нейросетью до 4K.
Ключевые идеи
- **Растеризация** - перевод математической геометрии в пиксели. Алгоритм Брезенхэма (1962) делает это без float: только сложения целых чисел. Тот же принцип - в каждом GPU сегодня
- **RGB/RGBA** - аддитивная модель. 8 бит/канал = 16.7 млн цветов. Alpha blending formula: `result = src * α + dst * (1 - α)` - основа любого полупрозрачного UI
- **Framebuffer** - непрерывный блок VRAM. 1920×1080 RGBA = 7.9 МБ. Double buffering: GPU рисует в back buffer, монитор читает front - swap происходит на vertical blank
- **Переход 1080p → 4K = 4× пикселей = 4× нагрузки на GPU.** Ray tracing добавляется поверх растеризации, не заменяет её - даже RTX 4090 использует гибридный подход
Связанные темы
Растеризация - первый шаг в понимании graphics pipeline:
- Линейная алгебра для графики — Вершины треугольников трансформируются матрицами до растеризации
- Координатные пространства — Геометрия проходит через цепочку пространств (model→world→screen) перед растеризацией
- Двоичная система — RGB565, RGBA8888 - упаковка цветов в биты. Битовые операции для быстрого доступа к каналам
Вопросы для размышления
- Почему алгоритм Брезенхэма до сих пор актуален, если современные GPU имеют аппаратные растеризаторы?
- Как бы вы реализовали alpha blending для полупрозрачных объектов, которые перекрывают друг друга? Важен ли порядок рисования?
- При переходе на 8K (7680×4320) - во сколько раз вырастет нагрузка на GPU по сравнению с 4K?
Связанные уроки
- la-01-vectors-intro — Линейная алгебра - язык компьютерной графики: векторы, матрицы трансформаций, dot/cross product
- gd-01 — Графика - основа геймдева: рендеринг, шейдеры, физика на GPU
- arvr-01 — AR/VR строится поверх computer graphics: stereo rendering, reprojection, spatial mapping
- dsp-01 — DSP обрабатывает сигналы во времени, computer graphics - в пространстве. Свёртка, фильтры, FFT применимы в обоих
- arch-04-cpu