Combining multiple materials in Three.js results in rendering issues

I am trying to combine two meshes with different materials, but I am facing rendering issues where only one side of the object is being rendered. Here is the code I have written on codesandbox:

https://codesandbox.io/s/summer-dawn-5nq05?fontsize=14

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
    <script src="https://threejs.org/build/three.js"></script>
    <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
  </head;

  <body>
    <script>
      let renderer = new THREE.WebGLRenderer({ antialias: true });
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);

      // More Code - Truncated for Clarity

      requestAnimationFrame(function animate() {
        requestAnimationFrame(animate);
        renderer.render(scene, camera);
      });
    </script>
  </body>
</html>

Answer №1

ANSWER OUTDATED

The API for Threejs has undergone changes and no longer supports the old Geometry interface. Now, you need to directly work with BufferGeometries and their attributes. Additional merging utilities can be found on the DOCS page: BufferGeometryUtils.


This issue arises due to BoxGeometry (source) assigning a different MaterialIndex to each of its faces. This determines which material will be applied to that face when using an array of materials.

To ensure that each geometry only uses the specified material in the array, you must reset the MaterialIndex to 0 for every face of the geometry. This way, the geometry.merge() function will accurately adjust each geometry to use the correct material.

I have updated your combine function to handle this seamlessly. I couldn't find a better approach to resetting MaterialIndex, at least not currently available.

function combine(meshes) {
  
    let mergeGeometry = new THREE.Geometry();
    
    for (let i = 0; i < meshes.length; i++) {
    
      meshes[i].updateMatrix();
      
      // update materialIndex
      for ( let j = 0; j < meshes[i].geometry.faces.length; j++ ) {
      
        meshes[i].geometry.faces[j].materialIndex = 0;
        
      }
      
      mergeGeometry.merge( meshes[i].geometry, meshes[i].matrix, i );
      
    }
    
    return mergeGeometry;
    
}

JSFiddle Example

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
    <style>
      body {
        margin: 0;
        position: fixed;
      }
      canvas {
        width: 100%;
        height: 100%;
        display: block;
      }
    </style>
    <script src="https://threejs.org/build/three.js"></script>
    <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
  </head>

  <body>
    <script>
      let renderer = new THREE.WebGLRenderer({ antialias: true });
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);


      // Scene

      let scene = new THREE.Scene();
      scene.background = new THREE.Color(0xcce0ff);

      // Camera

      let camera = new THREE.PerspectiveCamera(
        75,
        window.innerWidth / window.innerHeight,
        1,
        1000
      );
      camera.position.set(30, 0, 50);

      let controls = new THREE.OrbitControls(camera);

      // Geometry

      let cube1Geometry = new THREE.BoxGeometry(10, 10, 10);
      let cube2Geometry = new THREE.BoxGeometry(10, 15, 5);
      

      // Material

      let cube1Material = new THREE.MeshStandardMaterial({
        color: 0x7c3c3c,
        roughness: 1
      });
      let cube2Material = new THREE.MeshStandardMaterial({
        color: 0x01b8ba,
        roughness: 0.1
      });

      // Mesh

      let cube1 = new THREE.Mesh(cube1Geometry, cube1Material);
      let cube2 = new THREE.Mesh(cube2Geometry, cube2Material);

      // Combine

      let singleGeometry = combine([cube1, cube2]);

      let single = new THREE.Mesh(singleGeometry, [
        cube1Material,
        cube2Material
      ]);

      scene.add(single);

      function combine(meshes) {
      
        let mergeGeometry = new THREE.Geometry();
        
        for (let i = 0; i < meshes.length; i++) {
        
          meshes[i].updateMatrix();
          
          // update materialIndex
          for ( let j = 0; j < meshes[i].geometry.faces.length; j++ ) {
          
            meshes[i].geometry.faces[j].materialIndex = 0;
            
          }
          
          mergeGeometry.merge( meshes[i].geometry, meshes[i].matrix, i );
          
        }
        
        return mergeGeometry;
        
      }

      // LIGHT
      let light1 = new THREE.AmbientLight(0x666666);
      scene.add(light1);
      let light = new THREE.SpotLight(0xdfebff, 1);
      light.position.set(50, 200, 100);
      scene.add(light);

      requestAnimationFrame(function animate() {
        requestAnimationFrame(animate);
        renderer.render(scene, camera);
      });
    </script>
  </body>
</html>

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

Exploring Node's Configuration File Reading Process

Trying my best to provide all the details for my question. Being a newbie to NodeJS, I appreciate your kind guidance :D In the process of developing an application in Node, focusing on enabling users to view stats regarding their Plex Media Libraries. The ...

Using the plyloader from threejs r82 in combination with PointsMaterial and Points

Recently, I made the transition from threejs r75 to r82. However, upon upgrading, I encountered an issue while trying to load my custom PLY file using PLYLoader. Surprisingly, nothing appears in the view and there are no error messages in the console. Upon ...

How come I am receiving the E11000 error from Mongo when I have not designated any field as unique?

Encountering an issue while attempting to save the second document to MongoDB Atlas. The error message reads as follows: Error:MongoError: E11000 duplicate key error collection: test.orders index: orderId_1 dup key: { orderId: null } Despite having no un ...

Changing input values in AngularJS

Recently, I have been working on developing a grocery list application using AngularJS. I have encountered a specific issue that I need help with. Here is the link to the problem on CodePen. The main problem I am facing is related to the highlighting of C ...

Unable to dispatch asynchronous post request

I am currently attempting to wait for a post request. I have discovered the request-promise-native package, which allows for awaiting requests. While this method is effective for GET requests, it seems to be ineffective with POST. The URL and auth hash hav ...

Tips for displaying a loading message during an AJAX request?

My AJAX function is set up like this: function update_session(val) { var session_id_to_change=document.getElementById('select_path').value; $.ajax({ type: "GET", url: "/modify_path/", asy ...

Tips for displaying timestamps in a user-friendly format, such as: 1 second ago, 1 minute ago, 1 hour ago, 1 month ago

Hey there, I'm a JavaScript newbie seeking some help! The date I'm receiving from the server is: 2015-01-16T05:55:32.000Z I want to compare this with the current date and display something like: 1 sec ago 2 min ago 2 hours ago 2 weeks ago ...

Integrating a dynamically created DOM node from JavaScript into a Vue component using Vue render functions

Is it possible to pass a JavaScript function that returns a DOM node representing a tree view with nested nodes into the Vue render function? I am aware that directly manipulating the DOM is not recommended in Vue, and using a recursive template would be ...

Require assistance with refreshing the index for the chosen row

I have encountered a problem while attempting to manipulate table rows using Javascript. Adding new rows works fine, but deleting rows presents an issue. Specifically, the delete function fails if the first row or a row in the middle is deleted (the live ...

Using jQuery to add a checkbox to a list that is not ordered

My HTML structure includes an unordered list with nested lists. Here is a snippet of the code: <div id="checklist"> <ul> <li>On Page SEO <ul> <li>My work <i>And note.</i>< ...

By setting up a keydown event listener, I am unable to inspect HTML elements by using the F-12 key shortcut in Chrome

Recently, I encountered an issue where adding a keydown event listener in my JavaScript code prevented F-12 from working properly. window.addEventListener("keydown", function (event) { if (event.defaultPrevented){ retu ...

retrieve the value obtained from a promise in an outer scope

I need a simple function that returns the name. Here's my existing code snippet: getName(entity, id) { const promise = userServices.getName(entity, id).then((data) => { return data; }); / ...

Move images horizontally next to the height of an element

I am attempting to increase the top attribute in relation to the element it is currently adjacent to. The element should only scroll directly next to the other element and cease when it surpasses the height of that element. My Goal: I want the image to re ...

Leveraging PCDLoader in APS Viewer with version 71 of three.js

Attempting to load a .pcd file format with PCDLoader from three.js has been unsuccessful for me. It seems like there may be an issue related to version compatibility. I have added the necessary scripts in the HTML: <script type="importmap"&g ...

Utilize images inputted via an HTML DOM file uploader within p5.js

I'm facing a challenge with allowing users to upload their image file through a DOM file uploader (<input type="file"></input>). Once the image is uploaded, I'm unsure of how to transfer it to JavaScript and process it using p5.js. Ho ...

Exploring the changes in rootscope with AngularJS

I am attempting to connect to $rootscope in order to establish presence on a different server when the user logs in. Below is the code for my module. angular.module('presence', [ ] ) .run(function ($rootScope) { $rootScope.$watch($rootSc ...

Problem with starting the Node.js ./bin/www script at system boot

Currently, I am in the process of learning node.js. After successfully setting up Node and Express on my computer, I utilized the express auto project generator to create a basic project. Initially, everything was running smoothly as I could access my loca ...

The filter functionality is not functioning properly in AngularJS

Currently, I am working on an AngularJS extension and encountering an issue with the filter functionality. In the popup page of my extension, there is a button. Upon clicking this button, the ancestor node of the button gets blocked, and its relevant info ...

Forcing the Empty Table message in jQuery DataTables post an AJAX request

My configuration for jquery-datatables includes a custom search filter that acts as both the standard keyword filter and a specific Item ID search using an ajax call to retrieve a value from the back end, which is then used to search a particular column in ...

Dynamically populate a SAPUI5 form with labels and input fields, assigning unique IDs to the input fields without implementing any

While developing my application, I encountered a challenge with enabling users to dynamically add new labels and inputs into a form. The issue arises when the id for the new input is set as disabled by the user, but upon clicking the edit button, the enabl ...