How to Add a Secondary UV Map in Three.js
This article explains how to correctly apply a secondary UV map
(uv2) to a BufferGeometry in Three.js. You
will learn how to define the uv2 attribute by either
cloning existing UV coordinates or assigning custom ones, which is a
necessary step for rendering ambient occlusion (aoMap) and lightmaps on
your 3D models.
Why a Secondary UV Map is Needed
In Three.js, materials that utilize lightmaps (lightMap)
or ambient occlusion maps (aoMap) require a second set of
UV coordinates. While the primary uv attribute defines how
color textures wrap around a 3D model, the uv2 attribute
determines how light and shadow maps are mapped across the geometry.
Step 1: Clone Existing UVs (Quick Method)
If your lightmap or AO map uses the same layout as your default
texture, you can quickly create the uv2 attribute by
cloning the primary uv attribute.
// Assuming you have a mesh with BufferGeometry
const geometry = mesh.geometry;
// Check if the primary uv attribute exists
if (geometry.attributes.uv) {
// Clone the 'uv' attribute and assign it to 'uv2'
geometry.setAttribute('uv2', geometry.attributes.uv.clone());
}Step 2: Define Custom UV2 Coordinates
If your secondary map has a unique layout (for example, a
non-overlapping lightmap bake from Blender), you must define custom
coordinates. You can do this by creating a new Float32Array
and assigning it to the geometry as a BufferAttribute.
// Create an array of 2D coordinates (U, V) for each vertex
const uv2Array = new Float32Array([
0.0, 0.0, // Vertex 1
1.0, 0.0, // Vertex 2
0.0, 1.0, // Vertex 3
// Add coordinates for all vertices...
]);
// Assign the array to the 'uv2' attribute (item size of 2 for U and V)
geometry.setAttribute('uv2', new THREE.BufferAttribute(uv2Array, 2));Step 3: Apply the Texture to the Material
Once the uv2 attribute is added to the geometry,
Three.js will automatically detect it when you apply an
aoMap or lightMap to your material.
const textureLoader = new THREE.TextureLoader();
const aoTexture = textureLoader.load('path/to/ambient_occlusion.jpg');
const material = new THREE.MeshStandardMaterial({
color: 0xffffff,
aoMap: aoTexture,
aoMapIntensity: 1.0 // Adjust the intensity of the shadows
});
const mesh = new THREE.Mesh(geometry, material);By ensuring your BufferGeometry contains the
uv2 attribute, Three.js will correctly render complex
shadow and light details on your 3D meshes.