Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
В данном уроке расскажу о своем способе организации работы с уровнями детализации 3D объектов. Не претендую на новаторство. Делюсь своим опытом.
При разработке своего сервиса для встраивания 3D моделей на сайты стояла задача оптимизации процесса рендеринга всеми возможными способами. Одним из таких является технология LOD, когда мы показываем модель с разной детализацией в зависимости от расстояния камеры до объекта.
Стандартный метод
Первое что приходит на ум – для каждого диапазона приближения подгружать целиком модель с нужным количеством полигонов. Но такой подход мне показался не совсем рациональным. Ведь у модели могут быть части, которые не требуют изменения в деталях, и их может быть достаточно много, а мы их будем многократно подгружать без полезного эффекта.
Таким образом, при стандартном подходе к LOD мы можем зря расходовать память при хранении разных вариантов модели и увеличивать время их загрузки.
Оптимизация LOD
Мне пришла в голову следующая идея хранения информации о деталях модели:
Для первого уровня детализации мы храним в отдельном файле все меши нашей 3D модели с минимальным числом полигонов.
Для последующих уровней храним в соответствующих файлах только те меши, где детализация была изменена.
Далее получаем следующий алгоритм сборки нашего 3D объекта в зависимости от текущего уровня детализации:
Загружаем модель начального LOD
Определяем текущий уровень детализации на основе дистанции до объекта
Загружаем вариант модели нового уровня, если он еще не был загружен
Пробегаемся от начального уровня до текущего и последовательно заменяем геометрии мешей соответствующих уровней
Ниже приведу код последнего шага алгоритма (под капотом three.js):
for (let i = 0; i <= newLodLevel; i++)
{
const level = this.lodLevels.get(i);
level.meshes.forEach(mesh =>
{
const model = this.model.item(mesh.name);
const geometry = i === 0 ? mesh.userData.geometryInit : mesh.geometry;
model.setGeometry(geometry, {notUpdateGeometry: true});
});
}
Из важных нюансов данного фрагмента реализации алгоритма – для начального уровня мы берем геометрию начального состояния мешей (предварительно склонировали геометрию в userData.geometryInit)
Итого
Благодаря проведенной оптимизации стандартного подхода мы экономим память и ускоряем загрузку деталей.
Результат работы данного алгоритма можно посмотреть на примере.
Буду благодарен обратной связи по моему алгоритму. Надеюсь, что кому-то он будет полезен в своих проектах.