Карта нормалей представляет собой текстуру, которая используется для добавления мелких деталей на поверхность 3D-объекта без увеличения количества полигонов. Она изменяет направление нормалей на каждом пикселе модели, создавая эффект неровностей, выступов, трещин и других деталей освещения. В отличие от bump map, normal map хранит данные о направлении нормалей в RGB-каналах, что позволяет получать более точное и реалистичное освещение.
RGB-каналы карты нормалей кодируют смещение нормали:
Значения каналов нормализуются в диапазоне [0, 1] и преобразуются в векторы нормалей. Эти векторы затем используются в шейдерах для расчёта освещения: отражения, бликов и теней, создавая иллюзию сложной геометрии на простой поверхности.
Three.js поддерживает карты нормалей через материал MeshStandardMaterial или MeshPhongMaterial. Для подключения нормали необходимо:
import * as THREE from 'three';
const loader = new THREE.TextureLoader();
const normalTexture = loader.load('textures/brick_normal.jpg');
const material = new THREE.MeshStandardMaterial({
color: 0xffffff,
normalMap: normalTexture,
});
const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
В результате на кубе визуализируется текстура с видимыми неровностями, которые реагируют на источник света.
Three.js позволяет контролировать эффект карты нормалей через
свойство normalScale материала. Оно представляет собой
вектор типа THREE.Vector2, задающий масштаб смещения по
осям X и Y:
material.normalScale.set(1, 1); // стандартный масштаб
material.normalScale.set(0.5, 0.5); // уменьшение эффекта
material.normalScale.set(2, 2); // усиление эффекта
Изменение normalScale позволяет добиться нужного уровня детализации без изменения текстуры.
Для расширенного контроля над эффектом можно использовать ShaderMaterial. В фрагментном шейдере нормаль берётся из текстуры и преобразуется в пространство мировых координат для расчёта освещения:
vec3 normalTex = texture2D(normalMap, vUv).rgb;
normalTex = normalize(normalTex * 2.0 - 1.0); // преобразование из [0,1] в [-1,1]
vec3 finalNormal = normalize(vNormal + normalTex * normalScale);
Такой подход позволяет комбинировать несколько карт нормалей, создавать динамические эффекты, изменять неровности в реальном времени.
normalTexture.minFilter = THREE.LinearMipMapLinearFilter;
normalTexture.magFilter = THREE.LinearFilter;
const material = new THREE.MeshStandardMaterial({
map: texture,
normalMap: normalTexture,
normalScale: new THREE.Vector2(1, 1),
roughness: 0.5,
metalness: 0.1,
});
Эта конфигурация создаёт реалистичное освещение, глубину и мелкие детали без увеличения числа полигонов, что делает normalMap одним из ключевых инструментов оптимизации и реализма в Three.js.