Rendering a gradient material using threejs and GLSL

glsl
materials

#1

Hello again! I am trying to learn about shaders using three.js. What I am trying to do is create a shader that generates gradients to texture planets with. Right now I am just trying to generate one gradient to make sure it works. However, when I apply the shader it only renders one of the colors, and does not create the gradient effect I’m looking for.

I’m using the Book of Shaders as the basis for my code. Specifically, I was looking at this example, trying to replicate the background color.

Here is my shader code:

  <section id="fragmentshader">
      #ifdef GL_ES
      precision mediump float;
      #endif

      // #define PI 3.14159265359

      uniform vec2 u_resolution;
      // uniform vec2 u_mouse;
      // uniform float u_time;

      vec3 colorA = vec3(0.500,0.141,0.912);
      vec3 colorB = vec3(1.000,0.833,0.224);



      void main() {
          vec2 st = gl_FragCoord.xy/u_resolution.xy;
          vec3 color = vec3(0.0);

          color = mix( colorA,
                 colorB,
                 st.y);

          gl_FragColor = vec4(color,1.0);
      }
    </section>
    <section id="vertexshader">

      void main() {
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      }
    </section>

and my three.js code inside an a-frame component:

    var uniforms = {
      u_resolution: { type: "v2", value: new THREE.Vector2() },
    };

    var fShader = $('#fragmentshader');
    var vShader = $('#vertexshader');


    var geometry = new THREE.SphereGeometry(getRandomInt(100, 250), 20, 20);

    // var material = new THREE.MeshBasicMaterial( {wireframe: true });
    var material = new THREE.ShaderMaterial({
      uniforms: uniforms,
      vertexShader: vShader.text(),
      fragmentShader: fShader.text()

    });

    var sphere = new THREE.Mesh(geometry, material);

This is what my spheres look like after this:


#2

A few months ago, I also dealt with shader for the first time. I also took this book.

Then I have made some very simple examples for beginners. May be, they are helpful to you.

http://threejs.hofk.de/shader/01_shader_wuerfel.html
A part of http://threejs.hofk.de/

The comments are in German but with e.g. google translator you can understand them.

(Here in the forum writing I need also a translator as help.)


#3

I got it working :slight_smile: Seems like the issue was I wasn’t using the vertex shader correctly to pass the vertex info to the fragment shader.

For anyone else who might want to make gradient textures, here is the working code:

<script id="fragmentshader" type="x-shader/x-fragment">


  uniform vec3 color1;
  uniform vec3 color2;
  varying vec2 vUv;

  void main() {
    gl_FragColor = vec4(mix(color1, color2, vUv.y), 1.0);
  }


</script>
<script id="vertexshader" type="x-shader/x-vertex">

  varying vec2 vUv;
  void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
  }

</script>

var uniforms = {
  "color1" : {
  type : "c",
  value : new THREE.Color(getRandomColor())
  },
  "color2" : {
    type : "c",
    value : new THREE.Color(getRandomColor())
  }
};


var fShader = $('#fragmentshader');
var vShader = $('#vertexshader');


var geometry = new THREE.SphereGeometry(getRandomInt(100, 250), 20, 20);

var material = new THREE.ShaderMaterial({
  uniforms: uniforms,
  vertexShader: vShader.text(),
  fragmentShader: fShader.text()
});

var sphere = new THREE.Mesh(geometry, material);

#4

Well,

Seems like the issue was I wasn’t using the vertex shader correctly to pass the vertex info to the fragment shader.

It seems like you changed the whole purpose of the shader.

The first shader is supposed to do a screen space gradient by normalizing gl_FragCoord, which is in order of pixels on screen ( round floats , like vec2(1920.0,1080.0)), you instantiated an empty vector2 for the uniform, thus providing 0,0 for this normalization. This may even behave differently on different gpus, but it shot the number through the roof, and you got your yellow color (2nd argument).

You need to divide it with something that makes sense, so provide the width and height of your canvas element, call renderer.getSize(). While doing it, you should also do the inverse outside of the shader (something like 'uSize.set(1,1).divide(new THREE.Vector2(canvasWidth,canvasHeight)`. In the shader then you would have a multiplication instead of a division which is faster.

Your second shader is something else. It uses the mapping channel for the gradient. You may or may not have this attribute available, and you will have little control over it.