Home > Software design >  Three Scene.environment texture not updating on 3D model
Three Scene.environment texture not updating on 3D model

Time:06-30

I am importing a glTF model into a WebGL ThreeJS scene. In the gltf-viewer.donmccurdy.com preview I can see that the environment setting makes a big difference to how my model renders: enter image description here

This is how it looks without the environment map:

enter image description here

So I am loading a jpg texture to use as the environment map and then loading my glTF and adding the texture as the loaded models scene.environment property. Here's the main chunk of the code:

import * as THREE from "three";
import { GLTFLoader } from "GLTFLoader";
// import { EXRLoader } from "EXRLoader";

export class AppController {

    constructor(app) {
        this.app = app;
    }
    
    animate = () => {
        requestAnimationFrame( this.animate );
        this.renderer.render( this.scene, this.camera );
    }
    
    init = () => {
        console.log('AppController.init()');
        this.scene = new THREE.Scene();
        this.camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

        this.scene.background = new THREE.Color( 0x00ffff );

        const light = new THREE.AmbientLight( 0x404040 ); // soft white light
        this.scene.add( light );

        this.renderer = new THREE.WebGLRenderer();
        this.renderer.setSize( window.innerWidth, window.innerHeight );
        this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
        this.renderer.outputEncoding = THREE.sRGBEncoding;
        document.body.appendChild( this.renderer.domElement );
        this.camera.position.z = 100;

        const fthis = this;
        new THREE.TextureLoader().load("./assets/images/env.jpg",
        function ( texture ) {
            fthis.scene.environment = texture;
            fthis.loadModel(texture);
        },
        undefined,
        function ( err ) {
            console.error( err );
        });
        this.animate();
    }
    
    loadModel = (texture) => {

        const loader = new GLTFLoader();
        const modelPath = 'assets/models/Pebble_Logo_Extrude.gltf';
        const fthis = this;
        loader.load( modelPath, function ( gltf ) {
            console.log('gltf', gltf);
            fthis.pebbleLogo = gltf;
            fthis.pebbleLogo.scene.environment = texture;
            fthis.pebbleLogo.scene.traverse(function (child) {
                if (child instanceof THREE.Mesh) {
                    if(child.material !== null){
                        console.log("setting envmap");
                        child.material.envMap = texture;
                        child.material.metalness = 0.0;
                        child.material.envMapIntensity = 2.0;
                        child.material.needsUpdate = true;
                    }
                }
            });
            fthis.scene.add( fthis.pebbleLogo.scene );
        }, undefined, function ( error ) {
            console.error( error );
        } );
    }
}

The result is this: enter image description here

As you can see, the environment map has had no effect at all. I did read something about possibly needing to use the EXRLoader to make the map, but this was an old article and I assumed this may no longer be the case seeing as Three doesn't include in their documentation.

Any help most appreciated.

CodePudding user response:

Before assigning the environment map to Scene.environment, you need the following line of code:

texture.mapping = THREE.EquirectangularReflectionMapping;

This defines that your loaded texture is in the equirectangular format.

Besides, if you load a LDR environment map, you want to add the following line, too:

texture.encoding = THREE.sRGBEncoding;

BTW: If you use no HDR environment map, there is no need to configure tone mapping. So you can remove:

this.renderer.toneMapping = THREE.ACESFilmicToneMapping;

CodePudding user response:

In the examples source, you'll see they are using RGBELoader to get the texture for the environment mapping. This is all you need. My updated code:

import * as THREE from "three";
import { GLTFLoader } from "GLTFLoader";
import { RGBELoader } from "RGBELoader";

export class AppController {

    constructor(app) {
        this.app = app;
    }
    
    animate = () => {
        requestAnimationFrame( this.animate );
        this.renderer.render( this.scene, this.camera );
    }
    
    init = () => {
        console.log('AppController.init()');
        this.scene = new THREE.Scene();
        this.camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

        this.scene.background = new THREE.Color( 0x00ffff );
        const light = new THREE.AmbientLight( 0x404040 ); // soft white light
        this.scene.add( light );

        this.renderer = new THREE.WebGLRenderer();
        this.renderer.setSize( window.innerWidth, window.innerHeight );
        this.renderer.shadowMap.enabled = true;
        this.renderer.outputEncoding = THREE.sRGBEncoding;
        document.body.appendChild( this.renderer.domElement );
        this.camera.position.z = 50;

        const fthis = this;
        new RGBELoader()
            .setPath( 'assets/images/' )
            .load( 'venice_sunset_1k.hdr', function ( texture ) {
                texture.mapping = THREE.EquirectangularReflectionMapping;
                fthis.scene.environment = texture;
                fthis.loadModel(texture);
            } );
        this.animate();

        // get the mouse position
        this.raycaster = new THREE.Raycaster();
        this.mouse = new THREE.Vector2();

        // setup listeners
        document.addEventListener('mousemove', this.onDocumentMouseMove, false);
        window.addEventListener('resize', this.onWindowResize, false);
    }
    
    onDocumentMouseMove = (event) => {
        event.preventDefault();
        this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        this.mouse.y = -(event.clientY / window.innerHeight) * 2   1;
    }
    
    onWindowResize = () => {
        this.camera.aspect = window.innerWidth / window.innerHeight;
        this.camera.updateProjectionMatrix();
        this.renderer.setSize(window.innerWidth, window.innerHeight);
    }

    loadModel = (texture) => {

        const loader = new GLTFLoader();
        const modelPath = 'assets/models/Pebble_Logo_Extrude.gltf';
        const fthis = this;
        loader.load( modelPath, function ( gltf ) {
            console.log('gltf', gltf);
            fthis.pebbleLogo = gltf;
            fthis.scene.add( fthis.pebbleLogo.scene );
        }, undefined, function ( error ) {
            console.error( error );
        } );
    }
}
  • Related