How to add Multicolor Gradient to Imported GLTF Model

Hey, I have the following problem hoping for your help.

It’s my first project with three.js and I’m importing an gltf model made in blender.
This model is only one mesh and now I want to apply a multicolor gradient to it.
The gradient should go from a magenta to yellow to green to blue.

I found some help online, which enabled me to apply
a two-color gradient from (boundingbox) y.max to y.min to my mesh - but not the
four-color gradient, which should go from (x.max,y.max,z.max) to (x.min,y.min,z.min).

The color changes should be equally distributed:

  • 0-33% magenta to yellow
  • 33%-66% yellow to green
  • 66%-100% green to blue

Do you guys have an idea how to solve this?

My corresponding code looks like this till now.

[...]

    importModel() {
        const loader = new GLTFLoader()
        const model = new URL('/models/my_mesh_model.gltf', import.meta.url); 

        loader.load(
            // Model
            model.href,
            // called when the resource is loaded
            gltf => {
                gltf.scene.traverse(function (child) {
                    if (child.isMesh) {
                        child.material = new THREE.ShaderMaterial({
                            uniforms: {
                              color1: {
                                //magenta
                                value: new THREE.Color(0xFF0089)
                              },
                              color2: {
                                //yellow
                                value: new THREE.Color(0xCFC91E)
                              },
                              color3: {
                                //green
                                value: new THREE.Color(0x02E91E)
                              },
                              color4: {
                                //blue
                                value: new THREE.Color(0x00DAFF)
                              },
                              bboxMin: {
                                value: child.geometry.boundingBox.min
                              },
                              bboxMax: {
                                value: child.geometry.boundingBox.max
                              }
                            },
                            vertexShader: `
                              uniform vec3 bboxMin;
                              uniform vec3 bboxMax;
                            
                              varying vec2 vUv;
                          
                              void main() {
                                vUv.y = (position.y - bboxMin.y) / (bboxMax.y - bboxMin.y);
                                gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
                              }
                            `,
                            fragmentShader: `
                              uniform vec3 color1;
                              uniform vec3 color2;
                              uniform vec3 color3;
                              uniform vec3 color4;
                            
                              varying vec2 vUv;
                              
                              void main() {
                                // works - but only for two colors
                                gl_FragColor = vec4(mix(color1, color2, vUv.y), 1.0);
                                // not working
                                //gl_FragColor = vec4(color1*vUv.y*0.2+color2*vUv.y*0.4+color3*vUv.y*0.6+color4*vUv.y*0.8, 1.0);
                              }
                            `,
                            wireframe: false
                          });
                    }
                });

                gltf.scene.position.set(0, 0, 1);
                this.logo = gltf.scene;
                this.scene.add(gltf.scene);
            },
            // 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' );
            }
        );
    }

[...]

It’s my first time with three.js and I’m not quite into js in general, so please be nice with me. :wink:

A few questions (just out of curiosity)

  • What does this “four-color gradient, which should go from (x.max,y.max,z.max) to (x.min,y.min,z.min)” mean? The gradient is along the body diagonal of the bounding box?
  • Is the mesh static or it is rotating/scaling continuously?

Hey @PavelBoytchev

  • Yes, you’re right. it should go diagonal thorough the whole object. If you imagine a cube, it should go from the top left front corner to the bottom right back one.
  • The final object should “hover” a little bit. like something on the waves - but without the water. Just hovering in plain air. (movement just on the y-axis). Till now i realize that with the following function:
    animate() {
        this.renderer.render(this.scene, this.camera);
        requestAnimationFrame(this.animate.bind(this));
        //Floating effect: 0.05: floating height | 0.1: vertical offset
        const time = this.clock.getElapsedTime();
        if (typeof this.myObject !== 'undefined') {
            this.myObject.position.y = (Math.cos( time ) * 0.05) + 0.1;
        }
    }

Draw the gradient on a canvas and use it with CanvasTexture, processing it in shaders.

4 Likes

Wow! That looks fantastic and should solve my problem.
Do you have a hint/link with examples you could recommend?

I found these

Maybe - would you share your example. As I can see it would help me alot.


Add/Edit: I found your example. Thanks for not making this private. Even without comments, your code is more instructive than most I found. Like your codepens btw. :wink:

Thanks for your help! It’s great to have people like you :+1:

1 Like

Ah! Glad you found it.
I wasn’t sure that this is what you need, planned to post it later :smiley:

1 Like

It definitely helps me SO much. :+1: :+1:

1 Like

You’re welcome :handshake:

1 Like

Sorry to bother again, but now a followup question appears.
Can you tell me, what I would have to change, if I want to use your approach, to apply the gradient to individual meshes of a gltf model/object.

For example: I import a gltf file, which consists of three cubes (small, medium, big) the structure would be (according to https://gltf-viewer.donmccurdy.com/ )

image

but the output is something like this:

The desired output would be: The gradient is applied to each individual cube and is scaled to the cubes’s size/boundingBox.

My guess is, i have to modify u.box-part of your example (line 47 ff) to calculate a box for each mesh and apply it to the shader - but i don’t get it.

Or am I completely wrong?

My example gltf: example.gltf (8.9 KB)

Something like this?


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

1 Like

Ex-act-ly-! :star_struck:

Made my day! :+1: Thank you so much!

1 Like