Changing material(s) of a mesh in runtime

I want to change a meshes material on the scene. But when I try to assign a new material it throws an error as following:

Uncaught TypeError: Cannot read property 'toArray' of undefined
    at flatten (model-viewer.js:17482)
    at PureArrayUniform.setValueV3fArray [as setValue] (model-viewer.js:17931)
    at Function.WebGLUniforms.upload (model-viewer.js:18214)
    at setProgram (model-viewer.js:25982)
    at WebGLRenderer.renderBufferDirect (model-viewer.js:24754)
    at renderObject (model-viewer.js:25467)
    at renderObjects (model-viewer.js:25437)
    at WebGLRenderer.render (model-viewer.js:25239)
    at Renderer.render (model-viewer.js:56467)
    at model-viewer.js:56407

Currently, I can only set color of an existing material though.
What I try is:

    const materialsLib = [
        new THREE.MeshStandardMaterial( { color: 0xff4400, metalness: 0.9, roughness: 0.2, name: 'orange' } ),
    	new THREE.MeshStandardMaterial( { color: 0x001166, metalness: 0.9, roughness: 0.2, name: 'blue' } ),
    	new THREE.MeshStandardMaterial( { color: 0x990000, metalness: 0.9, roughness: 0.2, name: 'red' } ),
    	new THREE.MeshStandardMaterial( { color: 0x000000, metalness: 0.9, roughness: 0.5, name: 'black' } ),
    	new THREE.MeshStandardMaterial( { color: 0xffffff, metalness: 0.9, roughness: 0.5, name: 'white' } ),
        new THREE.MeshStandardMaterial( { color: 0x555555, metalness: 1.0, roughness: 0.2, name: 'metallic' } )
];

For brevity I did not write all code here. There are two select menus and a change event listener on them, and some arrays to hold the materials library and meshes on the scene. Change event handler as follows:

function updateMaterial (event) {
      console.log("matMenu.selectedIndex: ", matMenu.selectedIndex, "meshMenu.selectedIndex: ", meshMenu.selectedIndex);
      var meshName = meshNames[meshMenu.selectedIndex]
      const mesh = scene.model.getObjectByName(meshName);
      // mesh.material.color.setHex(colors[matMenu.selectedIndex].value);
      mesh.material = materialsLib[matMenu.selectedIndex];
      mesh.material.needsUpdate = true;
}

How can I update materials on run time?

It seems your code snippets are not sufficient in order to understand the issue. Replacing a material is actually easy, even setting Material.needsUpdate to true is not required, see:

https://jsfiddle.net/vcx5e4g6/

Consider to demonstrate your issue with a live example.

1 Like