Привет, Хаброжители! За красивыми образами анимационного фильма и реалистичной средой популярных видеоигр скрываются загадочные алгоритмы. В этой книге вы познакомитесь с двумя основными направлениями современной графики: рейтресингом и растеризацией. Такая литература пугает новичков из-за большого количества математики. Но только не в этом случае. Познакомьтесь с 3D-рендерингом без длинных формул! Вы создадите полноценные рабочие рендеры — рейтрейсинг, симулирующий лучи света и их отражение от объектов, растеризатор 3D-моделей, научитесь создавать реалистичные отражения и тени, а также отрисовывать сцены с любой точки обзора. Наглядные примеры с псевдокодом позволят без проблем создавать рендеры на любом языке, а живые JavaScript-демо каждого алгоритма вдохновят на самостоятельные подвиги.

Перспективная проекция


Текстуры
Наш растеризатор может отрисовывать кубы или сферы. Но нам неинтересны абстрактные фигуры вроде кубов и сфер. Нас интересуют разные реальные объекты, например ящики и планеты или игральные кубики и шарики для рулетки. В этой главе вы научитесь добавлять на поверхность объектов разные визуальные детали с помощью текстур.

Закрашивание ящика

Допустим, нам в сцене нужен деревянный ящик. Как можно превратить в него куб? Первый вариант — добавить много треугольников для воссоздания фактуры дерева, шляпок гвоздей и т. д. Это сработает, но скажется на быстродействии.

Есть еще вариант — сымитировать детали: вместо изменения геометрии объекта мы просто «нарисуем» поверх него что-то похожее на дерево. Если не разглядывать ящик вплотную, то и отличий заметить не удастся, а вычислительная стоимость окажется намного ниже.
Эти два варианта можно совмещать: выбирать правильный баланс между добавлением геометрических деталей и рисованием поверх них для достижения требуемого качества и скорости отрисовки. Мы уже знаем, как работать с геометрией, поэтому сразу перейдем к изучению второго варианта.

Для начала нам нужно изображение, которое мы будем рисовать на треугольниках. Его мы будем звать текстурой. На рис. 14.1 показана текстура деревянного ящика.



Дальше нужно указать, как эта текстура должна применяться к модели. Это наложение можно определить по треугольникам, сопоставив точки текстуры с их вершинами (рис. 14.2).

Для определения этого сопоставления нам понадобится система координат, позволяющая обращаться к точкам на текстуре. Напомню, что текстура — это просто изображение в виде прямоугольного массива пикселей. Можно было взять координаты x и y и рассматривать пиксели текстуры относительно них, но мы уже используем эти обозначения для холста. Поэтому координатами текстуры у нас будут u и v, а ее пиксели назовем текселями (сокр. от англ. texture elements — элементы текстуры).

Начало этой системы координат (u, v) мы установим в верхнем левом углу текстуры. Еще объявим, что u и v — это вещественные числа в диапазоне [0, 1], независимо от фактических размеров текселей. Это будет очень удобно по нескольким причинам. Например, нам может понадобиться использовать текстуры с разрешением выше или ниже, в зависимости от объема доступной памяти ОЗУ. Мы не привязаны к реальным размерам пикселей, поэтому можно изменять разрешение, не корректируя саму модель. Мы можем умножить u и v на ширину и высоту текстуры для получения фактических индексов tx и ty.



Основная идея наложения текстур проста: вычислить координаты (u, v) для каждого пикселя треугольника, получить соответствующий тексель и закрасить пиксель его цветом. Но модель указывает координаты u и v только для трех вершин треугольника, а нам они нужны для каждого пикселя…

Вы уже должны понемногу вникать в происходящее. Да, здесь снова появляется наш старый добрый друг — линейная интерполяция. Можно использовать отображение атрибутов, чтобы интерполировать значения u и v по всей грани треугольника. Это даст нам (u, v) для каждого пикселя. С их помощью мы сможем вычислить (tx, ty), получить тексель, применить затенение и закрасить пиксель итоговым цветом. Результат всего этого можно посмотреть на рис. 14.3.

Результат получается посредственный. Внешне ящики выглядят неплохо. Но если присмотреться к диагональным доскам, мы увидим, что они деформировались. Что же пошло не так?

Как и в главе 12, мы снова сделали неявное ошибочное допущение: то, что u и v изменяются по экрану линейно. Это не так. Рассмотрим стену очень длинного коридора, закрашенную чередующимися черными и белыми полосами. Чем дальше будет стена, тем тоньше будут полосы. Если мы сделаем так, чтобы координата u изменялась линейно относительно x, то получим неверный результат, как показано на рис. 14.4.



Ситуация схожа с той, что была у нас в главе 12, и решение тоже будет очень похожим: несмотря на то что u и v — не линейные в координатах экрана, такими являются u/z и
v/z (подтверждение этому будет похоже на подтверждение 1/z: представьте, что u изменяется линейно в 3D-пространстве, и подставьте x и y с их выражениями для области экрана). У нас уже есть интерполированные значения 1/z в каждом пикселе, этого достаточно для интерполяции u/z и v/z и получения назад u и v:



На рис. 14.6 сравниваются оба результата, чтобы можно было увидеть разницу.



Живая реализация алгоритма находится по ссылке gabrielgambetta.com/cgfs/textures-demo.

Эти примеры смотрятся красиво, потому что размер текстуры и пиксельный размер треугольников под ней приблизительно одинаковые.

Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок

По факту оплаты бумажной версии книги на e-mail высылается электронная книга.

Для Хаброжителей скидка 30% по купону — Гамбетта