Can't get right texture color values when expanding MeshStandardMaterial fragment shader

Hello everyone,

I am trying to create an effect similar to a “Color Ramp” node on Blender, or Gradient Map in Photoshop. The idea would be to sample a texture (grayscale) and read one of the channels (Blender apparently uses the green one) and use the value to mix two different colors. So the darker tones would be closer to color1 and darker tones would be closer to color2, and so we go.

I am importing a model with GLTF loader (using GLB file, compressed KTX2 texture, and DRACO for mesh compression) and then changing it’s material.

To keep things as simple as possible, I am trying to just expand/modify the MeshStandardMaterial fragment shader, by creating a new Material and modifying some lines of its fragment shader using onBeforeCompile.

One example I have that currently works is doing the gradient between 2 colors using the UVs Y coordinate as the factor, with vec3 col = mix(color1, color2, vUv.y);

My code for this would be

onBeforeCompile: shader => {
          shader.uniforms.color1 = uniforms.color1;
          shader.uniforms.color2 = uniforms.color2;

          shader.fragmentShader = `
            uniform vec3 color1;
            uniform vec3 color2;
            uniform sampler2D colorTexture;

            ${shader.fragmentShader}
          `.replace(
              `vec4 diffuseColor = vec4( diffuse, opacity );`, 

              `vec3 col = mix(color1, color2, vUv.y);
              vec4 diffuseColor = vec4( col, opacity ); `
          );
        }

And this works perfectly as expected, creating a color gradient in the model that changes the color based on the UV.

But when I try using the actual color of the textures, I don’t seem to be able to read the color values at all. Every time I sample the color from the texture I get the same value for all pixels, all the time, resulting in the same color all over the model instead of any gradient.

I tried sampling from the texture from map or even including a new texture in uniforms; tried to use the values after they were loaded into diffuseColor in the map_fragment.glsl include, but nothing seems to work.

Here is an example of the code I am trying, which results in a monochromatic rendering of the model:

        onBeforeCompile: shader => {
          shader.uniforms.color1 = uniforms.color1;
          shader.uniforms.color2 = uniforms.color2;
          
          shader.fragmentShader = `
            uniform vec3 color1;
            uniform vec3 color2;

            ${shader.fragmentShader}
          `.replace(
              `#include <map_fragment>`,
            `
              #include <map_fragment>
              vec3 col = mix(color1, color2, diffuseColor.g); 
              diffuseColor = vec4( col, diffuseColor.a );
            `
          );
        }

I feel like I am missing some part of how the fragment shader is put together or how the color is sampled, so any help would be appreciated!

Thanks in advance

If I understood correctly, you want to use the color from the green chanel of the map texture to interpolate those colors, right? Any chance your color is assigned with not white but black?

Since diffuseColor is not only the sampled color from the texture but it is multiplied with the diffuse color of the material. So if the color value is not white then diffuseColor value won’t be the same as the texture is.

I would try sampledDiffuseColor instead if you want to use the directly sampled color from the texture, based on the shader code map_fragment.glsl you shared.

`
#include <map_fragment>
vec3 col = mix(color1, color2, sampledDiffuseColor.g); 
diffuseColor = vec4(col, diffuseColor.a);
`

(The current version of threejs seems to update the variable naming from sampledDiffuseColor to texelColor just fyi in case you planning to update the library in the future) ignore this my bad

If you’ve got it in the color of uniforms.color1, then I would check if the texture for map is loaded. Looks like it’s not, so sampleDiffuseColor is totally black, and no matter what color variable you’re fetching .g from, it’s always 0. :thinking: