Use aoMap in Three.js MeshStandardMaterial

This article explains how to utilize an aoMap (Ambient Occlusion map) with MeshStandardMaterial in Three.js to add realistic, pre-calculated shadows to your 3D models. You will learn how to load the ambient occlusion texture, apply it to the material, adjust its intensity, and configure the critical second UV channel required for the map to display correctly.

1. Load the Ambient Occlusion Texture

To use an ambient occlusion map, you must first load the texture using the TextureLoader.

import * as THREE from 'three';

// Create a texture loader
const textureLoader = new THREE.TextureLoader();

// Load the AO map texture
const aoTexture = textureLoader.load('path/to/your/ambient_occlusion_map.png');

2. Set Up the MeshStandardMaterial

Once the texture is loaded, assign it to the aoMap property of your MeshStandardMaterial. You can also control the darkness of the shadows using the aoMapIntensity property, which accepts a float value (default is 1).

const material = new THREE.MeshStandardMaterial({
  color: 0xffffff,
  roughness: 0.5,
  metalness: 0.1,
  aoMap: aoTexture,         // Apply the AO map
  aoMapIntensity: 1.5       // Adjust shadow intensity (default is 1)
});

3. Configure the Second UV Channel (UV2)

In Three.js, ambient occlusion maps require a second set of UV coordinates (uv2) on the geometry to map the texture correctly. If your 3D model does not already contain a second UV channel (often exported from software like Blender), you must generate it.

You can copy the primary UV coordinates to the secondary UV channel using the following code:

const geometry = new THREE.BoxGeometry(1, 1, 1);

// Copy the 'uv' attribute to 'uv2'
const uvAttribute = geometry.attributes.uv;
geometry.setAttribute('uv2', new THREE.BufferAttribute(uvAttribute.array, 2));

If you are loading a model using GLTFLoader, you can traverse the scene and automatically apply the UV2 channel to all meshes:

gltfLoader.load('model.gltf', (gltf) => {
  gltf.scene.traverse((child) => {
    if (child.isMesh) {
      const geometry = child.geometry;
      
      // Ensure uv2 exists if an aoMap is used
      if (geometry.attributes.uv && !geometry.attributes.uv2) {
        geometry.setAttribute('uv2', new THREE.BufferAttribute(geometry.attributes.uv.array, 2));
      }
    }
  });
  scene.add(gltf.scene);
});

4. Create the Mesh

With the geometry and material configured, combine them into a mesh and add it to your Three.js scene.

const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);