Замена материалов у загруженных моделей

A-Frame предоставляет удобный способ работы с 3D-моделями, используя сущность <a-entity> с компонентом gltf-model. Модели могут быть загружены в формате GLTF или GLB и содержать сложные структуры, включая иерархию объектов, материалы и текстуры.

Пример базовой загрузки модели:

<a-scene>
  <a-entity gltf-model="#myModel" position="0 1.5 -3"></a-entity>
</a-scene>

Здесь #myModel — идентификатор ресурса, определённого через <a-assets>.

<a-assets>
  <a-asset-item id="myModel" src="models/car.glb"></a-asset-item>
</a-assets>

После загрузки модели, все её материалы доступны через объект mesh Three.js, на котором основан A-Frame. Замена материалов требует работы с этим объектом.


Доступ к материалам загруженной модели

Когда модель загружается, событие model-loaded сигнализирует о готовности всех ресурсов. Для замены материала необходимо обработать это событие:

const entity = document.querySelector('[gltf-model="#myModel"]');

entity.addEventListener('model-loaded', () => {
  const model = entity.getObject3D('mesh');
  if (!model) return;

  model.traverse((node) => {
    if (node.isMesh) {
      // Здесь node.material содержит текущий материал
    }
  });
});

Метод traverse рекурсивно проходит по всем узлам модели. node.isMesh позволяет отличать меши от групп или пустых узлов. Каждый node имеет свойство material, которое может быть объектом THREE.Material или массивом материалов, если на меш наложено несколько.


Замена одного материала

Для простой замены материала создаётся новый объект THREE.MeshStandardMaterial или другой подходящий тип материала. Например:

import * as THREE from 'three';

entity.addEventListener('model-loaded', () => {
  const model = entity.getObject3D('mesh');
  model.traverse((node) => {
    if (node.isMesh) {
      node.material = new THREE.MeshStandardMaterial({
        color: 0xff0000,
        metalness: 0.5,
        roughness: 0.5
      });
    }
  });
});

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

if (node.name === 'Wheel') {
  node.material = new THREE.MeshStandardMaterial({ color: 0x000000 });
}

Замена нескольких материалов

Если модель содержит несколько материалов на одном меше, node.material может быть массивом. Для замены всех материалов используется итерация:

if (Array.isArray(node.material)) {
  node.material.forEach((mat, index, arr) => {
    arr[index] = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
  });
} else {
  node.material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
}

Таким образом можно точно контролировать, какие материалы заменять и на какие значения.


Использование текстур при замене материала

Для реалистичных эффектов применяется текстура. Текстуры загружаются через THREE.TextureLoader:

const loader = new THREE.TextureLoader();
const texture = loader.load('textures/metal.jpg');

entity.addEventListener('model-loaded', () => {
  const model = entity.getObject3D('mesh');
  model.traverse((node) => {
    if (node.isMesh) {
      node.material = new THREE.MeshStandardMaterial({
        map: texture,
        metalness: 1,
        roughness: 0.4
      });
    }
  });
});

Использование карт (map, normalMap, roughnessMap) позволяет достигнуть визуальной реалистичности при минимальных усилиях.


Динамическая замена материала

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

document.querySelector('#changeColor').addEventListener('click', () => {
  const model = entity.getObject3D('mesh');
  model.traverse((node) => {
    if (node.isMesh) {
      node.material.color.set(0x0000ff);
    }
  });
});

Важно: при динамическом изменении материалов следует учитывать, что текстуры и физические свойства сохраняются, если не создаётся новый объект материала.


Оптимизация и производительность

  • Создание большого числа новых материалов для сложных моделей может негативно влиять на производительность.
  • Использование одного материала с изменяемым цветом предпочтительнее для анимаций и интерактивности.
  • Проверка node.isMesh перед заменой материала предотвращает ошибки при обходе групповых узлов.

Итоговые рекомендации

  • Всегда ждать события model-loaded перед доступом к объекту mesh.
  • Использовать traverse для обхода всех узлов.
  • Для сложных моделей применять проверку имени узла или индекса материала.
  • При интерактивной замене отдавать предпочтение изменению свойств существующего материала, а не созданию новых объектов без необходимости.

Эти подходы позволяют полностью контролировать внешний вид моделей в A-Frame, создавая визуально насыщенные и интерактивные 3D-сцены.