Вангеры 3D: пример использования Emscripten в Rust

Трасировка лучей в оригинальной игре.Трасировка лучей в оригинальной игре.

Вангеры в 2022 году продолжают удивлять, прежде всего людьми которые когда-то полюбили эту игру, но сегодня не забывают и вкладывают много сил, делая её лучше, современнее. Оригинальная игра получила обновление с поддержкой полноценного 3D, да не абы какого, а кросс платформенного — работающего на всех системах и даже в браузере. Статья о том, как нетипичная связка технологий помогла запустить обновление в браузере.

Данный материал логическое продолжение предыдущей статьи Vange-rs: взгляд на реализацию WebAssembly в Rust. У читателей могло сложиться впечатление, что я обиделся на Rust из-за того что комьюнити напрасно отказывается от emscripten, ради горячо любимого wasm32-unknown-unknown.

Это совсем не так, я в значительной степени впечатлен уровнем интеграции wasm в Rust, в частности легкость которую дает wasm-bindgen для организации взаимодействия между js и Rust. Объективно это лучшее решение для проектов написанных на Rust (RIIR).

В то же время, Emscripten нацелен больше на C/C++ проекты, поэтому достаточно закономерно, что он используется значительно реже в Rust. Тем не менее wasm32-unknown-emscripten работает хорошо, а большинство крейтов компилируются без ошибок из коробки. Так, Vange-rs зависит от 211 крейтов, и только 4 имели проблемы совместимости с Emscripten (winit, glow, ron, wgpu). Сложности начинаются, когда нужно задействовать API браузера, все крейты, которые оборачивают это API, построены на базе wasm-bindgen, а он не поддерживает Emscripten. Поэтому, если Rust проект выполняет не только вычисления, но и подразумевает какой-то визуальный интерфейс или взаимодействие с пользователем, то скорее всего скомпилировать в WebAssembly средствами Emsripten его не получится.

Однако, это правило действует и наоборот: если Ваш проект написан на C/C++ и связывает (link) (статически/динамически) библиотеки на Rust, то это автоматически означает что вы не сможете его скомпилировать в WebAssembly и запустить в браузере, используя wasm32-unknown-unknown. Потому что, ABI unknown не совпадает с ABI Emscripten или wasi, и нет компилятора C/C++ в ABI unknown.

Для такого рода проектов, можно и нужно использовать Emscripten.

А есть ли такие проекты?

В прошлой статье для экспериментов был использован проект vange-rs, от автора wgpu @kvark. Он включает в себя современный GPU рендер на основе нескольких оригинальных техник, самая перспективная из которых делается при помощи трассировки лучей. Этот проект был взят не просто так. В сообществе Вангеров зародилась идея внедрить рендер на базе vange-rs в саму игру. Эта амбициозная цель настолько всем понравилась, что вокруг неё собралась небольшая команда сильных программистов и сочувствующих.

Взяв за основу vange-rs, Никита Пряничников, создал общую абстракцию рендеринга, позволяющую переключать оригинальный ренедер Вангеров и динамически подключаемый из vange-rs. @kvark сделал из своего проекта ffi библиотеку, реализующую эту абстракцию. Таким образом, используя статическое связывание, удалось завести рендер vange-rs в оригинальных Вангерах.

Но, не всё так просто. vange-rs на данный момент используется только как рендеринг поверхности, все остальное обрисовывается используя оригинальный код Вангеров. А работает это следующим образом. При разработке HD версии, Никите удалось разделить рендер Вангеров на два слоя: нижний слой карты и верхний слой с мехосами, эффектами, интерфейсом и пр. Эти два слоя объединяются в единое изображение перед выводом кадра на экран.

Слоистый рендерСлоистый рендер

vange-rs выступает как замена оригинального слоя карты, а слой интерфейса отображается, используя оригинальный код. Чтобы добиться визуального совпадения, пришлось синхронизировать камеры vange-rs и Вангeров, а так же регулярно пересылать все изменения, которые вносит игра в карту (например, следы от колёс). Алгоритм работы с картой весьма хитрый и размазан по всему коду оригинальной игры. Так, отображение большинства эффектов влияет на ландшафт и вносит в него изменения. Поэтому, оригинальный рендер карты выполняется всегда, а рендер 3D использует данные 2D карты в своей работе. Была проделана большая работа, чтобы выделить все места, где происходит изменение ландшафта, и выполнить синхронизацию. Большое спасибо ребятам за проделанный труд!

Как такое возомжно?Как такое возомжно?

Интересный факт: кажется что изображения выше не должны работать в слоистом рендере, но работают! Код Вангеров, как и сама игра, удивителяет. Верхний слой с мехосами учитывает ландшафт своего расположения и отображается соответствующим образом. Мехосы находятся выше по иерархии слоев, но отображаются правильно.

РазгадкаРазгадка

Итак, 3D рендер в Вангерах, это та самая связка C++ и Rust, которую можно скомпилировать в WebAssembly используя Emscripten или wasi. Более того, Вангеры уже были скомпилированны в WebAssembly с помощью Emscripten. Осталось только добавить поддержку Emscripten в wgpu. Обратите внимание, как мало потребовалось правок, чтобы этого добиться. wgpu не использует браузерные API для GL бэкенда, а Emscripten подменяет нативный GL своей реализацией на WebGL-2. Благодаря этому С/C++ проекты, использующие wgpu (wgpu-native), будут работать в браузере через GLES-3 бэкенд. Т.е. wgpu становится альтернативой bgfx или sokol в браузере. Можно взять условный glfw/SDL1/2 подключить к нему wgpu-native и быть уверенным что проект заработает в браузере.

Например, другие проекты KD-Lab: Периметр и Самогонки используют старый рендер на DX9, из-за чего работают только на windows. Сообщество хочет заменить этот рендер на что-то более современное, например wgpu или bgfx. Решение ещё не принято, но теперь есть уверенность, что если это произойдёт то эти игры будут доступны в браузере.

Вангеры 3D в браузере

Вернемся к нашей любимой игре. Вот последовательность шагов которая требуется что бы добавить поддержку vange-rs в браузер.

  1. Компилируем librusty_vangers.a используя Emscripten triple:

cd ${VANGE_RS_ROOT}/ffi
cargo build --target wasm32-unknown emscripten --release --features emscripten
  1. Добавляем эту библиотеку при связывании основной игры:

# CMakeLists.txt
    target_link_directories(vangers-web PUBLIC "${VANGE_RS_ROOT}/target/wasm32-unknown-emscripten/release/")
    target_link_libraries(vangers-web rusty_vangers)

И это всё! В точности так же, как и с другими компиляторами gcc/clang.
Emscripten помимо всего прочего имеет мощный оптимизатор WebAssembly:

vange-rs web version: 25Mb // из предыдщуей статьи

librusty_vangers.a (rust)
debug: 151 Mb
release: 25 Mb

vangers (c++):
relase: 1.8Mb

optimized and linked with Vangers:
debug: 77 Mb
release: 3.7 Mb

Всего лишь 3.7Mb (1.9Mb — 3D рендер), занимает полная игра Вангеров + 3D рендер на Rust.

Сейчас идёт активная разработка нового рендера, поэтому совместимость отстает. Для запуска в браузере потребуется поддержка WebGL-2 и текстур размером 16384 пикселей (у меня работает только в Chrome на ПК). Чтобы запустить в браузере:

  1. Перейдите на сайт игры Вангеров

  2. Откройте »Настройки» → »Дополнительно»

  3. Включите »3D режим»

  4. В игре настройте камеру с наклоном или вращением, что бы увидеть потрясающее 3D

  5. Клавиша »\» динамически переключает режим отображения игры

image-loader.svg

Периметр и самогонки

Открытое обращение сообщества, к правообладателям:

За последний год с разрешения разработчиков исходные коды других игр KD-Lab, «Периметр» и «Самогонки» были опубликованы в Open Source. Сообщество поклонников этих игр активно адаптирует эти игры под современные компьютеры и операционные системы. Если на Хабре есть представители издателей игры (как я понимаю, 1С), то мы будем рады, если вы захотите делать обновления игр в Стиме на базе разработок сообщества.

Отказ от ответственности

Прошу не рассматривать статью как рекламную. Все выше изложенное является моим личным мнением и никак не связано с официальной позицией компании GamePix. Рынок России для GamePix не является целевым, поэтому выражаю им огромную благодарность за возможность портирования и публикации Вангеров в браузере.

© Habrahabr.ru