fatlinesofcode
fatlinesofcode

Reputation: 1667

How to create a mutli-step radial gradient Fragment shader

I'm using some shader code to create multi-step linear gradients by mixing colors. It works great. I have a radial shader which uses distance. Works fine but I dont know to adapt it to multi-step.

How could I approach it?

Linear Gradient http://glslsandbox.com/e#39992.0

uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;

void main( void ) {

    float y = gl_FragCoord.y / resolution.y;

    vec4 white = vec4(1.0, 1.0, 1.0, 1.0);
    vec4 red = vec4(1.0, 0.0, 0.0, 1.0);
    vec4 blue = vec4(0.0, 0.0, 1.0, 1.0);
    vec4 green = vec4(0.0, 1.0, 0.0, 1.0);
    float step1 = 0.0;
    float step2 = 0.33;
    float step3 = 0.66;
    float step4 = 1.0;

    vec4 color = mix(white, red, smoothstep(step1, step2, y));
    color = mix(color, blue, smoothstep(step2, step3, y));
    color = mix(color, green, smoothstep(step3, step4, y));

    gl_FragColor = color;

}

Simple radial gradient https://www.shadertoy.com/view/4tjSWh

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{

    float d = distance(iResolution.xy*0.5,fragCoord.xy)*(sin(1.0)+1.5)*0.003;
    fragColor = mix(vec4(1.0, 1.0, 1.0, 1.0), vec4(0.0, 0.0, 0.0, 1.0), d);
}

Upvotes: 3

Views: 7317

Answers (1)

Rabbid76
Rabbid76

Reputation: 211278

You simply have to calcualte the distance from the current fragment to the center of the viewport. For this you have to calculate the fragment position in normalized device coordinates. You can use the distance to the center in the same way, as you did it with the y coordinate in the "strip" shader:

vec2 pos_ndc = 2.0 * gl_FragCoord.xy / resolution.xy - 1.0;
float dist = length(pos_ndc);

Your shader should look somehow like this:

void main()
{
    vec2 pos_ndc = 2.0 * gl_FragCoord.xy / resolution.xy - 1.0;
    float dist = length(pos_ndc);

    vec4 white = vec4(1.0, 1.0, 1.0, 1.0);
    vec4 red = vec4(1.0, 0.0, 0.0, 1.0);
    vec4 blue = vec4(0.0, 0.0, 1.0, 1.0);
    vec4 green = vec4(0.0, 1.0, 0.0, 1.0);
    float step1 = 0.0;
    float step2 = 0.33;
    float step3 = 0.66;
    float step4 = 1.0;

    vec4 color = mix(white, red, smoothstep(step1, step2, dist));
    color = mix(color, blue, smoothstep(step2, step3, dist));
    color = mix(color, green, smoothstep(step3, step4, dist));

    gl_FragColor = color;
}

Preview:
radial gradient


See the code snippet:

var ShaderProgram = {};
  ShaderProgram.Create = function( shaderList ) {
      var shaderObjs = [];
      for ( var i_sh = 0; i_sh < shaderList.length; ++ i_sh ) {
          var shderObj = this.CompileShader( shaderList[i_sh].source, shaderList[i_sh].stage );
          if ( shderObj == 0 )
              return 0;
          shaderObjs.push( shderObj );
      }
      var progObj = this.LinkProgram( shaderObjs )
      if ( progObj != 0 ) {
          progObj.attribIndex = {};
          var noOfAttributes = gl.getProgramParameter( progObj, gl.ACTIVE_ATTRIBUTES );
          for ( var i_n = 0; i_n < noOfAttributes; ++ i_n ) {
              var name = gl.getActiveAttrib( progObj, i_n ).name;
              progObj.attribIndex[name] = gl.getAttribLocation( progObj, name );
          }
          progObj.unifomLocation = {};
          var noOfUniforms = gl.getProgramParameter( progObj, gl.ACTIVE_UNIFORMS );
          for ( var i_n = 0; i_n < noOfUniforms; ++ i_n ) {
              var name = gl.getActiveUniform( progObj, i_n ).name;
              progObj.unifomLocation[name] = gl.getUniformLocation( progObj, name );
          }
      }
      return progObj;
  }
  ShaderProgram.AttributeIndex = function( progObj, name ) { return progObj.attribIndex[name]; } 
  ShaderProgram.UniformLocation = function( progObj, name ) { return progObj.unifomLocation[name]; } 
  ShaderProgram.Use = function( progObj ) { gl.useProgram( progObj ); } 
  ShaderProgram.SetUniformI1  = function( progObj, name, val ) { if(progObj.unifomLocation[name]) gl.uniform1i( progObj.unifomLocation[name], val ); }
  ShaderProgram.SetUniformF1  = function( progObj, name, val ) { if(progObj.unifomLocation[name]) gl.uniform1f( progObj.unifomLocation[name], val ); }
  ShaderProgram.SetUniformF2  = function( progObj, name, arr ) { if(progObj.unifomLocation[name]) gl.uniform2fv( progObj.unifomLocation[name], arr ); }
  ShaderProgram.SetUniformF3  = function( progObj, name, arr ) { if(progObj.unifomLocation[name]) gl.uniform3fv( progObj.unifomLocation[name], arr ); }
  ShaderProgram.SetUniformF4  = function( progObj, name, arr ) { if(progObj.unifomLocation[name]) gl.uniform4fv( progObj.unifomLocation[name], arr ); }
  ShaderProgram.SetUniformM33 = function( progObj, name, mat ) { if(progObj.unifomLocation[name]) gl.uniformMatrix3fv( progObj.unifomLocation[name], false, mat ); }
  ShaderProgram.SetUniformM44 = function( progObj, name, mat ) { if(progObj.unifomLocation[name]) gl.uniformMatrix4fv( progObj.unifomLocation[name], false, mat ); }
  ShaderProgram.CompileShader = function( source, shaderStage ) {
      var shaderScript = document.getElementById(source);
      if (shaderScript) {
        source = "";
        var node = shaderScript.firstChild;
        while (node) {
          if (node.nodeType == 3) source += node.textContent;
          node = node.nextSibling;
        }
      }
      var shaderObj = gl.createShader( shaderStage );
      gl.shaderSource( shaderObj, source );
      gl.compileShader( shaderObj );
      var status = gl.getShaderParameter( shaderObj, gl.COMPILE_STATUS );
      if ( !status ) alert(gl.getShaderInfoLog(shaderObj));
      return status ? shaderObj : 0;
  } 
  ShaderProgram.LinkProgram = function( shaderObjs ) {
      var prog = gl.createProgram();
      for ( var i_sh = 0; i_sh < shaderObjs.length; ++ i_sh )
          gl.attachShader( prog, shaderObjs[i_sh] );
      gl.linkProgram( prog );
      status = gl.getProgramParameter( prog, gl.LINK_STATUS );
      if ( !status ) alert("Could not initialise shaders");
      gl.useProgram( null );
      return status ? prog : 0;
  }
          
  function drawScene(){
  
      var canvas = document.getElementById( "ogl-canvas" );
      var vp = [canvas.width, canvas.height];
      
      gl.viewport( 0, 0, canvas.width, canvas.height );
      gl.enable( gl.DEPTH_TEST );
      gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
      gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
      ShaderProgram.Use( progDraw );
      ShaderProgram.SetUniformF2( progDraw, "resolution", [256.0, 256.0] )
      gl.enableVertexAttribArray( progDraw.inPos );
      gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.pos );
      gl.vertexAttribPointer( progDraw.inPos, 2, gl.FLOAT, false, 0, 0 ); 
      gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufObj.inx );
      gl.drawElements( gl.TRIANGLES, bufObj.inx.len, gl.UNSIGNED_SHORT, 0 );
      gl.disableVertexAttribArray( progDraw.pos );
  }  
  
  var gl;
  var prog;
  var bufObj = {};
  function sceneStart() {
  
      var canvas = document.getElementById( "ogl-canvas");
      gl = canvas.getContext( "experimental-webgl" );
      if ( !gl )
        return;
  
      progDraw = ShaderProgram.Create( 
        [ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
          { source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
        ] );
      progDraw.inPos = gl.getAttribLocation( progDraw, "inPos" );
      if ( prog == 0 )
          return;
  
      var pos = [ -1, -1, 1, -1, 1, 1, -1, 1 ];
      var inx = [ 0, 1, 2, 0, 2, 3 ];
      bufObj.pos = gl.createBuffer();
      gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.pos );
      gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( pos ), gl.STATIC_DRAW );
      bufObj.inx = gl.createBuffer();
      bufObj.inx.len = inx.length;
      gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufObj.inx );
      gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( inx ), gl.STATIC_DRAW );
  
      setInterval(drawScene, 50);
  }
<script id="draw-shader-vs" type="x-shader/x-vertex">
  precision mediump float;
  
  attribute vec2 inPos;
  
  varying vec2 vertPos;
  
  void main()
  {
      vertPos = inPos;
      gl_Position = vec4( inPos.xy, 0.0, 1.0 );
  }
  </script>
  
  <script id="draw-shader-fs" type="x-shader/x-fragment">
  precision mediump float;
  
  varying vec2 vertPos;
  uniform vec2 resolution;


    void main()
    {
      vec2 pos_ndc = 2.0 * gl_FragCoord.xy / resolution.xy - 1.0;
      float dist = length(pos_ndc);
      
      vec4 white = vec4(1.0, 1.0, 1.0, 1.0);
      vec4 red = vec4(1.0, 0.0, 0.0, 1.0);
      vec4 blue = vec4(0.0, 0.0, 1.0, 1.0);
      vec4 green = vec4(0.0, 1.0, 0.0, 1.0);
      float step1 = 0.0;
      float step2 = 0.33;
      float step3 = 0.66;
      float step4 = 1.0;

      vec4 color = mix(white, red, smoothstep(step1, step2, dist));
      color = mix(color, blue, smoothstep(step2, step3, dist));
      color = mix(color, green, smoothstep(step3, step4, dist));

      gl_FragColor = color;
    }
  </script>
  
  <body onload="sceneStart();">
      <canvas id="ogl-canvas" style="border: none;" width="256" height="256"></canvas>
  </body>

Upvotes: 4

Related Questions