Компьютерная графика

Текстурирование

1974: рука Катмулла меняет всё

1974 год. Университет Юты. Эд Катмулл кладёт руку на планшет-сканер и строит 3D-модель кисти из 350 полигонов. Потом берёт фотографию этой руки и проецирует её на полигоны - как обои на стену. Рендерит. Впервые в истории 3D-объект выглядит как часть реального мира - не синтетической формой, а именно рукой. Катмулл публикует метод, уходит в Lucasfilm, основывает Pixar. Texture mapping становится фундаментом всей компьютерной графики на следующие 50 лет.

Flat quad из 2 треугольников притворяется скалой с тысячью деталей. Персонаж из 50 тысяч полигонов выглядит как 5 миллионов. Это не магия - это текстурирование.

  • Любой полигональный рендер: игры, фильмы, AR-фильтры в Instagram
  • Photogrammetry-сканирование актёров для AAA-игр (Horizon, God of War)
  • Нейральные текстуры в NeRF и Gaussian Splatting
  • Виртуальные примерки одежды в e-commerce (IKEA AR, Zara AR)
  • Медицинская визуализация: текстурированные 3D-модели органов из МРТ-данных

UV mapping: как разгладить 3D-поверхность в плоскость

1974 год. Эд Катмулл - аспирант Университета Юты, будущий президент Pixar - кладёт ладонь на сканер. Потом натягивает фотографию руки на полигональную 3D-модель кисти. Впервые в истории компьютерной графики объект выглядит реальным. Не закрашенным, не затенённым - именно **реальным**. Это был первый texture mapping в мире.

Идея проста до неприличия: каждой вершине 3D-меша присваиваются координаты $(u, v) \in [0, 1]^2$, которые указывают на точку в текстурном изображении. GPU при растеризации интерполирует $(u, v)$ по треугольнику и читает цвет из текстуры. Трёхмерная поверхность разворачивается в плоский лист - как кожура апельсина, которую разрезали и расплющили.

UV - это не случайные буквы. Алфавит уже занят: X, Y, Z - координаты 3D-пространства. U и V - следующие незанятые буквы. Диапазон $[0, 1]$ нормализован: независимо от разрешения текстуры (256x256 или 4096x4096), координата 0.5 всегда означает середину.

Развёртка (unwrapping) - задача с открытой сложностью. Развернуть сферу без искажений невозможно - именно поэтому все картографические проекции Земли врут в разных местах. С игровыми персонажами та же история: руки, голова, тело развёртываются отдельными "островами" (UV islands), которые художник вручную раскладывает на UV-листе. Ошибка в развёртке - и текстура растягивается, как неправильно наклеенные обои.

Stable Diffusion генерирует изображения 512x512 или 1024x1024 без знания 3D. Но pipelines вроде Zero123 берут сгенерированный 2D-арт и автоматически создают UV-развёртку для 3D-меша. Катмулл проецировал фото вручную - сейчас диффузионные модели делают это за секунды.

**Tile и Wrap**: UV может выходить за пределы $[0, 1]$. Режим `GL_REPEAT` повторяет текстуру тайлингом (трава, кирпич, асфальт - всё тайлится). `GL_CLAMP_TO_EDGE` растягивает крайние пиксели. `GL_MIRRORED_REPEAT` отражает при каждом повторении - убирает видимые швы.

UV-координаты $(u, v) = (0.5, 0.5)$ указывают на...

Фильтрация и mipmaps: когда пиксели расходятся во мнениях

Ближайший сосед (nearest neighbor) - самый дешёвый способ читать текстуру. Берётся пиксель, ближайший к $(u, v)$. Результат - пикселизация как в Minecraft. Это иногда художественный выбор, но чаще нежелательный артефакт.

Билинейная фильтрация берёт 4 ближайших пикселя и интерполирует линейно по обеим осям. Результат гладкий при увеличении (magnification). Но при уменьшении (minification) - текстура мерцает. Особенно на дальних объектах: каждый кадр GPU выбирает разные пиксели, и картинка "дрожит".

**Aliasing при minification** - это фундаментальная проблема сэмплирования. Теорема Найквиста: частота дискретизации должна быть минимум вдвое выше максимальной частоты сигнала. Далёкая текстура с мелкими деталями нарушает этот предел - и возникает алиасинг.

Lance Williams в 1983 году предложил mipmaps. Идея: заранее вычислить отфильтрованные версии текстуры с шагом 2x. Текстура 1024x1024 порождает пирамиду: 512x512, 256x256 ..., 2x2, 1x1. Общий размер - всего 4/3 от оригинала (геометрический ряд: $\sum_{k=0}^{\infty} (1/4)^k = 4/3$). GPU выбирает нужный уровень (LOD - Level of Detail) автоматически по производной UV-координат.

Трилинейная фильтрация = билинейная внутри двух соседних mip-уровней + линейная интерполяция между уровнями. Убирает "ступеньки" при смене LOD. Анизотропная фильтрация (AF) идёт дальше: учитывает угол обзора. Пол под острым углом камеры выглядит размытым при обычных mipmaps - AF читает текстуру в нескольких точках вдоль направления наклона. AF x16 - стандарт в играх с 2005 года, стоит 1-2% производительности.

**Texture compression**: BC7/BPTC (DX11+) и ASTC (мобильные) хранят 4x4 блока пикселей в 128 битах вместо 512. Коэффициент сжатия 6:1-8:1 с минимальными визуальными потерями. GPU декомпрессирует блоки прямо в кеше. Игры на PlayStation 5 используют ASTC: это экономит VRAM и уменьшает пропускную способность памяти.

Mipmaps занимают ровно в 2 раза больше памяти, чем оригинальная текстура. Верно?

Normal maps и PBR: обман геометрии, честная физика

Normal map - это текстурная ложь. Ложь, которая позволила PS2-эре выглядеть как PS4. Вместо добавления миллионов реальных полигонов для царапин и заклёпок - нормаль поверхности меняется попиксельно. GPU думает, что поверхность шероховатая в одном месте и гладкая в другом. Освещение считается по поддельной нормали. Flat quad из 2 треугольников выглядит как кирпичная стена со всеми швами и выбоинами.

Нормали в normal map хранятся в tangent space - локальной системе координат поверхности. Три вектора: tangent ($T$), bitangent ($B$), normal ($N$) образуют TBN-матрицу. Без TBN нормали работали бы только на статичных объектах - ни вращения, ни деформации. Синеватый оттенок normal maps (RGB около [128, 128, 255]) означает "нормаль смотрит прямо вверх" - это нейтральный вектор $(0, 0, 1)$ в диапазоне $[0, 1]$.

PBR (Physically Based Rendering) изменил индустрию в 2012-2014. Disney открыла свой principled shader для игровой индустрии. Unreal Engine 4 принял metallic-roughness workflow. Три параметра плюс цвет описывают физически корректный материал.

**PBR metallic-roughness**: `albedo` (базовый цвет), `metallic` (0 = диэлектрик, 1 = металл), `roughness` (0 = зеркало, 1 = матовый). Металлические поверхности не имеют diffuse-составляющей - они отражают свет тонированным цветом. Диэлектрики отражают ненасыщенным белым около 4% входящего света (Fresnel F0 = 0.04). В основе - Cook-Torrance BRDF: GGX distribution, Smith geometry term, Schlick Fresnel.

Набор текстур в PBR-пайплайне современной AAA-игры: albedo, normal, metallic, roughness, AO, height/displacement, emissive. Персонаж может требовать 7-8 текстур по 4K каждая - это 256 MB только на VRAM одного героя. Texture streaming и виртуальные текстуры (sparse textures в Vulkan/DX12) решают проблему: GPU держит в памяти только видимые mip-уровни.

NeRF (Neural Radiance Fields, 2020) полностью обходит UV mapping. Нейросеть кодирует цвет и плотность как функцию позиции и направления взгляда: $(x, y, z, \theta, \phi) \to (r, g, b, \sigma)$. Нет текстур, нет развёртки, нет ручного труда художника. Зато есть view-dependent effects - блики, которые меняются с углом обзора, - которые обычные текстуры не умеют делать корректно. Gaussian Splatting 2023 делает то же в реальном времени.

**Baking**: для real-time рендеринга high-poly меш (миллионы полигонов) "запекается" в набор PBR-текстур для low-poly версии. Normal map кодирует геометрические детали high-poly. Персонаж в игре - это обычно low-poly (10-50K треугольников) плюс запечённые текстуры от версии на 5-10 миллионов полигонов.

Normal map со значением RGB = (128, 128, 255) означает, что нормаль...

Итоги

  • UV mapping разворачивает 3D-поверхность в плоский квадрат [0,1]^2 - как кожуру апельсина на стол
  • Билинейная фильтрация устраняет пикселизацию при увеличении; mipmaps устраняют мерцание при уменьшении
  • Mipmaps добавляют всего 33% памяти, предвычисляя LoD-пирамиду (геометрический ряд)
  • Normal maps меняют нормаль поверхности попиксельно - flat quad притворяется скалой с тысячью деталей
  • PBR metallic-roughness: три параметра (albedo, metallic, roughness) - физически корректный материал
  • NeRF обходит UV mapping полностью, кодируя сцену как нейронную 5D-функцию

Что дальше

Текстуры - данные для следующих систем рендеринга. Normal maps идут в освещение и тени, PBR-набор питает весь шейдерный пайплайн.

  • Тени и AO — Normal maps используются в shadow mapping и SSAO для корректных теней
  • Шейдеры — GLSL sampler2D и texture() - практическое чтение UV в шейдерном коде
  • NeRF и Neural Rendering — Нейральная альтернатива UV-текстурам без ручной разметки художника

Вопросы для размышления

  • Катмулл написал texture mapping как дипломную работу - не знал, что создаёт основу Pixar и игровой индустрии. Какие из сегодняшних экспериментов с NeRF и Gaussian Splatting могут так же переписать правила через 50 лет?

Связанные уроки

  • cg-05 — PBR текстуры подают данные в Cook-Torrance BRDF
  • cg-07 — Normal maps используются в shadow mapping и SSAO
  • cg-09 — Шейдеры читают текстуры через sampler2D
  • cg-19 — NeRF учит 5D-текстуру без UV-развёртки
  • la-02-dot-product — Dot product нужен для нормализации UV и TBN матриц
  • arch-09-cache
Текстурирование

0

1

Войти