Вход на сайт

Просмотр новости

Найдите то, что Вас интересует

AAA Движок для GTA San Andreas в браузере и переписывание ThreeJS

Дата публикации: 04-08-2026 07:27:12

Данная статья - это продолжение повествования о том, как я делаю AAA-движок для запуска старых игр, а именно GTA San Andreas и её модов, в браузере. В ней речь пойдет о том, как я уперся в производительность Three.js и перевел движок на свой фреймверк под WebGPU. Читать далее

Основное содержимое страницы с новостью.

Если лень читать - сразу дам ссылки на важные ресурсы:

Прошлые статьи серии:

В прошлых эпизодах я описывал создание первого прототипа, а затем исправление проблем с картой.

Настала пора самого интересного - сделать современную графику.

Изначально движок RenderWare не предоставляет возможностей динамических теней и освещения: все эти данные зашиты внутри объектов мира как статический prelight и как нарисованные тени (тоже статика).

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

Перед началом, я написал пару тестовых сценариев, где камера пролетает над самыми тяжёлыми местами карты, чтобы измерять FPS. Замерять FPS я решил на каждом шаге своей работы.

Первый прогон, ещё без всех модификаций, показал не слишком оптимистичные результаты: мои улучшения карты, особенно высокополигональная растительность, дали серьёзную просадку по FPS. Причём просело неравномерно: за городом было 43 FPS, в Сан-Фиерро и Лас-Вентурасе - по 30, а в Лос-Сантосе, самом плотном городе, всего 18–19. То есть тяжёлый город лежал ещё до того, как я взялся за графику. Что ж, посмотрим, как будет дальше.

Вначале я подобрал систему управления цветом, выбирал из трёх вариантов: AgX, ACES, Neutral.

AGX

AGX

ACES

ACES

Neutral

Neutral

Остановился на ACES как на золотой середине между HDR-подходом Neutral и совсем блёклой картинкой AgX.

Сделал PBR-небо и правильный туман.

City

City

Картинка становилась всё лучше и лучше…

Cars

Cars

City

City

Night

Night

Night 2

Night 2

Shadows

Shadows

Sun

Sun

И наконец-то я сделал настоящий свет фар.

Car lights

Car lights

Car lights 2

Car lights 2

Но FPS всё падал и падал. В очередной проход, после докрутки каскадных теней и света, я сделал замер.

Low FPS

Low FPS

FPS скатился до 12–18. Это никуда не годилось, так как по сути мы имеем только пустой город, а его ещё надо наполнять машинами - и на этот счёт у меня большие идеи сделать эмуляцию жизни, а на неё нужны большие ресурсы.

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

Shadows

Shadows

Shadows 2

Shadows 2

Подходы рендера 3D-графики в браузере

Если объяснять по-простому, то существует два способа рендера.

WebGL - довольно старая технология. У нас много Draw Call, а так как карта состоит из множества объектов, их подготовка осуществляется на уровне CPU и забивает main thread, поэтому появляются фризы и теряются FPS.

WebGPU - новая технология, позволяющая использовать все возможности видеокарты устройства: в ней мы можем убрать нагрузку с CPU, освободив main thread.

Моя текущая версия была полностью построена на WebGL, значит, надо переезжать на WebGPU. Но есть проблема - ThreeJS не в полной мере поддерживает WebGPU. Правда, тогда я этого ещё не знал, поэтому об этом позже.

WebGPU

Прежде чем брать и переписывать, я решил сделать отдельную сцену с эмуляцией стриминга: разместил 15к объектов с заменой 2к на лету в течение нескольких секунд.

Результат на ThreeJS оказался неутешительным.

Test

Test

На FPS в такой сцене смотреть бесполезно: 15к коробок стоят друг за другом, видеокарта много раз перерисовывает одно и то же место, и цифра ничего не значит. Смотреть надо на другое - сколько времени процессор тратит, чтобы собрать один кадр и отдать его видеокарте. Чем меньше, тем лучше.

И вот тут WebGPU в ThreeJS дал 27 миллисекунд против 10 на старом WebGL. То есть новая технология оказалась в два с половиной раза МЕДЛЕННЕЕ старой. Такой переезд ничего не даёт!

Попробовал Babylon. На обычном WebGL он оказался ещё хуже - втрое медленнее ThreeJS. Зато в режиме WebGPU он умеет один хитрый трюк: записать всю сцену один раз и дальше просто проигрывать эту запись. С ним сборка кадра ускорилась почти в сто раз. Такого я не ожидал.

Но стриминг всё портил. Запись у Babylon одна на весь мир: стоит добавить или убрать хоть один объект - и её надо перезаписывать целиком, а это 50 миллисекунд. У меня же карта грузит и выгружает объекты постоянно, пока едешь. То есть я получал бы заметный рывок картинки на каждой такой подгрузке. Babylon - отличное решение для статики, но в динамике он проседает.

Мы с Fable решили написать патч для ThreeJS. И патч сработал: сборка кадра ускорилась в пять раз, карта стала загружаться быстро, камера летала, машина ехала.

Block

Block

Low FPS

Low FPS

А FPS не вырос. Вообще. Кадр всё так же занимал треть секунды, игра шла на 3–4 FPS.

Дальше пошли раунды раскопок, и вот что выяснилось: время сидело не в процессоре. Всё, до чего можно дотянуться снаружи фреймворка, мы вычистили - сборка кадра в итоге стала быстрее раз в пятнадцать, память прижали. А кадр всё равно уходил в видеокарту и пропадал там на сотни миллисекунд. Причём если уменьшить разрешение в четыре раза, это время не менялось вообще - значит, дело не в том, что видеокарте много работы. Оно сидело внутри самого ThreeJS, в его прослойке к видеодрайверу. А туда снаружи не залезть - надо владеть этой прослойкой.

После очередных раундов кодинга Fable выдал мне это.

Stuck

Stuck

Весь проект был под угрозой. Что делать?

Создание своего ThreeJS

Оставалось только одно - писать свой ThreeJS, который будет WebGPU-first и сразу адаптирован под нагруженный стриминг.

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

Было создано отдельное приложение, я назвал его "Лаба", где мы с Fable шаг за шагом воспроизводили то, что умеет прод-версия.

First Render

First Render

Вначале отрендерили множество простых кубиков и кубиков с альфа-каналом. Результат - 120 fps. Неплохо, идём дальше.

Рендер одного из городов. 120 fps:

Map

Map

При этом на каждом шаге я делал замеры benchmark, о которых ниже.

Карта с базовым освещением. 120 fps.

Map Lights

Map Lights

Карта с ночным освещением - всё те же 120:

Map Night

Map Night

Глобальная переделка

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

Её архитектура была сделана в unix-стиле, где каждый инструмент получает на вход игру и отдаёт пропатченную игру.

Packer

Packer

И я подумал: а что если - раз уж мы полностью меняем движок - сделать свой формат как моделей, так и текстур?

Что это может дать:

  • Мы можем зашить тени в LOD-объекты, а считать тени только для HD-сектора (равно как и свет).

  • Мы можем сложить текстуры в массивы, чтобы видеокарта брала их пачкой, и резать этим количество вызовов отрисовки. Классический вариант - свалить всё в один большой атлас - я тоже попробовал, и он оказался на 7 % хуже: в GTA текстуры замощают поверхность повторением, а в атласе повторять нечего.

  • Мы можем сделать абсолютно новые игровые механики, которые не предусматривает оригинальная игра.

Короче, начали делать дополнительный инструмент.

Так выглядят запечённые тени.

Shadows

Shadows

Конечно, без багов никуда.

Bug 1

Bug 1

Bug 2

Bug 2

Bug 3

Bug 3

Не буду долго томить: спустя множество раундов я получил инструмент, который не только улучшает графику ценой нулевых затрат в рантайме, но и делает модели легче, текстуры оптимальнее и так далее.

Возникла новая проблема - так как это новый формат, мы должны познакомить с ним и анимацию, и автомобили, и прочие вещи… Спустя время мы имели:

Car

Car

Промежуточные итоги:

Results

Results

После таких оптимистических цифр было принято решение - ПЕРЕПИСЫВАТЬ THREEJS!!!

В игре

Первый запуск в игре вышел комом, но - мир на движке, исторический скрин!

In Game

In Game

Куча багов, куда же без них.

Bug 1

Bug 1

Bug 2

Bug 2

Bug 3

Bug 3

Bug 4

Bug 4

Новый движок позволил сделать красивые облака.

Clouds

Clouds

Воду.

Water

Water

Реалистичное освещение было бы невозможно без преобразования карты. Вот как оно выглядит на карте без обработки, о которой я рассказывал в прошлой статье:

Lights Bug

Lights Bug

Normals Bug

Normals Bug

А вот как оно выглядит с обработкой:

Fix

Fix

Процесс работы

Такой большой пласт работы невозможен без постоянного контроля как качества, так и бенчмарков. Я делал замеры постоянно, реагировал на просадки FPS. В большинстве случаев они были связаны с багами.

Графика пока по уровню не полностью соответствует моим ожиданиям, но я буду над этим работать.

Также из наблюдений: Fable ошибается! И сильно! Была ситуация - две проблемы подряд: движок рассчитан только на одно авто, а авто рассчитано на один набор краски. То есть в мире бы ездили только авто одной марки с одним цветом. Это был не баг, а архитектурное решение.

Вообще работа с Fable мне напоминает работу с реально крутым разработчиком - Fable может спорить, отстаивать свои достижения, но все равно финальное слово за мной.

Подход с "Лабой" это настоящий опыт крупных компаний которые проводят масштабные рефакторинги - на малых фрагментах мы осуществляем гипотезу, потом мелкими шагами проводим замену, замеряя каждый шаг. На каждом этапе мы можем вернутсья к старой версии, так как удаление происходит только после полной миграции, мы можем сравнить и посмотреть, чего не хватает и как было сделано ранее. Последовательно пряча за флаги старые фичи и заменяя их новыми. И в финале мы просто деперкейтим устаревший код.

Итоги

Буквально за 2 недели я улучшил перфоманс с 16–18 до 100-120 FPS. Кадр стал собираться в 3–7 раз быстрее, а команд видеокарте движок отдаёт в 5–12 раз меньше. Всё это большими трудами и усилиями, в пока ещё не оконченном и не до конца подогнанном состоянии, но это огромный шаг вперёд, дающий мне хороший буфер производительности для имплементации самого важного - системы жизни в городе.

Мой следующий шаг - создать полноценный живой город, с персонажами и авто, дальностью прорисовки и прочим. Чтобы это смотрелось как мегаполис.

После этого я снова вернусь к графике, так как пока многие вещи находятся в процессе и мне нужно делать замеры на реально загруженном городе, чтобы понимать лимиты.

Продублирую полезные ссылки на важные ресурсы:

Прошлые статьи серии:

Всем спасибо за внимание. Продолжение следует.

Схожие новости

#Наименование новостиТональностьИнформативностьДата публикации
1Сложные сайты на вайбкоде: четыре лендинга, два пути к «дорогим» эффектам и 3D-логотип из спрайтов09.7705-08-2026
2Реальная и мнимая многопоточность в JS05.8330-07-2026
3Объекты в JS не бесплатные08.7405-08-2026
4Next.js, localStorage в App Router без hydration-ошибок07.1730-07-2026
5Картинка грузится, а fetch к тому же хосту — нет: как CSP тихо сломала три интеграции07.204-08-2026
6Как мы несколько дней искали проблему с перемоткой WebM08.8631-07-2026
7React Update Lifecycle: что происходит при обновлении компонента08.8605-08-2026
8Offline‑first PWA на примере фитнес‑трекера08.5505-08-2026
9[Перевод] <usermedia> — новый HTML элемент08.9631-07-2026
10Продакшн‑разработка в одиночку с AI‑агентами: принуждай к правилам, а не объясняй их08.2524-07-2026

Классификация: Пресс-релизы. Схожих патентов: 0. Схожих новостей: 10. Тональность: 0. Информативность: 6.18. Источник: habr.com.