Loaded .gltf/.glb model always shows "Dark"

I’m trying to implement a simple 3D model viewer. I can load a tshirt model
tshirt.gltf (6.0 KB) in either .gltf/glb successfully (when I say “successfully” I mean that the model actually loads and does not throw erros :sweat_smile:), however, I have this issue that, no matter how many solutions to “common issues” I try, the model always displays in a “black” color, however when loading it here https://gltf-viewer.donmccurdy.com/ it displays perfectly :melting_face:

Here’s how it currently loads:

…even if I change the mesh material’s color (notice how it displays in a “darkened” red):

Here’s the relevant code that I’ve used to implement the viewer:

// CONTEXT: I'm encapsulating the implementation in a single class that can be instanced however and whenever I want
constructor() {
        this.__scene = new THREE.Scene();
        this.__camera = new THREE.PerspectiveCamera(5, window.innerWidth / window.innerHeight, 1, 10000);
        this.__camera.position.set(0, 20, 100);
        this.__renderer = new THREE.WebGLRenderer();
        this.__renderer.setSize(window.innerWidth / 2, window.innerHeight / 2);
        this.__scene.background = new THREE.Color('#FFFFFF');
        // NOTE: how I'm adding light
        this.__scene.add(new THREE.HemisphereLight(0xffffff, 0xffffff, 3.0));
        this.__loaders = new Map();
        this.__controls = new OrbitControls(this.__camera, this.__renderer.domElement);
        // The currently only-instanced loader (works)
        this.__loaders.set('GLTF', new GLTFLoader());

        // Done as recommended on the official threejs website (removing it does not fix the issue)
        const dracoLoader = new DRACOLoader();
        dracoLoader.setDecoderPath('/examples/jsm/libs/draco/');
        (<GLTFLoader>this.__loaders.get('GLTF')).setDRACOLoader(dracoLoader);
}

… and here’s the loader code:

    public loadModel(modelURL: string) {

        // removing the only child of the scene (the previously-loaded model)
        const sceneChild = this.__scene.children[0];
        if (sceneChild) {
            // utility method to remove the object and its children the most optimal way possible
            dispose3DObject(sceneChild);
        }

        // getting the instance of the GLTF loader that's automatically created when instancing the "ModelViewer" class
        const modelLoader = <GLTFLoader>this.__loaders.get('GLTF');

        // Load a GLTF resource
        modelLoader.load(
            // resource URL
            modelURL,
            // called when the resource is loaded
            (glResource) => {

                this.__scene.add(glResource.scene);

               // this is another "common solution" that I tried, It kinda helped, since without it, the model is just pitch black, not a single shadow is cast on it 
                this.__scene.environment = new THREE.PMREMGenerator(this.__renderer).fromScene(this.__scene, 0.3).texture
            },
            // called while loading is progressing
            (xhr) => {
                const loadPercentage = ((xhr.loaded / xhr.total) * 100);
                console.log(loadPercentage )
            },
            // called when loading has errors
            function (error: object) {
                throw new StandardError({
                    message: `An error ocurred when trying to load model: ${modelURL}`,
                    info: error
                });
            }
        );
    }

at this point, I don’t even have an idea of what I could be missing

It looks like the metalness of the material is set at close to 1, you can either set the materials metalness to 0 or if you need to preserve the level of metalness on the material your scene will need an environment map in order for the metalness to “reflect” lighting in the scene…

1 Like

The issue I have with the examples, is that in those, the textures are loaded separately, while in my case, I’m loading just a single .gltf/.glb file, so I don’t know what to do in those cases (there should be a way to do so, since in the web I pointed out before, it loads perfectly)
The example from the three.js web:

                                        new RGBELoader()
					.setPath( 'textures/equirectangular/' )
                                         // here, the texture is loaded separately
					.load( 'royal_esplanade_1k.hdr', function ( texture ) {

						texture.mapping = THREE.EquirectangularReflectionMapping;

						scene.background = texture;
						scene.environment = texture;

The “texture” being loaded separately there is an environment map, and it is used to provide something for materials to reflect.
The web previewers load this map to provide some default light for materials to reflect. They usually also add an ambient light or a directional light.

2 Likes

[QUICK UPDATE] - After hours trying to find the issue, I found out that, even though I was instantiating a light source whenever I created a new instance of the wrapping object, it turns out that whenever I called the “loadModel” method, I ended up disposing of the lightsource object (since it WAS the first child in the “scene.children” array), so after a couple tweaks, I managed to make the model-loading cleanup work correcly:

PS: thanks everyone for your suggestions!

2 Likes