How to properly implement envMap in Three.js?

Could someone assist me in finding a material similar to the one shown here?

I attempted to implement it using the following code:

* {
margin: 0;
padding: 0;
}

.object {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
pointer-events: none;
background-color: rgb(200, 200, 200);
}
<script type="module">
import * as THREE from "https://threejs.org/build/three.module.js";

import { OBJLoader } from "https://threejs.org/examples/jsm/loaders/OBJLoader.js";

var container;

var camera, scene, renderer;

var mouseX = 0,
    mouseY = 0;

var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;

var object;

init();
animate();

function init() {
    // Code for initializing the scene and loading models
}

// Other functions and event listeners

</script>

Unfortunately, I am facing issues with its implementation. Can anyone provide some assistance on this? Thank you! :)

Answer №1

The primary concern arises from the fact that newEnvMap is not yet available when the object is loaded.

Typically, the key steps involved in adding an Environment Map to a scene are as follows:

  1. Import EXRLoader.
  2. Create a "Prefiltered, Mipmapped Radiance Environment Map (PMREM)" using THREE.PMREMGenerator.
  3. Load the EXR (or JPG image) using new EXRLoader() (or THREE.TextureLoader().load()).
  4. After loading the EXR, objects need to be updated with this setting. In the CodePen example, this is achieved through the function loadObjectAndAndEnvMap(). This function loads the model and updates envMap with
    object.material.envMap = newEnvMap;
    .
  5. Remember to set
    object.material.needsUpdate = true
    .
  6. To visually represent the background itself, ensure to define scene.background = background; within the render function.
  7. (Extra) If the environment map remains invisible, verify the values of roughness, metalness, and envMapIntensity for your materials. Incorrect settings can lead to discrepancies in reflecting the environment. Here are some example configurations:

https://i.sstatic.net/dHLlu.png

Demo:

* {
margin: 0;
padding: 0;
}

.object {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
pointer-events: none;
background-color: rgb(200, 200, 200);
}
<script type="module">
import * as THREE from "https://threejs.org/build/three.module.js";
import { OBJLoader } from "https://threejs.org/examples/jsm/loaders/OBJLoader.js";
import { EXRLoader } from "https://threejs.org/examples/jsm/loaders/EXRLoader.js";

var container;
var camera, scene, renderer;
let exrCubeRenderTarget, exrBackground;
let newEnvMap;
let torusMesh, planeMesh;

var mouseX = 0,
    mouseY = 0;

var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;

var object;

init();
animate();

function init() {
    container = document.createElement("div");
    container.className = "object";
    document.body.appendChild(container);

    camera = new THREE.PerspectiveCamera(
        45,
        window.innerWidth / window.innerHeight,
        1,
        2000
    );
    camera.position.z = 250;

    // scene

    scene = new THREE.Scene();

    /*var ambientLight = new THREE.AmbientLight(0xcccccc, 0.4);
    scene.add(ambientLight);
    var pointLight = new THREE.PointLight(0xffffff, 2);
    pointLight.position.set(100, 100, 50);
    camera.add(pointLight);*/

    scene.add(camera);

    // manager
    function loadModel() {
        THREE.DefaultLoadingManager.onLoad = function () {
            pmremGenerator.dispose();
        };

        // -----------------

        function loadObjectAndAndEnvMap() {
            object.traverse(function (child) {
                //This allow us to check if the children is an instance of the Mesh constructor
                if (child instanceof THREE.Mesh) {
                    child.material = new THREE.MeshStandardMaterial({
                        color: "#555",
                        roughness: 0.0,
                        metalness: 2.0,
                        envMapIntensity: 5.0
                    });
                    //child.material.flatShading = false;

                    console.log("setting envmap");
                    child.material.envMap = newEnvMap;
                    child.material.needsUpdate = true;

                    //Sometimes there are some vertex normals missing in the .obj files, ThreeJs will compute them
                }
            });
            object.position.y = -90;
            scene.add(object);
        }

        const pmremGenerator = new THREE.PMREMGenerator(renderer);
        pmremGenerator.compileEquirectangularShader();

        new EXRLoader()
            .setDataType(THREE.UnsignedByteType)
            .load(
                "https://threejs.org/examples/textures/piz_compressed.exr",
                function (texture) {
                    exrCubeRenderTarget = pmremGenerator.fromEquirectangular(texture);
                    exrBackground = exrCubeRenderTarget.texture;
                    newEnvMap = exrCubeRenderTarget ? exrCubeRenderTarget.texture : null;

                    loadObjectAndAndEnvMap(); // Add envmap once the texture has been loaded

                    texture.dispose();
                }
            );

        renderer.toneMapping = THREE.ACESFilmicToneMapping;
        renderer.outputEncoding = THREE.sRGBEncoding;
    }

    var manager = new THREE.LoadingManager(loadModel);

    manager.onProgress = function (item, loaded, total) {
        console.log(item, loaded, total);
    };


    function onProgress(xhr) {
        if (xhr.lengthComputable) {
            var percentComplete = (xhr.loaded / xhr.total) * 100;
            console.log("model " + Math.round(percentComplete, 2) + "% downloaded");
        }
    }
    function onError() {}
    var loader = new OBJLoader(manager);
    loader.load(
        "https://threejs.org/examples/models/obj/female02/female02.obj",
        function (obj) {
            object = obj;
        },
        onProgress,
        onError
    );

    //

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

    document.addEventListener("mousemove", onDocumentMouseMove, false);

    //

    window.addEventListener("resize", onWindowResize, false);
}

function onWindowResize() {
    windowHalfX = window.innerWidth / 2;
    windowHalfY = window.innerHeight / 2;

    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);
}

function onDocumentMouseMove(event) {
    mouseX = (event.clientX - windowHalfX) / 2;
    mouseY = (event.clientY - windowHalfY) / 2;
}

//

function animate() {
    requestAnimationFrame(animate);
    render();
}

function render() {
    camera.position.x += (mouseX - camera.position.x) * 0.05;
    camera.position.y += (-mouseY - camera.position.y) * 0.05;

    camera.lookAt(scene.position);

    scene.background = exrBackground;
    renderer.toneMappingExposure = 1.0;
    renderer.render(scene, camera);
}
</script>

If you prefer CodePen over SO snippets, feel free to access the CodePen version here for voting, forking, and sharing.

Answer №2

The latest feature allows for the utilization of scene.environment as the environment map across all physical materials within the scene. Nevertheless, it is vital to note that attempting to replace a pre-existing texture linked to MeshStandardMaterial.envMap is not supported. For more information, you can refer to this documentation link.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Leveraging ES6 Generators for Efficient XMLHttpRequests

My goal is to simplify AJAX calls using ES6 generators, but I've encountered some issues: let xhr = new XMLHttpRequest() function *statechange() { yield xhr.readyState; } let gen = statechange(); xhr.open("GET", myUrl, true); xhr.onreadystatec ...

Troubleshooting Automatic Scrolling Problems in React Virtualized

In my project utilizing react-virtualized v-9.21.2 to showcase a list, a problem arises when adding a new item. I employ a method of clearing the cache and updating the listKey to enable auto resizing the height. However, this results in an undesired behav ...

Having trouble identifying whether a file is a picture or video based solely on its extension

My current challenge involves determining whether an uploaded file is a video or a photo. This distinction is crucial as it dictates whether the file should be sent to my telegram bot as a video or photo attachment. Despite having what seems like a logica ...

"Discover the power of Algolia's docSearch feature

Currently, I am working on integrating Algolia DocSearch into my docusaurus project. After obtaining the api key and api id from Algolia, I am unsure of the next steps to take. I would appreciate guidance on the necessary procedures that need to be followe ...

Why is my filtering and sorting function failing to function properly?

I have a collection of events represented by an array of objects, where each event contains a start date and an end date. My goal is to filter out any events that have already passed based on the current time (now), and then sort the remaining events in d ...

Generating a unique event triggered by element class change

Is it possible to create custom JavaScript events that are triggered when elements receive a specific class? I am trying to monitor all elements within a table and perform certain actions once a particular class is added to them. Can this be done, and if ...

Tips for managing a PHP post request without full knowledge of the variables to retrieve

My form allows users to dynamically add new lines using JavaScript. However, when they click the save button, I am struggling to capture and assign the new data to a variable. The current issue is that once the user adds new rows and clicks save, the rows ...

What is the best way to retrieve the outcome of a node-sqlite3 query beyond the scope of db.get()?

I'm attempting to validate whether the sha256 hash stored in my sqlite database corresponds with the sha256 hash of the password that the user transmitted to my NodeJS server. The Auth() method is meant to deliver either a true or false result. Is the ...

Utilizing Axios Instances for Authorization in Next.js Data Fetching

I am currently utilizing NextJS version 12.0.10 along with next-redux-wrapper version 7.0.5. I have implemented an Axios custom instance to store the user JWT token in local storage and automatically include it in every request, as well as to handle errors ...

What is the best way to conceal a dropdown menu when the page first loads?

I have a dropdown menu that is displaying like this: <ul> <li class="dropdown open"> <a aria-expanded="true" href="#" class="dropdown-toggle waves-effect waves-button waves-classic" data-toggle="dropdown"> <spa ...

What is the best way to choose a single li element?

I am working on creating a reservation system using HTML, CSS, and JS. I want to customize the color of the border for each list item (li). However, I want only one li to be selected at a time. When I click on a different li, it should remove the selection ...

Create PDF and Excel files using Javascript on the client side

Are there any client-side Javascript frameworks that are comparable to Jasper Report in Java? I need to be able to generate both PDF and Excel files on the client side (browser) without relying on server-side processing. While I've come across Ja ...

Find a specific row in a table using jQuery without requiring any input from the

I want to create a search function that filters results in a table based on user input. The script I have currently works but I need it to only search the first column without requiring the user to click an input field. I want the default value to load whe ...

Using Bootstrap to handle opening a link when a dropdown menu button is clicked

Utilizing Bootstrap in my main menu was essential for navigating through the numerous pages, subpages, and sub-subpages within my project. The dropdownmenu feature proved to be very helpful in this regard. However, my client now has a specific request - t ...

Why does JavaScript function flawlessly in FireFox, yet fails completely in other web browsers?

When it comes to browsing, FireFox is my go-to browser, especially for testing out my website Avoru. However, I recently encountered an issue when checking the functionality of my code on other major browsers like Google Chrome, Opera, and Safari. It seems ...

Using JQuery to create interactive dropdown menus with dynamic options

I am exploring the possibility of dynamically updating the choices available in an HTML dropdown menu based on the selection made by a user - consider this sample JSON data: series: [ {name: 'Company X', product: 'X1'}, {name: 'Co ...

Is there a way to prevent a user who is already logged in from accessing their account on a different browser or tab

My current stack consists of reactjs, nodejs, and redux for user authentication. I am utilizing aws cognito to handle the user authentication process. The main functionality of my app is uploading files from users to an s3 bucket. One of my goals is to p ...

Access arrays/objects within main object using JavaScript's Object.keys()方法

Perhaps the title is a bit confusing, but I couldn't come up with a better way to phrase it. I have a function that determines the value of each property within a contact object and returns 'No Data' if the value is null/empty/undefined. Ho ...

jQuery's show/hide function exhibiting inconsistencies when checking or unchecking

I have implemented a feature on my webpage where there is a field for previous surgeries with certain elements. The goal is to display the previous surgery elements only if the "previous surgery" checkbox is checked. Below is a snippet of the code I'm ...

How do useCases interact with each other within Clean Architecture principles in NodeJS?

I'm currently working on implementing Bob Martin's Clean Architecture in my project, and I have a question. How do use-cases interact with each other? For instance: In my project, there are entities for Department and Employee. The Department ...