Help with aoMap of GLTF/GLB model

Hi there,

I’m having a hard time getting an aoMap working in three.js…

I have a glb asset with an aomap on the red channel or something. When I bring it into to babylon viewer, I can see the ao just fine, but it wont show up in the three.js viewer or my project. I think this has something to do with a second set of uvs, but I cant find a resource that involves doing that on top of using the gltf loader… I really dont know what to do here. Any response would be greatly appreciated!

Here is my code (I’m using a html-canvas as the texture)

And I get the model’s geometry and diffuse texture (all white) as desired, but the aomap isnt showing…

And here is my application using three.js

working (before I tried to using an aoMap and just had detail in the diffuse):

not working (once I changed the diffuse to all white and added the detail in the aoMap):

three.js requires that the AO Map use the 2nd UV set, and other textures use the first. If you have only one set of UVs GLTFLoader can automatically duplicate the first to support the AO map. If you have 2+ UV sets used differently than three.js requires, loading will fail.

Could you share the model, or how it is configured?


Thank you for the response! Really appreciate it. And I’ve found a number of resources saying something like that (second set of uvs), but I just don’t know exactly how to apply that…

Here is a repo with a blank white diffuse, and an aoMap in the gltf model (I think):

You can find the model in assets/models/af1_ao.gltf


I also got the three.js viewer to work. I just needed to add an ambient light…

Do you mean the model looks correct in the threejs viewer, but that you can’t see the aoMap in your own threejs project? Or you’ve got it working? Ambient occlusion only affects ambient lights and environment maps, so you’ll need at least one of those, yes.:slight_smile:

This is a really, really weird way of doing things. As a 3d artist by trade, the standard is to have

UV channel 0 for your objects’ textures such as diffuse, roughness, normal, ambient occlusion etc, and
UV channel 1 for your lightmap UVs, which are usually a combination of ALL scene objects into one 0-1 UV space.

So basically if I want to use the standard textures - including ambient occlusion - associated with modern workflows (uv0) AS WELL as the standard lightmaps which contribute scene-wide GI and occlusion (uv1), it will always fail?

I assume this is to compensate for three’s simplified lightmap model which doesn’t include scene-wide occlusion (both Unity and Unreal optionally bake scene-wide AO into lightmaps)?

It’s not that unusual to put AO in a second channel. You might not be baking all objects’ AO into one UV space, but even as a per-object map, the ideal UV layout for a diffuse texture is not necessarily the same as the ideal UV layout for AO information. I believe the original reasoning here was influenced by what we understood to be common practice in UE3-4. We needed to support models where AO required a second UV set from the basic set, and that was higher priority than supporting aoMap and lightMap with unrelated UVs.

That said, you’re correct: you can’t use different UVs for aoMap and lightMap as a result of this design. We have been looking into ways to allow users to change the UV set used for a particular texture, but I don’t think we would simply change the hardcoded UV set for aoMap at this point.

1 Like

Hey don, thank you for the reply. I must say I’m a little baffled, per-object AO maps (working together with the other maps on the same UV layout) are the absolute standard. Of course there might be edge cases where you’d maybe want to use an alternate layout specifically for AO, but I can’t really think of any situation where just AO would need more detail on certain parts of the mesh. Certainly not a UE norm afaik. Maybe the misconception here stems from Unreal/Unity combining their scene-wide AO with their scene-wide lightmapping and the resulting texture naturally needing a second UV channel?

So even if we forgo using a prebaked lightmap, using any old store-bought asset’s AO map automatically bumps up our uv channels to +1 - does this not incur rendering penalties for adding extra tex-verts?

EDIT: Even the GLTF sample models follow this norm:

Oops, I think I’m mistaken on the UE4 comment — that was the original reasoning for having AO use the ‘red’ channel of the texture, not for the selection of uv2. The addition of a vertex attribute here has never been identified as a relevant performance cost. There’s some extra memory footprint, but not much compared to the texture itself.

One example use case for a second UV set would be something like, supporting per-vertex AO. In that case, as I show on this glTF example, your AO texture can be a simple 256x1 gradient and you’d use the UVs to index into that gradient. It works well, and cheaply, for something like a voxel model.

Perhaps a more common example would be something like this discussion, where AO information is concentrated around areas that would normally be seams in your diffuse and other maps. But I’m not a 3D artist, perhaps I’m misunderstanding that. I’ve also seen cases where users want to tile their diffuse/metal/rough maps but not tile AO, and (because UV slots are associated with UV transforms) the separate UV slot helps with this too.

tl;dr — I don’t see us taking away the ability to use separate UVs for aoMap. It’s unfortunate that this causes a conflict with lightMap, although that conflict has (so far) been fairly rare. There is a fair bit of interest in more flexibility in general here, and being able to allocate UV slots and UV transforms in more custom ways. I think an eventual solution for that would also cover what you’re referring to here.

1 Like

Thanks again!

Yeah I guess once the standard has been set hereabouts, at best it’s a nuisance to change things, at worst it breaks stuff. I guess I had to vent my surprise once I actually understood what was happening to aoMaps, haha.

Mapping AO separately for objects with tiled diff/normal etc. does happen occasionally I guess. Though even here you’d be giving up the opportunity to have AO on said tiled textures. Usually one would trust the in-engine light baker and call it a day.

I don’t think I’ve ever come across anybody hiding texture seams with AO maps.

So to support the standard way of things, would I have to poke around in GLTFLoader only, or in the material definitions as well? (ie stop three from mapping AO to uv1)

Thanks again!

1 Like

I don’t think I’ve ever come across anybody hiding texture seams with AO maps.

Ah, I don’t really mean using AO to hide diffuse texture seams, but that you may want your AO texture seams in different places than diffuse texture seams to get smooth AO in corners of the model.

So to support the standard way of things, would I have to poke around in GLTFLoader only, or in the material definitions as well? (ie stop three from mapping AO to uv1)

I don’t think you’ll need to change anything in GLTFLoader – if your model has two UV sets it will leave them alone. The texture mapping of AO -> uv2 is in src/renderers/shaders/ShaderChunk/aomap_fragment.glsl.js, you’d need to patch that I think. The NodeMaterial system supports a lot more flexibility here, but loading a glTF model and converting its materials to NodeMaterial instances is a bit of extra work.


Lovely, I’ll take a look. Thanks so much for your time!