Search code examples
three.jsshader

How to make a simple gradient shader on a cube


I'm trying to use a code I found here to create a gradient shader on a cube, based on coordinates. But the position in my vertex shader doesn't seem to vary. It goes from 0 to 1 without any steps in between:

enter image description here

What am I doing wrong? https://codesandbox.io/s/modest-silence-xzg1c?file=/src/App.js

Here is my fragment and vertex shader:

    const vertexShader = `
      varying vec2 vUv; 

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

    const fragmentShader = `
      uniform vec3 colors[2]; 

      varying vec2 vUv;

      void main() {
        gl_FragColor = vec4(mix(colors[0], colors[1], vUv.y), 1.0);
      }
    `

    const uniforms = {
      colors: {
        type: 'v3v',
        value: [new Color('#ff0000'), new Color('#0000ff')]
      }
    }

Solution

  • This is how you can interpolate colors, using Y-coord of vertices:

    body{
      overflow: hidden;
      margin: 0;
    }
    <script type="module">
    import * as THREE from "https://cdn.skypack.dev/three@0.135.0";
    import {OrbitControls} from "https://cdn.skypack.dev/three@0.135.0/examples/jsm/controls/OrbitControls";
    
    let scene = new THREE.Scene();
    let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 10000);
    camera.position.set(0, 0, 1500);
    let renderer = new THREE.WebGLRenderer();
    renderer.setSize(innerWidth, innerHeight);
    document.body.appendChild(renderer.domElement);
    
    let controls = new OrbitControls(camera, renderer.domElement);
    
    let g = new THREE.BoxBufferGeometry(200, 200, 200, 10, 10, 10);
    let m = new THREE.ShaderMaterial({
      uniforms: {
        colors: { 
          value: [new THREE.Color('#ff0000'), new THREE.Color('#0000ff')]
        }
      },
      vertexShader: `
        varying float h; 
    
        void main() {
          h = position.y;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
      `,
      fragmentShader: `
        uniform vec3 colors[2]; 
    
        varying float h;
    
        void main() {
          float f = (h + 100.) / 200.;  // linear interpolation
                                    // but you can also use 'smoothstep'
          f = clamp(f, 0., 1.);
          gl_FragColor = vec4(mix(colors[0], colors[1], f), 1.0);
        }
      `
    })
    let o = new THREE.Mesh(g, m);
    o.scale.setScalar(5);
    scene.add(o);
    
    renderer.setAnimationLoop(() => {
      renderer.render(scene, camera);
    });
    </script>