Render OBJ with MTL and multiple EXR textures

hello,

im very new into the topic of 3d models and 3d rendering.
i’ve created a 3d model with meshroom (photogrammetry) which i wanted to render/display with threejs. i have an .obj, a .mtl and two .exr files.

the model is displayed correctly, but the second texture is somehow not loading (s. the black spots on the screenshot). i cannot figure out how to fix that.

here is my code:

init() {
    this.camera = new THREE.PerspectiveCamera(45, this.threeViewer.offsetWidth / this.threeViewer.offsetHeight, 0.1, 10000);
    this.camera.position.z = 2.5;

    // Scene
    this.scene = new THREE.Scene();

    // Lights
    const ambientLight = new THREE.AmbientLight(0xffffff, 10);
    this.scene.add(ambientLight);
    this.scene.add(this.camera);

    // Renderer
    this.renderer = new THREE.WebGLRenderer({ antialias: true });
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.renderer.setSize(this.threeViewer.offsetWidth, this.threeViewer.offsetHeight);
    this.renderer.setClearColor( 0xffffff, 0);
    this.renderer.setAnimationLoop(this.animate);
    this.threeViewer.appendChild(this.renderer.domElement);

    // Controls
    const controls = new OrbitControls(this.camera, this.renderer.domElement);
    controls.minDistance = 2;
    controls.maxDistance = 100;

    // Loaders
    this.loadModel();

    // Resize Listener
    window.addEventListener('resize', () => this.onWindowResize());
  }

  loadModel() {
    const onProgress = (xhr) => {
      if (xhr.lengthComputable) {
        const percentComplete = (xhr.loaded / xhr.total) * 100;
        this.loadingBar.style.width = `${percentComplete}%`;
        console.log(`${percentComplete.toFixed(2)}% downloaded`);

        if (percentComplete === 100) {
          setTimeout(() => {
            this.loadingContainer.style.display = 'none';
          }, 500);
        }
      }
    };

    new MTLLoader()
        .setPath('/model/')
        .load('texturedMesh.mtl', (materials) => {
          materials.preload();

          const exrLoader = new EXRLoader().setPath('/model/');
          const texturePromises = [
            new Promise((resolve) => {
              exrLoader.load('texture_1001.exr', (texture) => resolve(texture));
            }),
            new Promise((resolve) => {
              exrLoader.load('texture_1002.exr', (texture) => resolve(texture));
            }),
          ];

          Promise.all(texturePromises).then((textures) => {
            new OBJLoader()
                .setMaterials(materials)
                .setPath('/model/')
                .load(
                    'texturedMesh.obj',
                    (object) => {
                      object.traverse((child) => {
                            if (child.isMesh) {
                              if (Array.isArray(child.material)) {
                                console.log(child.material)
                                child.material[0].map = textures[0];
                                child.material[1].map = textures[1];
                                console.log(child.material)
                              } else {
                                child.material.map = textures[0];
                                child.material.emissiveMap = textures[1];
                              }
                              child.material.needsUpdate = true;
                            }
                          }
                      );

                      object.position.y = -0.95;
                      object.rotation.set(Math.PI / 4, Math.PI - (25 * (Math.PI / 180)), 0);
                      this.camera.position.set(0, 0, 50);
                      object.scale.setScalar(0.01);
                      this.scene.add(object);
                      this.loadingContainer.style.display = 'none';
                    },
                    onProgress
                );
          });
        });
  }

  onWindowResize() {
    this.camera.aspect = this.threeViewer.offsetWidth / this.threeViewer.offsetHeight;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(this.threeViewer.offsetWidth, this.threeViewer.offsetHeight);
  }

  animate = () => {
    this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
    this.renderer.toneMappingExposure = 1.0;
    this.renderer.render(this.scene, this.camera);
  };

here are the used model files: WeTransfer Download

thank you! :blush:

I think that it is a problem with your texture.

Try replacing texture_1002.exr with piz_compressed.exr example from three.js repository.

You should be able to see it looking like the attached picture.

thats weird. when i import it into blender, i get the correct model with correct textures:

also whats weird it, that in blender it shows a 3rd material “Material” which looks like a mask or something, which is not specified in the .mtl file or somewhere else:

I am not really a Blender person so somebody else might need to help you with that.

You could consider switching to a GLTF / GLB format instead of using OBJ+MTL+EXR.

Can you take a screenshot of the UV’s in the blender uv editor, this looks like a UDIM texture atlas

like that?

or what do do you mean?

Yes exactly, as you can see there are uvs that lay outside the 0,0 - 1,1 space over into the u dimension (UDIM), afaik 3js doesn’t handle this ootb, I don’t have much time right now but I’ll try dig up some resources on this when I get a moment…

1 Like

thank you!!

i got it now

it was an option in meshroom which was ticked by default:

image

i ticked it off and now its working :smiley:

1 Like