Лидерборд — это таблица, ранжирующая участников заезда по какому-либо критерию, например, по позиции на маршруте. Фактически, это логика гонок, которая определяет, кто побеждает, а кто проигрывает. Правильная реализация лидерборда — не такая уж простая задача, как может показаться, потому что она зависит от того, как игра хранит трассу, и во многом пересекается с кодом ИИ-оппонентов.
Если отталкиваться от методологии, которую я описал в статье Как сделать ИИ для гонок, то задача упрощается – можно использовать следующий подход, который показался мне весьма надежным и лишенным логических изъянов.
Выпустил новую версию Dagon. Основным поводом к этому релизу стало исправление серьезного бага, связанного с горячим подключением геймпадов (в качестве индексов для GameInputDevice использовались неправильные ID). Кроме того, игра заметно подтормаживала при вызове InputManager.getAxis из-за использования SDL_GameControllerGetAxis — эта проблема была решена использованием значений осей из событий SDL_CONTROLLERAXISMOTION. Соответственно, GameInputDevice теперь имеет поле axisValues.
Исправлена утечка памяти в методе Application.takeScreenshot.
Добавлена поддержка суперсэмплинга. Он управляется при помощи опции ss.sampleRatio в render.conf. Если выставить в нее 2, то игра будет рендериться в двухкратном разрешении.
Шейдер FXAA заменен на более качественную версию 3.11. Добавлена поддержка независимого от разрешения радиуса размытия для фильтра свечения (glow). Если в render.conf присутствует опция glow.normalizedRadius, то обычный glow.radius не используется — радиус вычисляется динамически как доля высоты экрана. Например, если выставить значение 0.005, то при 1080p радиус свечения будет 6 пикселей.
Вышел Dagon 1.1.1. Добавлено свойство GLTFMeshPrimitive.materialIndex, индекс материала в файле glTF. Этот индекс записывается в структуру Triangle при обходе по граней с помощью GLTFAsset.opApply, что позволяет передавать данные о материалах в различные обработчики мешей. Основное применение этой фичи — учет материалов при обнаружении столкновений и рейкастинге. Соответственно, в dagon:newton добавлена поддержка индексов материалов при создании NewtonMeshShape. В интерфейсный метод NewtonRaycaster.onRayHit добавлен параметр long id, через который коллбек получает искомый индекс материала при попадании луча в меш.
Пошла серьезная работа над Chillwave Drive. Я уже рассказывал про ИИ, но, помимо этого, на днях улучшил модель колеса и наконец-то добавил поддержку материалов, чтобы машина учитывала свойства покрытия. Например, когда машина вылетает с трассы, то трава на обочине сильно ее замедляет и не дает разгоняться. Также добавил HUD-тахометр, обновил текстуры тестовой трассы, реализовал эффекты погоды — дождь и динамически появляющееся и исчезающее за облаками солнце. У игры появилась страничка на GameJolt, где я регулярно выкладываю альфа-версии.
Привел в порядок блог, объединил старую версию и новую, подкорректировал неактуальную информацию.
В предыдущем посте я уже писал о том, почему glTF нельзя использовать в качестве полноценного формата игровых ассетов, и вот альтернатива, которую я специально проектирую для Dagon 2.0 — Dagon Asset Format, сокращенно DAF.
DAF хранит, в первую очередь, вершинные буферы для прямой передачи в VRAM. Формат бинарный, архитектурно соответствующий API Dagon 2.0 и расширяемый: в нем могут быть объявлены любые дополнительные структуры данных и даже динамические свойства, не ломая обратную совместимость.
Основные задачи формата:
Хранение данных в форме, подходящей для прямой загрузки в видеопамять без накладных расходов на обработку (zero overhead). В отличие от glTF, в DAF вершинные буферы имеют фиксированный формат, согласованный с пайплайном движка, не требуя конверсии;
Максимальная эффективность десериализации. glTF требует парсинга JSON и динамического построения довольно сложных объектов в памяти (списков, словарей), а загрузка DAF – это просто реинтерпретация слайсов байтового буфера в массивы POD-структур. DAF экономит память и сокращает риск утечек, поскольку не требует множества аллокаций;
Частичная десериализация. Декодер может читать из DAF только те данные, которые ему нужны, не разбирая остальные.
Формат «все в одном». Файл DAF может хранить как отдельный меш, так и целую сцену. Все структуры формата поддерживают пользовательские свойства, что позволяет хранить в DAF метаданные редактора. Фактически, DAF может быть использован как простая NoSQL база данных для различных целей.
Поддержка семантики данных. Все объекты имеют список классов, что позволяет движку группировать их для задач игровой логики. Все текстуры помечаются как baseColor, normal, height, roughness-metallic, emission, для того, чтобы движок мог выбрать оптимальный BCn формат сжатия.
Поддержка данных для физики и проверки столкновений (в разработке).
За последние недели значительно продвинулся с портом: реализовал в Dagon 2 загрузку glTF (через библиотеку Assimp), обновил шейдер антиалиасинга, добавил цветокоррекцию и поддержку 3D-текстур, проделал множество мелких улучшений в рендере.
Модель — Datsun 280Z от Martin Trafas (бесплатная).
Как и планировал, сумел победить зашумленность отражений при помощи накопления данных с предыдущих кадров (с репроекцией по motion-векторам для компенсации медленного движения камеры):
Это решение также неплохо сглаживает шум, вызванный неоднородностью нормалей:
При резких движениях, конечно, репроекция не спасает, приходится сбрасывать историю, и шум возвращается. И, конечно, экранная трассировка — это аппроксимация, у нее всегда будут видимые артефакты в виде пустот там, где сэмплинг невозможен ввиду недостатка информации в буферах. Но результат вполне, как говорится, shippable — мне кажется, у меня получилось даже лучше, чем во многих коммерческих играх. Во многом, это благодаря использованию GGX importance sampling: распределение лучей очень близко к тому, что делается для префильтрации кубических карт под PBR.
Реализацию этого эффекта я в свое время отложил до выхода первой версии движка, и вот, наконец, пришло время за нее взяться. SSLR (screen-space local reflections) — это расчет отражений в пространстве экрана, упрощенная разновидность трассировки лучей. Берется HDR-буфер, буферы глубины и нормалей и в каждой точке выстреливается луч в направлении зеркального отражения поверхности. Луч делится на сегменты, и каждый сегмент последовательно проверяется на попадание в геометрию (путем сравнения Z со значением из буфера глубины). Если попали, то читаем цвет, соответствующий по экранным UV-координатам точке попадания.
На низких уровнях шероховатости результаты получились неожиданно качественные. На высоких, конечно, присутствует шум, который я планирую смягчить темпоральной аккумуляцией.
Разработка Dagon 2.0 началась в весьма бодром темпе. Deferred-рендер уже почти готов — остались только тени PSM и DPSM, поддержка локальных зондов освещения и forward-проход. Я добавил систему кэширования ресурсов, сжатие в BC7 (на основе компрессора bc7enc Рича Гелдриха) и проделал еще множество мелких улучшений на разных стадиях формирования кадра. Особое внимание я уделил тому, чтобы картинка соответствовала Eevee в Blender 5.
Что еще нового? Часть функциональности, которая в Dagon 1.0 реализована в качестве расширений, теперь входит в ядро — это физика на базе Jolt и загрузчик текстур в формате KTX/KTX2. Такое решение я принял исходя из полезности этих фич, простоты сборки Jolt и libktx из исходников и их автономности: они не имеют собственных зависимостей и отлично работают на всех платформах (в противовес тому же Newton, который имеет проблемы с работой некоторых функций под Linux). Наличие libktx «из коробки» дает серьезные преимущества и ставит Dagon 2 в авангард движкостроения; в будущем не исключен перевод текстурного кэша с DDS на KTX2.
Еще одним нововведением будет встроенная VM GScript3, которую я разработал в прошлом году. Движок при старте загружает скомпилированный байт-код скрипта и выполняет его, а скрипт, в свою очередь, навешивает обработчики событий, позволяя, таким образом, выполнять внешнюю логику без пересборки игры. Игра может экспонировать скриптовой системе свои данные и методы, что полезно для создания модов. Некоторые встроенные классы Dagon уже реализуют интерфейс GsObject и напрямую совместимы с GScript: это Entity, Scene, World, BaseGame.
Зарегистрирован пакет dagon2 в реестре DUB, так что начать пользоваться можно уже сейчас, несмотря на то, что разработка находится на ранней стадии.
Рад сообщить, что наконец-то выпустил Dagon 1.0.0, первый стабильный релиз моего игрового движка! Это в основном исправляющий релиз, который подытоживает огромную работу над Dagon, которую я проделал за последние два года.
Исправлен баг с неправильным сэмплингом BRDF LUT, что ранее приводило к излишней яркости шероховатых поверхностей. Исправлен эффект тумана для точечных источников света. Улучшена система частиц. Добавлено свойтсво Emitter.fadeInDuration для плавного появления частиц из прозрачности, а также Emitter.gravity для управления гравитацией частиц. Свойство Emitter.initialPositionRandomRadius теперь является вектором и называется Emitter.initialPositionRandomRadii — это радиус-вектор, позволяющий задать разброс случайного появления частиц отдельно по всем трем осям. Добавлен новый тип событий CustomResize, который используется в тех случаях, когда пользовательский код должен обрабатывать кастомный ресайз вьюпорта (обычно это необходимо, если размер вьюпорта не привязан жестко к размеру окна и вычисляется другим способом). Также менеджер событий теперь обрабатывает SDL_WINDOWEVENT_CLOSE для грациозного закрытия игры при нажатии Alt+F4. Добавлен новый метод Application.isWindowMinimized. Добавлен новый режим воспроизведения анимации для GLTFBlendedPose — PlayMode.OnceAndStop. Он позволяет проиграть анимацию один раз и остановиться на последнем ключевом кадре. Добавлено новое свойство GLTFBlendedPose.timeScale для управления скоростью анимации. Добавлено свойство Light.angularRadius, которое симулирует солнечный диск угловым радиусом распределения света. Улучшен шейдер ночного неба StarfieldSkyShader: добавлены свойства sunEnergy (энергия светила), spaceColorZenith (цвет пространства в зените), spaceColorHorizon (цвет пространства на горизонте). Добавлено новое свойство FirstPersonViewComponent.roll — поворот камеры от первого лица по оси Z для эффекта наклона головы вбок. В расширении dagon:jolt добавлена поддержка convex cast (JoltPhysicsWorld.shapeCast). Контроллер персонажа JoltCharacterController теперь использует shapeCast в логике приседания (для проверки высоты потолка над головой). Как следствие, персонаж теперь автоматически приседает, чтобы избежать врезания головой в низкие потолки. Добавлен метод onGround, который возвращает true, если персонаж стоит на земле. Исправлена компиляция расширения dagon:nuklear.