Switching from .json to .gltf objects

I am reworking an older Three.js file and switching my .json object over to .gltf and trying to wrap my head around some differences I am seeing.

When I load a freshly exported GLTF from blender (not exporting the scene just a single selected object), it comes in as a full hierarchy: scene>group>sub-object-parts, while the exported .json file was just a single object.

Is that normal? Is there any simple way to collapse that back into just a single object with multiple materials rather than multiple parts in a group?

Perhaps I’m understanding the usefulness of the GLTF format incorrectly and trying to use it for something it’s not intended.

I’m moving away from json because of issues with normals being loaded incorrectly when exporting with blendshapes from Blender


Are you using the latest version of three.js? It will keep multi-material meshes together, at least in some cases. But yes, the result of GLTFLoader will always be (at least) Scene > Mesh. Depending on the model and the exporter, there could be other meshes or subgroups. That’s just a difference in the format representation, you can certainly throw away the scene and just use scene.children[0]. If it’s a skinned mesh be sure to include the armature as well.

Thanks, I was using r92, switched to dev r93 and had the same results. Still not seeing multi-materials, but it’s good to understand how GLTF is supposed to work.

Okay, so I am also in the process of switching to gltf2 as well, and for the most part this is going great. However, I’ve encountered something unexpected/strange as I am playing around with embedded textures. My exported .glb from Blender (with embedded glTF Metallic Roughness node textures) looks fantastic in three, but when I try to swap out a map in three, everything goes to hell. Like if I swap out .map it is way too bright, and if I swap out .roughness it’s like it is coated in KY Jelly™ (zero roughness)… So what gives? Is there something going on internally with gltf/glb that is changing the gamma of embedded textures, and thus anything swapped out is not at the right gamma?

var loader = new THREE.GLTFLoader();
loader.load('thing.glb', function(gltf){
	var txloader = new THREE.TextureLoader();
	var roughmap = txloader.load('thing_roughness.jpg', function(){
		var metalmap = txloader.load('thing_metalness.jpg', function(){
				gltf.scene.traverse( function ( child ) {
				if ( child.isMesh ) {
					child.material.roughness = roughmap;
					//child.material.metalness = metalmap;
					child.material.envMap = envMap;							
			scene.add( gltf.scene );	

Please advise. Thanks!

Two things GLTFLoader does automatically, which you’ll need to include if loading textures separately with TextureLoader:

  1. Set texture.flipY = false.
  2. For color textures (map, envMap, emissiveMap) set texture.encoding = THREE.sRGBEncoding.

Oh, hi Don! Hey, thanks for swinging by! So the flipY is really nice to know, since I was flipping stuff manually in PS… And the THREE.sRGBEncoding is making regular .map textures look correct… but I can’t get roughness (haven’t messed with any others) to be anything other than slippery as a wet seal! I’ve tried bringing in new roughness as grayscale, rgb, png, jpg…

Roughness won’t need THREE.sRGBEncoding, the default (THREE.LinearEncoding) is fine because it’s not color data. I don’t think there’s anything else special there… Roughness comes from the “green” channel of the texture, so it can be packed with occlusion and metalness. What does the roughness texture look like? You may also want to confirm that material.roughness in three.js (or roughnessFactor in the glTF) is not zero, since that would cancel out any texture you apply.

Thanks again Don. I will look into all of that later today… In the meantime, is all that whitespace in .glb necessary? :-/ Wouldn’t these be reduced significantly if they could be minified during export?..

Do you mean whitespace in the JSON portion at the front of the .glb? It’s not necessary — the Strip delimiters option in the Blender exporter will disable it. Probably it should be off by default with GLB export, I thought it was already. But usually the textures and geometry are the large majority of your file, and those will be binary without any JSON-related whitespace.

EDIT: No longer need help with ORM.

Holy smokes! Using occlusionRoughnessMetallic map is absolutely incredible! I am stunned. Wow, does this make life easier!

1 Like

Okay, now, to be clear, whenever any mesh is loaded into three, be it gltf, glb, obj, fbx, json, etc, etc, they all become THREE.Mesh, right?.. In other words, regardless of format, they all behave and are essentially the same once we have them loaded and either use: var mesh = new THREE.Mesh(geometry); OR traverse through gltf.scene and use: if ( child.isMesh ) { blah blah blah );… correct?

In general, yes, correct.
Some loaders return a buffer geometry, not a mesh or a scene. But yes, all of them are of the types that in the framework :slight_smile: