Paint vertices based on distance from center

I need a shader, which will paint mesh vertices of any 3d model based on distance from its center. From black in the middle to white on edges. So the closer to the center of the object the fragment/vert is the draker and it’s good if I can control the padding. I understand how to do it without shaders, but I need a solution to use shader. I thought I could use a fresnel shader, but it doesn’t work with other models, except of a sphere.

Except for the sphere - how do you define “center” ? Just local 0,0,0?

I think it can be bounding box/sphere center

You might need to be more specific of the meaning of “center”. For example, all points of a sphere are at equal distance from its center, so according to your description all should be painted pure white. Maybe you talk about “centers” and “edges” of the models after they are projected on the screen?

Sorry, you are right. Lets imagine that we have a 3d model of a bush. So in the middle(center) of a bush we will have black leaves, and on edges we will have white leaves.

You’ll need to know position of each leaf to interpolate colors (in shaders, or in JS) :thinking:


Demo: https://codepen.io/prisoner849/full/ZEdvovo

1 Like

@prisoner849 all leaves are parts of the same geometry, they are not separate meshes. I thought vertex position is enough to calculate distance to the center point of our model and set a right color for it.

I think we need a vertex(gl_Position?), center of a mesh coordinates(which we may need to pass as uniform?) Then we need to calculate distance of this vertex/face to the center, using distanceTo and set a color value based on this distance. I understand how to do it by setting geometry.attributes.vertexColors, but I need a solution to work as a shader and I don’t know shaders well.

An option with modified shaders:


Demo: https://codepen.io/prisoner849/full/WNqyNqP

3 Likes

Looks amazing, thank you.

However, I’m getting weird results with imported gltf, I need transition from black in the middle to white.
shader.uniforms.diffColor1 = {value: new THREE.Color(0x000000)};
shader.uniforms.diffColor2 = {value: new THREE.Color(0xFFFFFF)};

This is how my code looks:

var loader = new THREE.GLTFLoader();
loader.load("bush_1.glb", (gltf)=>
{


    console.log("gltf", gltf)
    model1 = gltf.scene;
    scene.add(model1);




    const foliage = model1.children[0];

    var mesh_material = new THREE.MeshLambertMaterial({side: THREE.DoubleSide, vertexColors: true, transparent: true, opacity: 0.5});

    foliage.geometry.computeBoundingSphere();
    let s2 = foliage.geometry.boundingSphere;


    foliage.material.onBeforeCompile = shader => {
        shader.uniforms.sphere = {value: new THREE.Vector4(s2.center.x, s2.center.y, s2.center.z, s2.radius)}
        shader.uniforms.diffColor1 = {value: new THREE.Color(0xFFFFFF)};
        shader.uniforms.diffColor2 = {value: new THREE.Color(0x000000)};
        shader.vertexShader = `
          uniform vec3 diffColor1;
          uniform vec3 diffColor2;
          uniform vec4 sphere;
          ${shader.vertexShader}
        `.replace(
          `#include <color_vertex>`,
          `#include <color_vertex>
            
            float distRatio = clamp(distance(sphere.xyz, position) / sphere.w, 0., 1.);
            vColor = mix(diffColor1, diffColor2,pow(distRatio, 3.));
          `
        );
      }

      foliage.material = mesh_material;
      foliage.material.needsUpdate = true;

    



    var controls = new THREE.OrbitControls( camera, renderer.domElement );
    camera.position.copy(model1.position);
    camera.position.z += 10;
    controls.target.copy(model1.position);
    controls.update();


}, 	// called while loading is progressing
function ( xhr ) {

	console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );

},
// called when loading has errors
function ( error ) {

	console.log( 'An error happened' );

});

Sharing 3d models
tree_1.glb (565.3 KB)
bush_1.glb (287.7 KB)