glTF: Assign an HDR environement map to glTF asset

I have my asset exported and looking alright, but it’s missing an environment map. I know glTF has all the data built in and got some crazy results when I tried to add my own MeshStandardMaterial.

This is what my scene looks like (ambient light is being used to visualize the model, otherwise it’s totally black). Code is below
rbg_envmap

var renderer;
var control;
var mixer;

var mesh; // use this to get a reference to to the mesh outside the scope of loader.load

var container = document.createElement('div');
document.body.appendChild(container);

var clock = new THREE.Clock();

var scene = new THREE.Scene();

var camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 1, 10000);
camera.position.set(50,50,50);
camera.target = new THREE.Vector3( 0, 0, 0 );

renderer = new THREE.WebGLRenderer( { alpha: false } );
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
container.appendChild(renderer.domElement);

var controls = new THREE.OrbitControls( camera, renderer.domElement );

var light = new THREE.AmbientLight( 0xffffff, 1 );
light.position.set( 0.0, 0.0, 0.0 ).normalize();
scene.add( light );

// HDR
var floorMaterial = new THREE.MeshBasicMaterial( {
    color: 0xffffff,
    // needsUpdate: true
} );

var planeGeometry = new THREE.PlaneBufferGeometry( 200, 200 );
var planeMesh1 = new THREE.Mesh( planeGeometry, floorMaterial );
planeMesh1.position.y = - 50;
planeMesh1.rotation.x = - Math.PI * 0.5;
scene.add( planeMesh1 );

var ibl_texture = new THREE.EXRLoader().load( './assets/hdr/piz_compressed.exr', function ( texture ) {
    texture.minFilter = THREE.NearestFilter;
    texture.magFilter = THREE.NearestFilter;
    texture.encoding = THREE.LinearEncoding;

    var cubemapGenerator = new THREE.EquirectangularToCubeGenerator( texture, 512 );
    var cubeMapTexture = cubemapGenerator.update( renderer );

    var pmremGenerator = new THREE.PMREMGenerator( cubeMapTexture );
    pmremGenerator.update( renderer );

    var pmremCubeUVPacker = new THREE.PMREMCubeUVPacker( pmremGenerator.cubeLods );
    pmremCubeUVPacker.update( renderer );

    exrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;

    texture.dispose();
    cubemapGenerator.dispose();
    pmremGenerator.dispose();
    pmremCubeUVPacker.dispose();
});


floorMaterial.map = ibl_texture;

var manager = new THREE.LoadingManager();

// Material
var textureLoader = new THREE.TextureLoader();
var aoTexture = textureLoader.load('./assets/textures/ao.png');
var baseTexture = textureLoader.load('./assets/textures/baseColor.png');
var heightTexture = textureLoader.load('./assets/textures/height.png');
var metalTexture = textureLoader.load('./assets/textures/metallic.png');
var normalTexture = textureLoader.load('./assets/textures/normal.png');
var alphaTexture = textureLoader.load('./assets/textures/opacity.png');
var roughTexture = textureLoader.load('./assets/textures/roughness.png');
var scattTexture = textureLoader.load('./assets/textures/scattering.png');


var material = new THREE.MeshStandardMaterial({
    alphaMap: alphaTexture,
    map: baseTexture,
    aoMap: aoTexture,
    normalMap: normalTexture,
    roughness: 1,
    roughnessMap: roughTexture,
    metalness: 1,
    metalnessMap: metalTexture,
    bumpMap: heightTexture,
    lights: true,
});

var loader = new THREE.GLTFLoader();
loader.load( './assets/models/from_sketchfab/scene.gltf', function ( gltf ) {

	var content = gltf.scene;

    clips = gltf.animations;
    content.position.set(0, 0, 0);
	content.scale.set(15, 15, 15);

	scene.add(content);

    clips.forEach((clip) => {
        if (clip.validate()) clip.optimize();
    });

    mixer = new THREE.AnimationMixer(scene);

    clips.forEach((clip) => {
        mixer.clipAction(clip).play();
    });

	// Apply envMap to model.
	content.traverse((node) => {
		if (node.isMesh) {
		    node.material.envMap = ibl_texture;
        }
		debugger;
	});



});

animate();

function animate() {
    requestAnimationFrame( animate );

    if(mixer){

        mixer.update(clock.getDelta());
    }

    render();

}

function render() {
    renderer.render( scene, camera );
}

Not sure about the HDR environment but for that material variable you create, I assume you want to attach it to the mesh in your glTF model? If so, see the notes in https://github.com/mrdoob/three.js/pull/14897 — you’ll want .flipY = false on each texture, at least.

I have the material working, but the hands are freaking out. Everything looks fine if I don’t move the camera. Once I do (mostly when I zoom in and out) the hands start to freak out. Any thoughts? This doesn’t happen when the mesh isn’t assigned.

crazy_mat

Hi Mike,

Try swappping this out:
// Apply envMap to model.
content.traverse((node) => {
if (node.isMesh) {
node.material.envMap = ibl_texture;
}
debugger;
});

For this:

content.traverse( function ( child ) {

    if ( child.isMesh ) {
      child.material.envMap = ibl_texture;
      //shadows are optional:
      //child.castShadow = true;
      //child.receiveShadow = true;
    }

  });
1 Like

Hm, yeah I’m confused by the hands/skinning issue — do you see the same on http://gltf-viewer.donmccurdy.com/? Or could you share a demo and/or the model? It sounds a bit like https://github.com/mrdoob/three.js/issues/13288.

It doesn’t occur in the viewer or within my own code if I keep the material off the mesh. It only happens when I assign the material. I figured out that I have to assign the render target to the envmap in my render method (based on the three.js example file for EXR that I’m using). I can send my repo privately if you’d like to inspect it.

@mikebourbeauart, can you please, show that part of code which you described here:

I figured out that I have to assign the render target to the envmap in my render method

I took several approaches but failed. All this a bit confusing for me.
Thanks in advance!