Update material uniforms

Hey there,

I want to update some of uniforms of my material after creating it. I use material.uniforms.targetUniform.value but there is an error which says material.uniforms is not defined.

My material code:

const fragmentShaderReplacements = [
    {
        from: `#include <common>`,
        to: `
                #include <common>
                uniform sampler2D grayScaleTexture;
                uniform sampler2D maskTexture;
                uniform vec3 bottomRedChannelColor, topRedChannelColor,
			                 bottomGreenChannelColor, topGreenChannelColor,
			                 bottomBlueChannelColor, topBlueChannelColor;    
            `,
    },
    {
        from: `#include <map_fragment>`,
        to: `
                {
                    vec4 mapColor = texture2D( map, vUv );
	                vec4 mask = texture2D(maskTexture, vUv);
	                float grayscale = mapColor.r;
	                vec3 redChannelColor = mix(bottomRedChannelColor, topRedChannelColor, grayscale) * mask.r;
	                vec3 greenChannelColor = mix(bottomGreenChannelColor, topGreenChannelColor, grayscale) * mask.g;
	                vec3 blueChannelColor = mix(bottomBlueChannelColor, topBlueChannelColor, grayscale) * mask.b;
                    diffuseColor *= vec4(redChannelColor + greenChannelColor + blueChannelColor, 1.0);
                }
            `,
    },
];

function createMaterial(path, index, activeSkinning) {
    const texture = textureLoader.load(path + index + ".png", function (texture) {
        texture.encoding = THREE.sRGBEncoding;
        texture.flipY = false;
    });
    const material = new THREE.MeshLambertMaterial({
        map: texture,
        skinning: activeSkinning,
    });
    material.onBeforeCompile = function (shader) {
        fragmentShaderReplacements.forEach((rep) => {
            shader.fragmentShader = shader.fragmentShader.replace(rep.from, rep.to);
        });
        shader.uniforms.maskTexture = {
            type: "t", value: textureLoader.load(path + index + "_mask.png", function (texture) {
                texture.encoding = THREE.sRGBEncoding;
                texture.flipY = false;
            })
        };
        shader.uniforms.topRedChannelColor = { type: 'c', value: new THREE.Color("rgb(255, 0, 0)") };
        shader.uniforms.bottomRedChannelColor = { type: 'c', value: new THREE.Color("rgb(255, 128, 128)") };
        shader.uniforms.topGreenChannelColor = { type: 'c', value: new THREE.Color("rgb(0, 255, 0)") };
        shader.uniforms.bottomGreenChannelColor = { type: 'c', value: new THREE.Color("rgb(128, 255, 128)") };
        shader.uniforms.topBlueChannelColor = { type: 'c', value: new THREE.Color("rgb(0, 0, 255)") };
        shader.uniforms.bottomBlueChannelColor = { type: 'c', value: new THREE.Color("rgb(128, 128, 255)") };
    };
    return material;
}

Hi!
Try something like this:

function createMaterial(path, index, activeSkinning) {
    const texture = textureLoader.load(path + index + ".png", function (texture) {
        texture.encoding = THREE.sRGBEncoding;
        texture.flipY = false;
    });
    const material = new THREE.MeshLambertMaterial({
        map: texture,
        skinning: activeSkinning,
    });
    material.userData = {
      topRedChannelColor : { value: new THREE.Color("rgb(255, 0, 0)") },
      bottomRedChannelColor : { value: new THREE.Color("rgb(255, 128, 128)") },
      topGreenChannelColor : { value: new THREE.Color("rgb(0, 255, 0)") },
      bottomGreenChannelColor : { value: new THREE.Color("rgb(128, 255, 128)") },
      topBlueChannelColor : { value: new THREE.Color("rgb(0, 0, 255)") },
      bottomBlueChannelColor : { value: new THREE.Color("rgb(128, 128, 255)") }
   }
    material.onBeforeCompile = function (shader) {
        fragmentShaderReplacements.forEach((rep) => {
            shader.fragmentShader = shader.fragmentShader.replace(rep.from, rep.to);
        });
        shader.uniforms.maskTexture = {
            type: "t", value: textureLoader.load(path + index + "_mask.png", function (texture) {
                texture.encoding = THREE.sRGBEncoding;
                texture.flipY = false;
            })
        };
        shader.uniforms.topRedChannelColor = material.userData.topRedChannelColor;
        shader.uniforms.bottomRedChannelColor = material.userData.bottomRedChannelColor;
        shader.uniforms.topGreenChannelColor = material.userData.topGreenChannelColor ;
        shader.uniforms.bottomGreenChannelColor = material.userData.bottomGreenChannelColor;
        shader.uniforms.topBlueChannelColor = material.userData.topBlueChannelColor;
        shader.uniforms.bottomBlueChannelColor = material.userData.bottomBlueChannelColor;
    };
    return material;
}

And then change the values in the userData:
material.userData.topRedChannelColor.value.set("rgb(128, 0, 0)");

An example: https://jsfiddle.net/prisoner849/seudpcm1/

2 Likes