# Using multiple UV maps per model (r150 and earlier)

**URL:** https://discourse.threejs.org/t/using-multiple-uv-maps-per-model-r150-and-earlier/41973
**Category:** Resources
**Tags:** models, uv-mapping, materials
**Created:** [August 28, 2022, 2:34pm UTC](https://discourse.threejs.org/t/using-multiple-uv-maps-per-model-r150-and-earlier/41973 "2022-08-28T14:34:33Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![mjurczyk](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mjurczyk/32/10995_2.png) [@mjurczyk](https://discourse.threejs.org/u/mjurczyk)
#### Post date: [August 28, 2022, 2:34pm UTC](https://discourse.threejs.org/t/using-multiple-uv-maps-per-model-r150-and-earlier/41973/1 "2022-08-28T14:34:33Z")

</div>

# 🗺 How to assign custom UV map to any texture on the model material

Having way more time than it is appropriate on my hands and inspired by the unbearable lack of closure to “[Multiple UVs Material for one single mesh. Is it somehow possible?](https://discourse.threejs.org/t/multiple-uvs-material-for-one-single-mesh-is-it-somehow-possible/20458)” - here we are.

* * *

### Introductory notes:

- Starting with [r151](https://github.com/mrdoob/three.js/releases/tag/r151), three.js supports multiple UV maps. This fix should be necessary only when using earlier versions of three.
- This should work on any built-in Material type in three - for as long as it supports at least basic UV mapping (MeshStandardMaterial, MeshPhongMaterial etc.)
- You will **not** need a custom ShaderMaterial. While the vision of writing custom shaders brings warmth to any developer’s heart - they can be hard to maintain and to keep in-sync with core three (ie. lights / shadows / other chunks.)
- Tested on glTF and FBX models. If you’re using OBJ - consider not using OBJ¹ 🙂

* * *

### Assigning UVs to specific textures:

TLDR - [Codepen](https://codepen.io/mjurczyk/pen/PoRrmvy?editors=0010) (lines 5 - 91)

1. Create plenty of UVs (feel free to ignore naming them, since they are renamed automatically either way):

 ![Blender UV maps](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/8/7/871c04f7b6dacf8854e2f0713432df7fa5c21cbb.jpeg)

1. Note - **don’t** use second UV map from the list for your own purposes. `uv2` (ie. `UVMap.001` above) is hardcoded in a few places in built-in three shaders. Using it for your own purposes may work but may also cause weird stuff to happen - use maps from index 3 and further for a worry-less experience (ex. use `UVMap.002` above.)

2. Export the model to GLB / glTF / FBX. While the names stay the same in Blender, UVs will be likely renamed by both [the exporter](https://github.com/KhronosGroup/glTF-Blender-IO/blob/ab046736fa7afd9783177779af9d098c02b6a0f7/addons/io_scene_gltf2/blender/exp/gltf2_blender_extract.py#L279) and three’s [GLTFLoader](https://github.com/mrdoob/three.js/blob/8de7680efd897a088efb443ad136b9379ccd5bef/examples/jsm/loaders/GLTFLoader.js#L2100) in one way or another:

 ![uv-names](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/d/1/d16eb928095e8b310def8e62c1d95b551f3f55ba.png)

1. Load the model in your code and copy-paste the [remapMaterialUVs](https://codepen.io/mjurczyk/pen/PoRrmvy?editors=0010) function (lines 5 - 91, rest is just boilerplate.) This function does not create any new ShaderMaterial type, it just copies and unwraps an existing material - while also adding necessary attributes / uniforms and texture sampling to both the fragment and vertex shaders². That way all the built-in quirks and features of the original material type are still there - including skinning, shadow maps, fog, instancing etc (which would not be the case for ShaderMaterial unless you’d code and add it.)

2. If you’re using GLB / glTF format, you can just reassign the maps right away:

```javascript
gltf.scene.traverse(child => {
  if (!child.material) {
    return;
  }

  remapMaterialUVs(child.material, {
    map: 'texcoord_2',
    emissiveMap: 'texcoord_3'
  });
});

```

1. For FBX - UV maps are renamed to `uv` instead of `texcoord_`, so just be sure to pass a proper prefix and offset (to skip `uv2`):

```javascript
gltf.scene.traverse(child => {
  if (!child.material) {
    return;
  }

  remapMaterialUVs(child.material, {
    map: 'uv3',
    emissiveMap: 'uv4'
  }, {
    uvAttributePrefix: 'uv',
    uvAttributeOffset: 3
  });
});

```

1. You’re done - with little-to-none shader code written on that day 😌

* * *

### Codepen Preview

[https://codepen.io/mjurczyk/pen/PoRrmvy](https://codepen.io/mjurczyk/pen/PoRrmvy)

* * *

### Troubleshooting

> **Expand**
>
> 1. If you’re using another model format or another glTF exporter - your file may have UV maps renamed differently. Export and load your model to three, then just console log the model and find `.children[i].geometry.attributes`:
> 
> ![children.geometry.attributes](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/0/2/02c16641377826a0598ca0308e74ce0762e28fce.png)
> 
> In this case maps `uv3` and `uv4` use `uv<Number>` prefix, so just pass it to the remapping function:
> 
> ```javascript
> remapMaterialUVs(child.material, {
> map: 'uv2',
> emissiveMap: 'uv3'
> }, {
> uvAttributePrefix: 'uv',
> uvAttributeOffset: 3 // NOTE Since the third UV map is called `uv3` - pass 3 as the default offset
> });
> 
> ```
> 
> 1. If you’re getting `THREE is not defined` errors - it’s likely that you’re using module imports instead of global scope. In that case it should be enough to just add:
> 
> ```javascript
> // NOTE Assuming you're importing three like so:
> import * as Three from 'three';
> 
> // NOTE Just add the following line before remapMaterialUVs definition:
> const THREE = Three;
> 
> ```

* * *

¹ More seriously - there’s a small chance it will also work with other model types like OBJ - it just wasn’t tested and UV maps support / renaming seems to be defined on a per-model-format basis.  
² Keep in mind - overriding the mapping must create a new material in three’s materials cache, since built-in materials support only `uv` and `uv2` maps. Consider reusing or limiting the amount of remapped materials because of this.

---

<div class="post-metadata">

### Author: ![valentincognito](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/valentincognito/32/29154_2.png) [@valentincognito](https://discourse.threejs.org/u/valentincognito)
#### Post date: [May 10, 2023, 8:00am UTC](https://discourse.threejs.org/t/using-multiple-uv-maps-per-model-r150-and-earlier/41973/2 "2023-05-10T08:00:20Z")

</div>

I have been looking for this for a while unfortunately I think three.js updated some of the shaders lib code so this solution no longer work.

I really wanted to have a separate set of uv for my roughness and metalness map so I had to scratch my head a little bit to make it work in recent three.js version.

The code is not as clean and fexible as you @mjurczyk but I just wanted to share it in case someone had a similar use case 🙂

```javascript
const remapMaterialUVs = (material = {}) => {    
    material.customProgramCacheKey = () => Math.random()
    material.onBeforeCompile = (shader, context) => {  
      const resolveIncludes = (shader) => {
        const includePattern = /^[\t]*#include +<([\w\d./]+)>/gm
  
        return shader.replace(includePattern, (match, include) => {
          const string = ShaderChunk[include]
          if (!string) return
          return resolveIncludes(string)
        })
      }
      
      shader.vertexShader = resolveIncludes(shader.vertexShader)
      shader.fragmentShader = resolveIncludes(shader.fragmentShader)

      shader.vertexShader = shader.vertexShader.replace(`attribute vec2 uv2;`, `
        attribute vec2 uv2;
        attribute vec2 texcoord_2;
      `)

      shader.vertexShader = shader.vertexShader.replace(`vRoughnessMapUv = ( roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) ).xy;`, `
        vRoughnessMapUv = ( roughnessMapTransform * vec3( texcoord_2, 1 ) ).xy;
      `)
      
      shader.vertexShader = shader.vertexShader.replace(`vMetalnessMapUv = ( metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) ).xy;`, `
        vMetalnessMapUv = ( metalnessMapTransform * vec3( texcoord_2, 1 ) ).xy;
      `)
    }
  }

  useMemo(() => {
    remapMaterialUVs(nodes.Forest.material)
  }, [materials])

```

Later maybe I’ll write a more flexible code with a code sandbox!

---

<div class="post-metadata">

### Author: ![mjurczyk](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mjurczyk/32/10995_2.png) [@mjurczyk](https://discourse.threejs.org/u/mjurczyk)
#### Post date: [May 10, 2023, 8:16am UTC](https://discourse.threejs.org/t/using-multiple-uv-maps-per-model-r150-and-earlier/41973/3 "2023-05-10T08:16:07Z")

</div>

Unless I’m mistaken, this fix is needed only for older versions of three.js. Starting with [r151](https://github.com/mrdoob/three.js/releases/tag/r151) - three should support multiple UV maps ([github](https://github.com/mrdoob/three.js/pull/25721)) ?

---

<div class="post-metadata">

### Author: ![valentincognito](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/valentincognito/32/29154_2.png) [@valentincognito](https://discourse.threejs.org/u/valentincognito)
#### Post date: [May 10, 2023, 8:52am UTC](https://discourse.threejs.org/t/using-multiple-uv-maps-per-model-r150-and-earlier/41973/4 "2023-05-10T08:52:41Z")

</div>

Wow I didn’t see that… Dammit I should have upgraded three.js before spending time on this one 😅

---

<div class="post-metadata">

### Author: ![valentincognito](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/valentincognito/32/29154_2.png) [@valentincognito](https://discourse.threejs.org/u/valentincognito)
#### Post date: [May 11, 2023, 1:40am UTC](https://discourse.threejs.org/t/using-multiple-uv-maps-per-model-r150-and-earlier/41973/5 "2023-05-11T01:40:13Z")

</div>

After a bit of research last night I am not sure how well integrated the multiple UV feature is yet. I am only testing with react three fiber at the moment but seems to me that the new channel properties only works for uv1 and 2.

I was able with blender to export a glb with the roughness channel mapped to uv2 which is already a win but if I try uv3 or uv4 in threejs it defaults back to uv1. Not sure if it’s blender gltf exporter, blender itself, react drei gltf loader or just three.js but personally I still couldnt achieve using 4 set of uvs unless I change the shader code myself.

---

<div class="post-metadata">

### Author: ![donmccurdy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/donmccurdy/32/33209_2.png) [@donmccurdy](https://discourse.threejs.org/u/donmccurdy)
#### Post date: [May 11, 2023, 2:45am UTC](https://discourse.threejs.org/t/using-multiple-uv-maps-per-model-r150-and-earlier/41973/6 "2023-05-11T02:45:57Z")

</div>

Which three.js version are you using?

Three.js has supported 2 UV sets for a while, but had major restrictions on how they could be used. You couldn’t pick and choose which texture used which UVs. Beginning in **r151** you are still limited to 2 UVs, but you can assign any texture to either of them. Beginning in **r152** three.js supports up to 4 UVs, and you can still assign any texture to any of them.

I’m not sure which releases of Drei or three-stdlib correspond to those three.js releases, though. Blender and Blender’s glTF exporter should have no trouble with any of this.

---

<div class="post-metadata">

### Author: ![valentincognito](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/valentincognito/32/29154_2.png) [@valentincognito](https://discourse.threejs.org/u/valentincognito)
#### Post date: [May 11, 2023, 3:17am UTC](https://discourse.threejs.org/t/using-multiple-uv-maps-per-model-r150-and-earlier/41973/7 "2023-05-11T03:17:08Z")

</div>

Oh I see!! My bad then… I didn’t see only r152 brought the 4 UVs feature!  
Sorry I got confused 😅

---

<div class="post-metadata">

### Author: ![Lokesh\_Shanmuga](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/lokesh_shanmuga/32/50105_2.png) [@Lokesh\_Shanmuga](https://discourse.threejs.org/u/Lokesh_Shanmuga)
#### Post date: [August 14, 2024, 2:35pm UTC](https://discourse.threejs.org/t/using-multiple-uv-maps-per-model-r150-and-earlier/41973/8 "2024-08-14T14:35:20Z")

</div>

i am not able to access the multiple UV maps exported from blender as there is no names given to it by the GLTF exporter. the issue is detailed in the link below . how do i target a particular UV for texture application when all UVs are named “”?  
my version of threejs is r155

> [@How to access UV maps after gltf exports from blender in threejs](https://discourse.threejs.org/t/how-to-access-uv-maps-after-gltf-exports-from-blender-in-threejs/69255):
>
> I am trying to access UV3 (uvmap) of mesh to apply a particular texture, however its not being named anything by my gltf exporter. How do I access these UV maps or change their relative name? also the gltf exporter in blender doesnt assign names to the UV maps. is this normal? i tried to access with the name uv3 itself but apparently that’s not possible. I am trying to achieve application of textures with different UV on the same mesh.
