Why your GLB looks different in every viewer
The same file, three viewers, three appearances. Almost always one of four things: colour space, an extension the viewer does not implement, the lighting environment, or normals that were never in the file.
You export a GLB, open it in three viewers, and get three different pictures. glTF is a tightly written specification, so this is frustrating in a specific way: it feels like somebody must be wrong. Usually nobody is. The differences come from four places, and they are easy to tell apart once you know what to look at.
1. Colour space
glTF is explicit about which textures are sRGB and which are linear. Base colour and emissive are sRGB, because they are colours a human picked. Normal, metallic-roughness, occlusion and anything else numeric are linear, because they are data. A viewer that uploads a normal map as sRGB gets lighting that is subtly wrong everywhere, and a viewer that treats base colour as linear produces a model that looks washed out or muddy depending on the direction of the mistake.
How to spot it: the whole model reads too dark or too pale, uniformly, and the difference is largest in the midtones. If one viewer is right and one is wrong, the wrong one is usually the older one.
2. An extension the viewer does not implement
The base glTF material is metallic-roughness and nothing else. Everything past that is an extension: clearcoat, transmission, sheen, iridescence, specular, anisotropy, volume, emissive strength, unlit. A viewer that does not implement one draws the base material underneath and carries on, which is correct behaviour and looks like a bug.
How to spot it: the difference is confined to specific materials rather than the whole model. Glass renders opaque, car paint loses its flake, fabric loses its rim. Open the JSON and read extensionsUsed; that list is what the file is asking for.
Worth knowing: extensions in extensionsRequired must be refused if unsupported, but ones in extensionsUsed may be ignored. So a file can render differently in two conforming viewers by design.
3. The lighting environment
This is the biggest one and it is not a bug at all. A glTF file usually contains no lights. A PBR material is a description of how a surface responds to light, so with no light in the file, every viewer supplies its own environment. A studio HDRI, a neutral grey room and a sunny outdoor probe will produce three completely different images of an identical, correct file.
How to spot it: the shapes and textures match, the mood does not. Metals are the tell: a metal surface is almost entirely a mirror of its environment, so it changes the most.
There is no fix, because there is nothing broken. If the appearance has to be identical everywhere, the environment has to be part of the deliverable, or the materials have to be unlit.
4. Normals that were never in the file
Vertex normals are optional. When they are absent, the specification says to compute flat, per-face normals. Plenty of viewers instead compute smooth ones, because it usually looks better. So the same file arrives faceted in one and smooth in another, and both have a defensible reason.
How to spot it: the silhouette matches exactly but the shading is blocky in one and rounded in the other. Same story for tangents: without them, normal mapping depends on how the viewer generates them.
The fix is to export normals. Any exporter can, and a file without them is asking each reader to guess.
Narrowing it down
A quick order of operations when a file looks wrong:
- Open it in the viewer here and read the material list. If a map you expect is missing, the problem is the export, not the viewer.
- Switch to normals shading. If the surface is faceted, the file has no vertex normals.
- Check
extensionsUsed. Anything past metallic-roughness is a candidate. - Compare against a known-good asset in the same viewer. If that renders correctly, the viewer is fine and the file is the variable.
And one thing that is genuinely a bug rather than a difference: if the file came out of a FBX or .max pipeline, its materials were probably Phong, Lambert or V-Ray rather than metallic-roughness, and something in the chain had to invent a mapping. Specular-glossiness converted to metallic-roughness is approximate by nature, and highly specular non-metals are where it shows most.
If you want to see what actually survived a conversion, every converter page here states which of geometry, materials, textures, animation and hierarchy the target format can hold, before you run it.
Sources
Primary references for the claims above. Where a specification exists, it is cited in preference to anybody's summary of it.
- glTF 2.0 specification, Khronos GroupWhich textures are sRGB, which are linear, and what a viewer is required to do with them.
- glTF extension registry, Khronos GroupThe ratified and vendor extensions a file may lean on and a viewer may ignore.
- three.js, GitHubThe renderer used here, and a readable reference implementation of the material model.
- glTF Sample Assets, Khronos GroupThe models to test a viewer against before blaming your own file.