Three.js loading object from glb file looks washed out

enter image description here

That’s how my model is looking on https://gltf-viewer.donmccurdy.com/.

And How it’s looking in my code?
enter image description here

I know I should share the code too and will but before that, I would like to know what might be going wrong.

  1. Is this a problem with lighting?
  2. Is this a problem of shadow? (I don’t think so)
  3. Is this related to the opacity of the object? (I don’t think so because it shows opacity: 1 in code)
  4. Is this related to the material of the object?

Here are some snippets of code. I’m using Angular.

HTML file:

<div class="canvas-container">
  <canvas class="webgl" linear flat></canvas>
</div>

JavaScript:

    const canvas: HTMLCanvasElement = document.querySelector('canvas.webgl');
    const scene = new THREE.Scene();
    const sizes = {
      width: window.innerWidth,
      height: window.innerHeight
    };
    const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100);
    camera.position.set(10, 35, 20);
    camera.layers.enableAll();
    camera.layers.toggle(1);
    this.loadTexture(scene);
    this.loadModels(scene);
const renderer = new THREE.WebGLRenderer({
      canvas,
      antialias: true
    });
    renderer.physicallyCorrectLights = true;
    renderer.outputEncoding = THREE.sRGBEncoding;
    renderer.textureEncoding = THREE.sRGBEncoding;
    renderer.toneMappingExposure = 0.2;
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    renderer.setSize(sizes.width, sizes.height);
    renderer.setPixelRatio(window.devicePixelRatio);
    const canvas_container = document.querySelector('.canvas-container');
    const controls = new OrbitControls(camera, canvas_container);
    controls.maxDistance = 50;

    const tick = () => {
      window.requestAnimationFrame(tick);
      controls.update();
      renderer.render(scene, camera);
    };

    tick();

LoadTexture Fn:

  loadTexture(scene) {
    const cubeTextureLoader = new THREE.CubeTextureLoader();
    const debugObject: any = {};
    const environmentMap = cubeTextureLoader.load([
      '/assets/3d.js/textures/environmentMaps/4/px.jpg',
      '/assets/3d.js/textures/environmentMaps/4/nx.jpg',
      '/assets/3d.js/textures/environmentMaps/4/py.jpg',
      '/assets/3d.js/textures/environmentMaps/4/ny.jpg',
      '/assets/3d.js/textures/environmentMaps/4/pz.jpg',
      '/assets/3d.js/textures/environmentMaps/4/nz.jpg'
    ]);
    environmentMap.encoding = THREE.sRGBEncoding;
    scene.background = environmentMap;
    scene.environment = environmentMap;
    debugObject.envMapIntensity = 0.001;
  }

LoadModels Fn:

loadModels(scene) {
    const gltfLoader = new GLTFLoader();
    const updateAllMaterials = () => {
      scene.traverse(child => {
        if (child instanceof THREE.Mesh && child.material instanceof THREE.MeshStandardMaterial) {
          child.material.needsUpdate = true;
          child.castShadow = true;
          child.receiveShadow = true;
          console.log(child.material);
        }
      });
    };

    gltfLoader.load('/assets/3d.js/models/Tank.glb', gltf => {
      console.log(gltf);
      gltf.scene.scale.set(1, 1, 1);
      gltf.scene.position.set(-4, 0, 0);
      gltf.scene.rotation.y = Math.PI * 0.5;
      scene.add(gltf.scene);

      updateAllMaterials();
    });
  }

I’ve commented all other lines which I think are not necessary.

The renderer has no property textureEncoding.

Apart from that, I suggest you load a HDR instead of a LDR environment map via e.g. RGBELoader or HDRCubeTextureLoader similar to the official examples. Use webgl_loader_gltf as an example.

If the HDRI is the only light source in your scene, you can turn of shadows (since an environment map does not support shadow mapping).

I’ve not much idea of what you’re talking here.

Designer gave me a design in glb file, I googled how to load glb file on web apps and it worked fine earlier, everything good.

But the file size was large. As the files contains multiple same assets so i thought why not just get one asset file and load the same multiple times, that would save some bandwidth for the client right? Turns out file size got reduced but now it has this problem. I didn’t change much of the code.

So, if you can modify the code a little bit, that would be helpful.

I tried to change CubeTextureLoader to RGBELoader, but it throws below error:
Error: export ‘RGBELoader’ (imported as ‘THREE’) was not found in ‘three’

Any idea?

any update yet plz? @Mugen87

You have to import RGBELoader separately like demonstrated in the above linked example.

It’s showing just black. I tried both HDRCubeTextureLoader and RGBELoader.

can you provide me with your model?

Tank.glb (189.3 KB)

Here is the glb file for tank asset.

Unfortunately, screenshots are not sufficient to investigate your issue. It’s actually best if you share your code as a live example.

should i share code on stackblitz?

I’m not familiar with stackblitz, sorry. If it’s comparable to jsfiddle, codepen or codesandbox then fine.


The model looks fine, your scene may not be using lighting or hdr causing your model to look black.
use of hdr

new RGBELoader()
        .setPath('textures/equirectangular/')
        .load('royal_esplanade_1k.hdr', function (texture) {
     texture.mapping = THREE.EquirectangularReflectionMapping
     scene.environment = texture
})

@Qiumeng12 what is this equirectangular texture?

you can look at this example.
https://threejs.org/examples/?q=gltf#webgl_loader_gltf

is it required to use hdr file for texture instead of png or jpg? @Qiumeng12

If using jpg or png format.

const pmremGenerator = new THREE.PMREMGenerator(renderer);
pmremGenerator.compileEquirectangularShader();
new THREE.TextureLoader().load('textures/equirectangular.png', function (texture) {
        texture.encoding = THREE.sRGBEncoding;
        const pngCubeRenderTarget = pmremGenerator.fromEquirectangular(texture);
        scene.environment = pngCubeRenderTarget.texture;
})

stackblitz doesn’t support static assets.

The only thing you can do right now is to copy the code locally and use this glb file.

Tank.glb (189.3 KB)

Plz if you can help, i’m really frustrated :frowning:

You are using it wrong, you should use it like this

const pmremGenerator = new THREE.PMREMGenerator( renderer );
pmremGenerator.compileCubemapShader();
const Target = pmremGenerator.fromCubemap( environmentMap)
scene.environment = Target.texture

Do you mean like this?

loadTexture(scene, renderer) {
    const cubeTextureLoader = new RGBELoader();
    const debugObject: any = {};
    const environmentMap = cubeTextureLoader.load([
      '/assets/3d.js/textures/environmentMaps/4/px.jpg',
      '/assets/3d.js/textures/environmentMaps/4/nx.jpg',
      '/assets/3d.js/textures/environmentMaps/4/py.jpg',
      '/assets/3d.js/textures/environmentMaps/4/ny.jpg',
      '/assets/3d.js/textures/environmentMaps/4/pz.jpg',
      '/assets/3d.js/textures/environmentMaps/4/nz.jpg'
    ]);
    environmentMap.encoding = THREE.sRGBEncoding;
    scene.background = environmentMap;
    // scene.environment = environmentMap;
    debugObject.envMapIntensity = 0.001;

    const pmremGenerator = new THREE.PMREMGenerator(renderer);
    pmremGenerator.compileCubemapShader();
    const Target = pmremGenerator.fromCubemap(environmentMap);
    scene.environment = Target.texture;
  }

It’s throwing below error

you can try.