Примечание: В оригинале статьи есть демо на WebGL2, которые в переводе заменены на видео и GIF.
Одно из моих хобби — написание программ просмотра моделей и графических инструментов для игр. Это хорошее соединение интересных мне тем — графики, рендеринга, реверс-инжиниринга сложных систем и ностальгии по старым видеоиграм.
Недавно я расширил возможности моего приложения просмотра игровых моделей на основе WebGL, добавив поддержку некоторых игр с Nintendo GameCube, в том числе The Legend of Zelda: The Wind Waker и Super Mario Sunshine. В GameCube, если вы не знаете, установлен передовой, почти программируемый видеопроцессор, но с фиксированным функционалом. Разработчики не могли писать шейдеры, и вместо этого программировали наборы комбинаторов текстур способом, похожим на использованные в конвейерах glTexEnv, но доведённым до максимума возможностей. Для тех, кто привык к современным программируемым видеопроцессорам, может показаться безумной мысль о том, что с помощью этого приёма можно реализовать сложные эффекты. И тем не менее, в 2002 году была выпущена Super Mario Sunshine с отлично выглядящей на то время водой. Вот как она выглядит в действии:
Этот эффект загружается в браузер непосредственно из исходных файлов игры для Delfino Plaza и размещаются на плоскости. Давайте рассмотрим подробнее, как он реализован.
Текстурирование плоскости
Верьте или нет, но этот эффект на самом деле начинается вот с этого:
Этот эффект можно рассматривать как довольно сложную вариацию очень древнего приёма: «скроллинга текстур». Он немного сложнее, чем я показал здесь, но основы остаются теми же. Наша плоскость начинает свою жизнь как эта скроллящаяся волновая текстура, дающая нам интересный шум, с которым можно поработать. Затем она комбинируется с вторым слоем той же текстуры, но на этот раз скроллящейся только в одном измерении.
Это даёт нам интересный муаровый паттерн, который является основой того, что вода кажется пузырящейся и двигающейся естественным образом. Можно даже заметить «призрачное» наложение при встрече двух текстур. Этот артефакт заметен на исходном материале, но больше похож на намеренно добавленный луч солнца или света, проходящий по воде. Подобное сокрытие артефактов с помощью продуманного дизайна материалов является важной частью техник создания игровой графики.
Очевидно, что текстура не является чёрной. Цвета не чёрно-белые, они смешиваются с фоном, что даёт нам более прозрачный вид.
И у нас начинает что-то получаться. На этом этапе второй слой текстуры добавлен уже дважды, как и первый, что делает его особенно ярким, как будто с эффектом bloom. Эта функция будет полезной позже, когда мы определим засветы на воде.
Если мы вернёмся к исходному материалу, то он выглядит гораздо более «динамичным». Если перемещать камеру, приближать и отдалять её, то кажется, что текстура изменяется. Рядом она чётко видна, а на расстоянии постепенно исчезает. В традиционной конвейере с фиксированным функционалом эффекты такого типа невозможны. Материал никак не может узнать расстояние до камеры! Однако для реализации этого эффекта Nintendo совершает хитрый трюк с более традиционным способом. Давайте поговорим о том, что я называю «mip-трюком».
Построение квадратного mip из круглого отверстия
Mip-текстурирование — это традиционный приём оптимизации графики. Когда видеопроцессоры накладывают текстуры, они стремятся к тому, чтобы готовое изображение было как можно более гладким, и при этом как можно более быстрым. Сэмплируемая здесь текстура на самом деле имеет размер всего 64?64 пикселя (да, это правда!), а окна браузера обычно гораздо больше. Если приблизить камеру, особенно в последнем демо, то можно увидеть пиксели и то, как они смешиваются друг с другом, появляются и растворяются. Но нужно не забывать, что видеопроцессоры должны выполнять эти вычисления для каждого пикселя в готовом изображения. Если смотреть сверху, то в этом случае текстура увеличивается, но если смотреть под углом, когда плоскость становится более сжатой на расстоянии, то текстура на экране становится размером меньше 64?64 пикселя.
Когда такое происходит, то говорят, что текстура «минимизируется» и видеопроцессору нужно считывать гораздо больше пикселей текстуры, чтобы сделать готовое изображение гладким. Это затратно с точки зрения вычислений — видеопроцессор стремится считывать как можно меньше пикселей. По этой причине были изобретены «mip-текстуры» — предварительно вычисленные более мелкие версии каждого изображения. При минимизации текстуры видеопроцессор вместо полной текстуры может использовать эти версии. Итак, у нас есть версии текстуры размером 32?32 пикселя и 16?16 пикселей, а видеопроцессор может выбирать нужную ему, или даже смешивать две версии, чтобы получить наилучшую картинку. Mip-текстуры — это превосходный пример компромисса между временем и пространствов, а также пример оптимизаций контента в процессе сборки приложения.
Однако, как я сказал, это происходит, когда текстура минимизируется. Это случается, когда она становится меньше на экране, а такое обычно бывает, когда текстура расположена дальше. Понимаете, к чему я клоню? Именно таким образом можно и получать расстояние от камеры!
Что, если вместо использования уменьшенных версий одной текстуры мы используем разные текстуры? Nintendo тоже пришла к такому же решению. Именно это я называю «mip-трюком». Текстура волн, которую я показал выше — это ещё не вся история. На практике существует полная текстура волн с показанными всеми уровнями mip-текстур.
На самом крупном уровне mip-текстуры (когда текстура ближе всего к камере) у нас нет никаких пикселей. По сути, это убирает эффект воды в небольшом радиусе вокруг камеры, что делает воду чистой. Во-первых, это позволяет избежать ощущения монотонности материала воды, во-вторых, помогает в геймплее, показывая игроку подводные предметы, находящиеся рядом с ним. Умное решение! Второй уровень mip-текстуры — это именно та текстура, которая использовалась в демо до текущего момента, и она имеет «среднюю силу».
Третий уровень mip-текстуры — самый яркий, он соответствует этой «полосе» яркого свечения посередине. Я думаю, что эта полоса — умный способ для имитации отражений среды. На этом расстоянии до камеры можно представить, что она является отражением текстуры неба, наклонённой к воде под углом в 20 градусов, например, облаков. В уровне Sirena Beach эта полоса имеет жёлтый оттенок, придавая уровню красивое жёлтое свечение, соответствующее вечернему закату.
Давайте попробуем загрузить все эти mip-текстуры в наше демо.
Мы уже намного ближе к нужному эффекту!
Краткое отступление: поскольку алгоритм, выбирающий используемые для текстуры mip-текстуры жёстко прописан в видеопроцессоре, то это означает, что он не обязательно портируем. GameCube выполняет рендеринг в разрешении 640?548, и mip-текстуры созданы под этот размер. Разработчики эмулятора Dolphin тоже это заметили — так как Dolphin может выполнять рендеринг в бОльших разрешениях, чем поддерживаемое консолью GameCube, этот трюк может поломаться, если не отнестись к нему внимательно. К счастью, современные графические API позволяют применять смещение при выборе mip-текстур. Получив разрешение экрана и обладая знаниями об исходном разрешении 640?548 консоли GameCube, мы можем вычислить это смещение и использовать его при сэмплировании.
Решив эту проблему, мы можем внести финальный штрих. Повторюсь — верьте или нет, но для превращения последнего демо в готовый продукт остался только один шаг. Простая функция (называемая альфа-тестом) проверяет, «насколько ярок» конечный пиксель, и если он находится в определённых пределах, пиксель полностью отбрасывается. В нашем случае полностью отбрасываются все пиксели в интервале от 0,13 до 0,92.
Это придаёт внешним полосам эффекта уникальный вид «блеска пищевой плёнки». Посередине же вода в основном состоит из более ярких пикселей, то есть высокий порог позволяет просвечивать только самым ярким пикселям. Это даёт нам такую пустую полосу и эти потрясающие засветы!
Забытое предание
В современном мире программируемых шейдеров, PBR-конвейеров и огромных бюджетов на графику подобные трюки всё больше напоминают позабытое знание. На мой пристрастный взгляд, игры эры GameCube имеют самую красивую графику для того времени. Даже несмотря на то, что я упоминал только GameCube, в Wii по сути используется то же оборудование, поэтому такие же трюки можно найти в играх серии Mario Galaxy, Super Smash Bros. Brawl и даже в The Legend of Zelda: Skyward Sword. Очень интересно, что с видеопроцессорной технологией, изобретённой в 2001 году, Nintendo прошла весь путь до 2012 года, когда была выпущена Wii U.
Хорошая художественная режиссура, огромные объёмы креативного дизайна и подробные знания «железа» позволили создать потрясающие эффекты даже в условиях таких жёстких ограничений. Ради развлечения попробуйте разобраться с эффектами стеклянных панелей на уровне Delfino Hotel или с усовершенствованиями этой техники, использованными в Super Mario Galaxy.
Код, использованный в каждом из этих демо, имеет открытые исходники и выложен на GitHub. Очевидно, что все труды по созданию исходной графики выполнены невероятно талантливыми художниками из Nintendo. Кроме того, я передаю особую благодарность всем моим друзьям, работающим в команде эмулятора Dolphin.
Комментарии (2)
nizkopal
28.03.2018 10:33+1Интересно, круто. Спасибо за перевод!
А я, почему-то вспомнил, как выглядела вода раньше. В тех же первой Готике или Морровинде:
Были же времена. :)
Сейчас с этим, конечно, намного лучше…
VBKesha
Читать про такие хитрости всегда интересно!