How to combine gradient shader material with mesh origin material

I have a shader material.

function createGradient(originColor){
    return new THREE.ShaderMaterial({
        uniforms: {
          originColor: {
            value: originColor
          },
          center: {
            value: new THREE.Vector3(-5, -20, 0)
          },
          radius: {
            value: 20.0
          }
        },
        vertexShader: `
          varying vec3 vPosition;

          void main() {
            vPosition = position;
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
          }
        `,

        fragmentShader: `
          uniform vec3 originColor; 
          uniform vec3 center; 
          uniform float radius;
      
          varying vec3 vPosition;

          void main() {
            float dis = distance(vPosition, center);
            float bound1 = radius / 3.0;
            float bound2 = (radius / 3.0) * 2.0;
            float r, g, b;
            if (dis < bound1) {
              float rate = dis / bound1;
              r = 1.0;
              g = mix(0.0, 1.0, rate);
              b = 0.0;
            } 

            if (dis >= bound1 && dis < bound2) {
              float rate = (dis - bound1) / bound1;
              r = mix(1.0, 0.0, rate);
              g = 1.0;
              b = mix(0.0, 1.0, rate);
            }

            if (dis >= bound2 && dis <= radius) {
              float rate = (dis - bound2) / bound1;
              r = 0.0;
              g = mix(1.0, 0.0, rate);
              b = 1.0;
            }
            if (dis > radius) {
              r = originColor.r;
              g = originColor.g;
              b = originColor.b;
            }
            gl_FragColor = vec4(r, g, b, 1.0);
          }`
      });
}

When switch to the gradient material.

mesh.material = createGradient(mesh.material.color)
so out of the gradient radius use mesh original color.


but lost original material oapcity and light.
Snipaste_2020-12-10_16-50-08

Is there a easy to combine the gradient and original material?

Combination of materials is not possible. Have you considered to inject your shader code via Material.onBeforeCompile() into built-in materials? This approach is demonstrated in webgl_materials_modified.

3 Likes

I also created two gists for this purpose

1 Like

Not so long ago, there was a question on discord about a force field and how to show its border on objects in a scene.
I wrote an example, that uses .onBeforeCompile() for MeshStandardMaterial:

The solution also works for instanced meshes:

3 Likes

thanks! i will try Material.onBeforeCompile

looks very nice! thanks for your example :beer:

thanks for your code! i will tri it.

Hi! i create the gradient successful by follow the example https://codepen.io/prisoner849/pen/NWrJmrG?editors=0010


many thanks!
And when i want to remove the gradient. I tried to restore the shader in onBeforeCompile by replace the shader code back to default StandardMaterial shader code, but it not works, because the onBeforeCompile only execute once.
But i remove the gradient by blow code.

let cachedMaterial = new Map();
if (mode === 0) {
        // remove gradient by restore material from cache
        mesh.material = this.cachedMaterial.get(mesh)
        mesh.material.needsUpdate = true
      } else {
        // save origin material to cache
        if (!this.cachedMaterial.has(mesh.material)) {
          this.cachedMaterial.set(mesh, mesh.material)
        }

        mesh.material = mesh.material.clone();
        mesh.material.onBeforeCompile = onBeforeCompile.bind(null, mode, length, position);
        mesh.material.needsUpdate = true;
  }

Is this the right to remove the gradient to origin material?