Three.js: GLTFLoader does not support texCoord fields

Created on 9 Nov 2017  Â·  17Comments  Â·  Source: mrdoob/three.js

Description of the problem

Today, the Three.js engine does not allow the use of arbitrary UV coordinate sets for textures. Instead, they're hardcoded to channel 0 for all textures, except for lightMap which uses channel 1. As a result, the texCoord property of glTF textureInfo objects are ignored. Thus, the glTF loader is not compliant to the spec.

Once the engine supports UV channels better, this issue is a reminder to update the loader too.

Three.js version
  • [x] Dev
  • [ ] r87
  • [ ] ...
Enhancement Loaders

Most helpful comment

I am currently running into this issue with our glTF exports from Clara.io. We should make Three.JS up to specification and it is also just an important feature for Three.JS.

All 17 comments

Hi @stevenvergenz — this requirement that most textures use uv1, except aoMap and lightMap which use uv2, is shared across all of three.js. It isn't possible to fix it just for a single loader without using a bunch of ShaderMaterials, which we don't want to do.

I'm not sure if there is interest/support for allowing per-texture UV sets in three.js. See: https://github.com/mrdoob/three.js/pull/8278

^Which is not to say I'm opposed at all to supporting per-texture UV sets in three.js... I just don't know what the concerns/obstacles were with that previous PR.

GLTF supports per-map uvs?

Yes, each map can specify a UV set. But also:

Client implementations must support at least two UV texture coordinate sets, one vertex color, and one joints/weights set.

So, there is an expectation that for best compatibility models should not use more than 2 UV sets.

Just to chime in on this, Adobe Dimension's glTF exporter (which I'm currently writing) requires the second UV channel to display decals correctly.
We'll also be supporting separate transforms per texture in the near future which will also be problematic for three.js. Time for a refactor of how three.js materials use textures :) #8278

@MiiBond Good to know. Thanks!

@MiiBond could you say more about how you might use separate transforms per texture? A diffuse and occlusion map with different transforms, for example?

If someone were to rebase and resolve conflicts on #8278, are there any changes that would make us more comfortable with it?

I like the fact that #8278 is so flexible and has a clear API, but if it seems like too much of a change, some ideas...

A. Give each material two (2) slots, rather than per-map slots.

// .map, .normalMap, .emissiveMap, ...
material.mapSlot = new THREE.MapSlot( channel1, transform1 );

// .aoMap, .lightMap, ...
material.mapSlot2 = new THREE.MapSlot( channel2, transform2 );

B. Put .channel on the Texture object, similar to .rotation:

material.map.channel = 1;

I don't like (b) as much, unless we can fix the issue that texture.clone() causes additional texture uploads.

@donmccurdy The most common use case would be for something like a detail map, typically a highly-tiled normal map to add detail at a far smaller scale than the rest of the map.

I would prefer a way to explicitly assign a UV channel and transform per map. Having slots locked to specific maps seems unnecessarily restrictive. In the case of Adobe Dimension, we will have separate transforms for each map. And, upon export to glTF, I'll be using UV1 to apply decals while still using UV0 for the underlying material (e.g. normal map).

When choosing a "channel" (i.e. UV0 or UV1), we might want to add options for common generated UV's. e.g. equirectangular projection, etc.

I started a disussion about this topic including some test-files here:
https://github.com/KhronosGroup/glTF-Blender-Exporter/issues/194

Actually, right now it seems that the ao-map just gets ignored when parsed by three-js gltf-plugin.

Actually, right now it seems that the ao-map just gets ignored when parsed by three-js gltf-plugin.

Cross-posting from the other thread: "three.js is very particular about how multiple UV sets may be used. Most textures MUST use the first UV set, and ambient occlusion MUST use the second UV set. If ambient occlusion is present and only 1 set of UVs are available, a second set is automatically created at load time."

I would prefer a way to explicitly assign a UV channel and transform per map. Having slots locked to specific maps seems unnecessarily restrictive. In the case of Adobe Dimension, we will have separate transforms for each map. And, upon export to glTF, I'll be using UV1 to apply decals while still using UV0 for the underlying material (e.g. normal map).

If you're creating a new UV set for decals, why also use a per-map transform, as opposed to baking the transform into those UVs?

If you're creating a new UV set for decals, why also use a per-map transform, as opposed to baking the transform into those UVs?

We don't want separate UV channels for each map. All maps share UV0 and use transforms to tile (or rotate/translate) differently. The decals are a special case where Dimension is doing an intelligent application (i.e. a non-affine projection) of the decal on top of the material and generating its own UV's to avoid stretching, repetition, etc.

Thank you very much for your explanations.

How should the case of model-scale normal-maps be handled in the occasion that a tiling texture-map has it‘s own normals that should tile, too?

Ok, thanks for clarifying @MiiBond.

@goetzmoritz three.js does not support this in default materials, sorry. You would need to use a custom ShaderMaterial.

I am currently running into this issue with our glTF exports from Clara.io. We should make Three.JS up to specification and it is also just an important feature for Three.JS.

It's conceptually trivial to solve this problem with existing Material functionality.

http://dusanbosnjak.com/test/webGL/three-materials-extended/webgl_materials_extended_multiple_uvs_props.html

With onBeforeCompile each "built-in" material can be selectively modified. For this use case, there are several texture2D( <mapName>, vUv) calls. One can add additional inputs for each map _(i added two vec2 but could be the same pattern as the new texture transforms)_ and override vUv. One can safely ignore the remaining 1000+ lines of the shader.

This is working nicely with NodeMaterial. Still constrained to two UV slots for now, but the textures can be assigned arbitrarily.

| MeshStandardMaterial | StandardNodeMaterial |
|--|--|
| screen shot 2018-07-14 at 11 10 37 pm | screen shot 2018-07-14 at 11 31 17 pm |

model from sketchfab and modified GLTFLoader code

Was this page helpful?
0 / 5 - 0 ratings