Пример LOD из данного урока
Пример LOD из данного урока

В данном уроке расскажу о своем способе организации работы с уровнями детализации 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).

Итого

Благодаря проведенной оптимизации стандартного подхода мы экономим память и ускоряем загрузку деталей.

Результат работы данного алгоритма можно посмотреть на примере.

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

Комментарии (2)


  1. Hasthur
    03.07.2023 10:23

    Похоже, что вы в дальнейшем не переключаете lod по дистанции (на понижение). В этом случае для стартовой геометрии в userData лучше делать dispose. А если под капотом у вас всё равно threejs, то встроенную lod-механику оптимальнее использовать, чем создавать ссылку в userData


    1. site3d Автор
      03.07.2023 10:23

      При отдалении lod понижается, это видно из примера в статье. Стартовая геометрия нужна для того, чтобы просто запомнить начальное состояние модели самой низкой детализации, т.к. она затирается при сборке итоговой геометрии. Встроенная в three.js lod-механика использует описанный в статье стандартный подход.