Broken lighting at normal map seams in GLTF


I’m creating a sphere from a cube in blender to use in three.js and all textures are fine except as soon as I include my normal map to the export the lighting breaks along the seams in threejs.


Is there something I need to account for when exporting or baking in blender or loading in threejs?

can I ask why you are using a cube to create a sphere in blender? ( *if evenly spaced vertices are what you are looking for I suggest using an icosahedron in threejs and applying all the textures on that)

Previously I used a uv-sphere but I had problems with texture on the poles with it. After researching I’ve found the solution to be subdividing and casting a cube into a sphere.
It also results in a much smoother surface than an icosphere with the same amount of subdivision unless I cast it into a sphere too. Would you recommend doing that?

I’ve just tried out the an icosphere and the same problem remains, even if the seams look different.

I would suggest trying to get the mesh and textures appearing as you want it in Blender, first. There is no perfect way (without distortion or seams) to map a rectangular texture onto a sphere. Different UV maps will have different tradeoffs, and Blender is a flexible tool for doing UV maps.

Probably it would also be possible to project a cubemap onto a sphere. I expect that would minimize seams, but you’d need to reproject the texture first.

That’s exactly what I’m already doing: Creating the mesh with textures as I want and then exporting it to a glb file including all textures. Loading into threejs everything looks great too until I include the normal map in the glb. Every other texture works fine for me.
For me it looks like threejs isn’t able to tell how one seam of the normal map transitions to another and I need to configure it somehow differently or the information is getting lost during export, since blender hasn’t this problem.

Just to confirm, you are also including the normal map in Blender, it looks correct in Blender, but once the GLB (containing the normal map) is exported, the result does not look right in three.js?

If so, I would suggest enabling the Geometry → Mesh → Tangents option in the export settings, and see if that helps:

Screen Shot 2022-10-11 at 11.05.07 AM

I have done this already aswell. I’ve tried all checkboxes there and all together.
But I’ve found out that if I’m connecting the nodes I’m using to generate the normal map directly to “normal”, then I’m getting the same issue blender.

Usually I’m connecting these nodes to “displacement”, then bake normals, then I’m able to disconnect displacement and use the baked normal map to connect to “normal” through a “normal-node”. I could guess that this procedure I’m doing is totally wrong and I have to do it an other way. What do you think?

If your baked normal map is connected to “normal” as shown in this section of the Blender manual, and looks correct in Blender that way, then I would have expected it to also work if exported to glTF:

One other idea, do you have more than one UV set on the mesh? That might be a problem if so, three.js can only use the 1st UV set for normal maps. Deleting any extra/unused UV sets might also be worth a try.

My arrangement looks like this.

And the UV Maps section inside Object Data Properties shows only a single entry.

That looks correct. are you able to share the .blend?

Sure, here you can get it.
Do you need the baked textures?

Do you have a version that still looks correct in Blender, with a texture connected to the normal map slot? There’s a lot going into that slot here…

I’m not sure it’s possible to bake that node graph down correctly into a texture.
That should contain a blend with all the textures referenced by the image texture nodes.
Or do you need the generated textures to be attached?

Ok, I tested the following:

  1. Disconnected the emissive node (since it was still nodes)
  2. Export to GLB

The result looks OK in a viewer, I couldn’t find any seams here:

If you’re adding the normal map manually in code, rather than embedding it in the GLB, you’ll need to add texture.flipY=false as well. But otherwise it looks like this should work, have I missed anything?

You mean you disconnected the emission strength?

I’ve tried to reproduce your result by disconnecting emission and emission strength and loading the exported glb into glTF Viewer.
So only Base Color, Roughness and Normal are connected and I am still getting the same result.

I’m not adding anything manually. Just embedding directly in the GLB.
The question is, what am I missing that you haven’t?

So we’re starting from this state in the .blend file and then exporting, right? Only image textures wired up?

I don’t see any seams when I open it if so. One very weird thing I do see is that the extensions of the images are incorrect, like Blender has mis-identified which are PNG and which are JPEG images. I wonder if that needs to be given in their names on the Image Texture nodes?

I fixed that manually, here’s the result:
ownPlanetExt-v1.glb (5.4 MB)

(reduced to 4K textures to stay under the upload limit)

OMG, that is exactly the fix!
I either to turn all images I exported into jpeg or set the images export to “automatic” while exporting the glb.
So yeah, thank you very much!