Applying gradient on textured mesh

Hi,

I have a problem with my code. I imported a mesh with obj and mtl and i implemented a function to make a gradient onto the mesh. the problem is because the material consists of many files so the gradient doesn’t function properly. How can I fix this?

function applyHeightBasedColor(object) {
object.traverse(function (child) {
if (child instanceof THREE.Mesh && child.geometry) {
const geometry = child.geometry;
geometry.computeBoundingBox();
const minZ = geometry.boundingBox.min.z;
const maxZ = geometry.boundingBox.max.z;
const heightRange = maxZ - minZ;
const color = new THREE.Color();
const colors = ;
const positionAttribute = geometry.attributes.position;

        for (let i = 0; i < positionAttribute.count; i++) {
            const z = positionAttribute.getZ(i);
            const normalizedHeight = (z - minZ) / heightRange;

            // Interpolate between 0 (red), 0.33 (green) and 0.66 (blue)
            const hue = normalizedHeight * 0.66; 
            color.setHSL(hue, 1.0, 0.5);

            colors.push(color.r, color.g, color.b);
        }

        geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
        child.material = new THREE.MeshPhongMaterial({
            vertexColors: true,
            shininess: 0
        });
    }
});

}

I’m not sure I understand the question, and there is no way to debug the code, so I can only guess what is happening.

Namely, you define gradient color depending on the Z coordinate of each vertex in the geometry. The problem is that this is not always the Z position in the scene, because the child object can be translated, scaled, rotated … and its geometry data will still be the same.

I could only suggest that you get the (X,Y,Z) of a vertex, convert it to global coordinates (aka world coordinates) and then use its Z value to calculate gradient. If this fails, then there might be some other issue.

Good luck with your project.

1 Like

A very rough example of how you can achieve it, having a hierarchical object
Pictures:


Demo: JSFiddle

2 Likes

just played a bit with the code and i decided to not use a texture but it seems like the gradient has problems with my mesh because i still have artifacts. The Mesh is an OBJ file with Texture

Do you know why I have this issue?:

function applyHeightBasedGradientMaterial(object) {
    object.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
            const geometry = child.geometry;
            geometry.computeVertexNormals(); 

            geometry.computeBoundingBox();
            const minZ = geometry.boundingBox.min.z;
            const maxZ = geometry.boundingBox.max.z;

            // Vertex Shader
            const vertexShader = `
                varying float vHeight;
                varying vec3 vNormal;
                void main() {
                    vHeight = position.z;
                    vNormal = normalize(normalMatrix * normal);
                    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
                }
            `;

            // Fragment Shader mit einfacher Beleuchtung
            const fragmentShader = `
                precision highp float;
                varying vec3 vNormal;
                varying float vHeight;
                uniform float minZ;
                uniform float maxZ;
                vec3 hsl2rgb(vec3 hsl) {
                    vec3 rgb = clamp(abs(mod(hsl.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0);
                    rgb = rgb*rgb*(3.0-2.0*rgb);
                    return hsl.z + hsl.y * (rgb-0.5)*(1.0-abs(2.0*hsl.z-1.0));
                }
                void main() {
                    float heightRatio = (vHeight - minZ) / (maxZ - minZ);
                    vec3 color = hsl2rgb(vec3(0.66 * heightRatio, 1.0, 0.5)); // HSL-Farbverlauf
                    float lighting = dot(normalize(vNormal), vec3(0, 0, 1));
                    lighting = (lighting + 1.0) / 2.0; // Normalisieren des Wertes auf das Intervall [0,1]
                    lighting = lighting * 0.5 + 0.5; // Lighting up Areas with Shadows
                    vec3 finalColor = color * lighting;
                    gl_FragColor = vec4(finalColor, 1.0);
                }
            `;

            // ShaderMaterial mit Uniforms und Shadern
            const material = new THREE.ShaderMaterial({
                vertexShader: vertexShader,
                fragmentShader: fragmentShader,
                uniforms: {
                    minZ: { value: minZ },
                    maxZ: { value: maxZ }
                }
            });

            child.material = material;
        }
    });
}



Doing so, you operate with coordinates in local space of a mesh, whereas you need coordinates in world space.

Why Z-axis for height, by the way?

The main function of vertex shader needs to be something like so:

void main(){
    vNormal = normalize(normalMatrix * normal);
    vec4 modelPos = modelMatrix * vec4(position, 1.); // to world space
    vHeight = modelPos.z; // possibly, it has to be Y-axis now, in world space
    gl_Position = projectionMatrix * viewMatrix * modelPos; 
}

:thinking:

1 Like

Hey thanks for your help!

I Want to Use the Z Axis für ground models like digital terrain models so i can see the lowest areas and the highest :-)!

I implemented your code snippet to my code but it looks the same :frowning:

function applyHeightBasedGradientMaterial(object) {
    object.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
            const geometry = child.geometry;
            geometry.computeVertexNormals(); // Stellen Sie sicher, dass Vertex-Normalen berechnet werden
            geometry.computeBoundingBox();
            const minZ = geometry.boundingBox.min.z;
            const maxZ = geometry.boundingBox.max.z;

            // Verbesserter Vertex Shader
            const vertexShader = `
                varying float vHeight;
                varying vec3 vNormal;
                void main() {
                    vNormal = normalize(normalMatrix * normal);
                    vec4 modelPos = modelMatrix * vec4(position, 1.0); // Transformation to World Coordinatesystem
                    vHeight = modelPos.z; // Z-Coordinate in world Coordinates
                    gl_Position = projectionMatrix * viewMatrix * modelPos; 
                }
            `;

            // Fragment Shader mit einfacher Beleuchtung
            const fragmentShader = `
                precision highp float;
                varying vec3 vNormal;
                varying float vHeight;
                uniform float minZ;
                uniform float maxZ;
                vec3 hsl2rgb(vec3 hsl) {
                    vec3 rgb = clamp(abs(mod(hsl.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0);
                    rgb = rgb*rgb*(3.0-2.0*rgb);
                    return hsl.z + hsl.y * (rgb-0.5)*(1.0-abs(2.0*hsl.z-1.0));
                }
                void main() {
                    float heightRatio = (vHeight - minZ) / (maxZ - minZ);
                    vec3 color = hsl2rgb(vec3(0.66 * heightRatio, 1.0, 0.5)); // HSL-Gradient
                    float lighting = dot(normalize(vNormal), vec3(0, 0, 1));
                    lighting = (lighting + 1.0) / 2.0; // Normalisieren des Wertes auf das Intervall [0,1]
                    lighting = lighting * 0.5 + 0.5; // Birighten up Shadows
                    vec3 finalColor = color * lighting;
                    gl_FragColor = vec4(finalColor, 1.0);
                }
            `;

            // ShaderMaterial mit Uniforms und Shadern
            const material = new THREE.ShaderMaterial({
                vertexShader: vertexShader,
                fragmentShader: fragmentShader,
                uniforms: {
                    minZ: { value: minZ },
                    maxZ: { value: maxZ }
                }
            });

            child.material = material;
        }
    });
}

Compute min and max z-values for the whole object, not for its separate children. :thinking:

Hmmmm but it is one obj File :thinking:. So where are the childrens :joy::see_no_evil:?

It doesn’t mean, that it’s a single mesh. From what I see, I would say the model contains several parts (meshes, thus, children).

Here is an example (not the ultimate solution) of how you can modify materials of a model, that contains children.

Before:

After:

Demo: JSFiddle

2 Likes