在Fragment Shader中创建梯度颜色[英] Creating a Gradient Color in Fragment Shader

本文是小编为大家收集整理的关于在Fragment Shader中创建梯度颜色的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我试图像设计应用程序一样实现梯度颜色(例如Photoshop),但无法获得我想要的确切结果.

我的着色器创造了非常不错的"渐变",但还包含其他与我要切换的颜色不同的颜色.

看起来不错,但是,我的目标是稍后添加混合功能,并制作一种色彩更正着色器.但是首先我必须获得正确的颜色.

这是我的片段着色器 http://player.thebookofshaders.com/?log = 1711191111216

uniform vec2 u_resolution;

void main() {

  vec2 st = gl_FragCoord.xy/u_resolution.xy;

  vec3 color1 = vec3(1.9,0.55,0);
  vec3 color2 = vec3(0.226,0.000,0.615);

  float mixValue = distance(st,vec2(0,1));
  vec3 color = mix(color1,color2,mixValue);

  gl_FragColor = vec4(color,mixValue);
}

这是 着色器和设计应用中的结果比较

预先感谢..

推荐答案

设置颜色值时,这是一个简单的错误.当您设置橙色时,您将1.9用于红色通道的值,而不是1.0.

将您的代码更改为:

vec3 color1 = vec3(1.0, 0.55, 0.0); // 1.0 insted of 1.9

注意,最终的颜色通道被夹紧到[0,1],但是由于您使用mix插入颜色,因此颜色通道高于1.0,将渐变的部分提高了渐变.


预览:

在此处输入图像说明


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.SetUniformF2  = function( progObj, name, arr ) { if(progObj.unifomLocation[name]) gl.uniform2fv( progObj.unifomLocation[name], arr ); }
  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, "u_resolution", vp )
      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", { premultipliedAlpha: true } );
      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 u_resolution;


void main()
{
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
  
    vec3 color1 = vec3(1.0,0.55,0);
    vec3 color2 = vec3(0.226,0.000,0.615);
  
    float mixValue = distance(st,vec2(0,1));
    
    vec3 color = mix(color1,color2,mixValue);
    
    gl_FragColor = vec4(color,1.0);
}
</script>

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

其他推荐答案

仅回应您的问题标题,您可能还需要考虑在其他颜色空间中进行混合.您的代码在RGB空间中混合,但您将在不同的空间中获得不同的结果.

示例

const gl = document.createElement("canvas").getContext("webgl");
gl.canvas.width = 100;
gl.canvas.height = 100;
gl.viewport(0, 0, 100, 100);

const vsrc = `
void main() {
 gl_PointSize = 100.0;
 gl_Position = vec4(0, 0, 0, 1);
}
`;
const fRGB = `
precision mediump float;

uniform vec3 color1;
uniform vec3 color2;

void main() {
  vec2 st = gl_PointCoord;
  float mixValue = distance(st, vec2(0, 1));

  vec3 color = mix(color1, color2, mixValue);
    
  gl_FragColor = vec4(color, 1);
}
`;
const fHSV = `
precision mediump float;

uniform vec3 color1;
uniform vec3 color2;

// from: http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl

vec3 rgb2hsv(vec3 c) {
  vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
  vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
  vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));

  float d = q.x - min(q.w, q.y);
  float e = 1.0e-10;
  return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

vec3 hsv2rgb(vec3 c) {
  c = vec3(c.x, clamp(c.yz, 0.0, 1.0));
  vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
  vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
  return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

void main() {
  vec2 st = gl_PointCoord;
  float mixValue = distance(st, vec2(0, 1));

  vec3 hsv1 = rgb2hsv(color1);
  vec3 hsv2 = rgb2hsv(color2);
  
  // mix hue in toward closest direction
  float hue = (mod(mod((hsv2.x - hsv1.x), 1.) + 1.5, 1.) - 0.5) * mixValue + hsv1.x;
  vec3 hsv = vec3(hue, mix(hsv1.yz, hsv2.yz, mixValue));
  
  vec3 color = hsv2rgb(hsv);
    
  gl_FragColor = vec4(color, 1);
}

`;

const fHSL = `
precision mediump float;

uniform vec3 color1;
uniform vec3 color2;

const float Epsilon = 1e-10;

vec3 rgb2hcv(in vec3 RGB)
{
  // Based on work by Sam Hocevar and Emil Persson
  vec4 P = lerp(vec4(RGB.bg, -1.0, 2.0/3.0), vec4(RGB.gb, 0.0, -1.0/3.0), step(RGB.b, RGB.g));
  vec4 Q = mix(vec4(P.xyw, RGB.r), vec4(RGB.r, P.yzx), step(P.x, RGB.r));
  float C = Q.x - min(Q.w, Q.y);
  float H = abs((Q.w - Q.y) / (6. * C + Epsilon) + Q.z);
  return vec3(H, C, Q.x);
}

vec3 rgb2hsl(in vec3 RGB)
{
  vec3 HCV = rgb2hcv(RGB);
  float L = HCV.z - HCV.y * 0.5;
  float S = HCV.y / (1 - abs(L * 2. - 1.) + Epsilon);
  return vec3(HCV.x, S, L);
}

vec3 hsl2rgb(vec3 c)
{
  c = vec3(fract(c.x), clamp(c.yz, 0.0, 1.0));
  vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
  return c.z + c.y * (rgb - 0.5) * (1.0 - abs(2.0 * c.z - 1.0));
}

void main() {
  vec2 st = gl_PointCoord;
  float mixValue = distance(st, vec2(0, 1));

  vec3 hsl1 = rgb2hsl(color1);
  vec3 hsl2 = rgb2hsl(color2);
  
  // mix hue in toward closest direction
  float hue = (mod(mod((hsl2.x - hsl1.x), 1.) + 1.5, 1.) - 0.5) * mixValue + hsl1.x;
  vec3 hsl = vec3(hue, mix(hsl1.yz, hsl2.yz, mixValue));

  vec3 color = hsl2rgb(hsv);
    
  gl_FragColor = vec4(color, 1);
}
`;

const fLAB = `
precision mediump float;

uniform vec3 color1;
uniform vec3 color2;

// from: https://code.google.com/archive/p/flowabs/source

vec3 rgb2xyz( vec3 c ) {
    vec3 tmp;
    tmp.x = ( c.r > 0.04045 ) ? pow( ( c.r + 0.055 ) / 1.055, 2.4 ) : c.r / 12.92;
    tmp.y = ( c.g > 0.04045 ) ? pow( ( c.g + 0.055 ) / 1.055, 2.4 ) : c.g / 12.92,
    tmp.z = ( c.b > 0.04045 ) ? pow( ( c.b + 0.055 ) / 1.055, 2.4 ) : c.b / 12.92;
    return 100.0 * tmp *
        mat3( 0.4124, 0.3576, 0.1805,
              0.2126, 0.7152, 0.0722,
              0.0193, 0.1192, 0.9505 );
}

vec3 xyz2lab( vec3 c ) {
    vec3 n = c / vec3( 95.047, 100, 108.883 );
    vec3 v;
    v.x = ( n.x > 0.008856 ) ? pow( n.x, 1.0 / 3.0 ) : ( 7.787 * n.x ) + ( 16.0 / 116.0 );
    v.y = ( n.y > 0.008856 ) ? pow( n.y, 1.0 / 3.0 ) : ( 7.787 * n.y ) + ( 16.0 / 116.0 );
    v.z = ( n.z > 0.008856 ) ? pow( n.z, 1.0 / 3.0 ) : ( 7.787 * n.z ) + ( 16.0 / 116.0 );
    return vec3(( 116.0 * v.y ) - 16.0, 500.0 * ( v.x - v.y ), 200.0 * ( v.y - v.z ));
}

vec3 rgb2lab(vec3 c) {
    vec3 lab = xyz2lab( rgb2xyz( c ) );
    return vec3( lab.x / 100.0, 0.5 + 0.5 * ( lab.y / 127.0 ), 0.5 + 0.5 * ( lab.z / 127.0 ));
}


vec3 lab2xyz( vec3 c ) {
    float fy = ( c.x + 16.0 ) / 116.0;
    float fx = c.y / 500.0 + fy;
    float fz = fy - c.z / 200.0;
    return vec3(
         95.047 * (( fx > 0.206897 ) ? fx * fx * fx : ( fx - 16.0 / 116.0 ) / 7.787),
        100.000 * (( fy > 0.206897 ) ? fy * fy * fy : ( fy - 16.0 / 116.0 ) / 7.787),
        108.883 * (( fz > 0.206897 ) ? fz * fz * fz : ( fz - 16.0 / 116.0 ) / 7.787)
    );
}

vec3 xyz2rgb( vec3 c ) {
    vec3 v =  c / 100.0 * mat3( 
        3.2406, -1.5372, -0.4986,
        -0.9689, 1.8758, 0.0415,
        0.0557, -0.2040, 1.0570
    );
    vec3 r;
    r.x = ( v.r > 0.0031308 ) ? (( 1.055 * pow( v.r, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.r;
    r.y = ( v.g > 0.0031308 ) ? (( 1.055 * pow( v.g, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.g;
    r.z = ( v.b > 0.0031308 ) ? (( 1.055 * pow( v.b, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.b;
    return r;
}

vec3 lab2rgb(vec3 c) {
    return xyz2rgb( lab2xyz( vec3(100.0 * c.x, 2.0 * 127.0 * (c.y - 0.5), 2.0 * 127.0 * (c.z - 0.5)) ) );
}

void main() {
  vec2 st = gl_PointCoord;
  float mixValue = distance(st, vec2(0, 1));

  vec3 lab1 = rgb2lab(color1);
  vec3 lab2 = rgb2lab(color2);
    
  vec3 lab = mix(lab1, lab2, mixValue);
  
  vec3 color = lab2rgb(lab);
    
  gl_FragColor = vec4(color, 1);
}
`;

function draw(gl, shaders, color1, color2, label) {
  const programInfo = twgl.createProgramInfo(gl, shaders);
  gl.useProgram(programInfo.program);
  twgl.setUniforms(programInfo, {
    color1: color1,
    color2: color2,
  });
  gl.drawArrays(gl.POINTS, 0, 1);
  const div = document.createElement("div");
  const img = new Image();
  img.src = gl.canvas.toDataURL();
  div.appendChild(img);
  const inner = document.createElement("span");
  inner.textContent = label;
  div.appendChild(inner);
  document.body.appendChild(div);
}

const color1 = [1.0, 0.55, 0];
const color2 = [0.226, 0.000, 0.615];

draw(gl, [vsrc, fRGB], color1, color2, "rgb");
draw(gl, [vsrc, fHSV], color1, color2, "hsv");
draw(gl, [vsrc, fHSV], color1, color2, "hsl");
draw(gl, [vsrc, fLAB], color1, color2, "lab");
img { border: 1px solid black; margin: 2px; }
span { display: block; }
div { display: inline-block; text-align: center; }
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

我还建议将您的颜色传递到坡道纹理中. NX1纹理并使用

color = texture2D(
    rampTexture, 
    vec2((mixValue * (rampWidth - 1.) + .5) / rampWidth, 0.5)).rgb;

然后,您可以轻松地融合2种颜色,3种颜色,20种颜色.您也可以通过重复颜色等来摆脱颜色.

示例:

const gl = document.createElement("canvas").getContext("webgl");
gl.canvas.width = 100;
gl.canvas.height = 100;
gl.viewport(0, 0, 100, 100);

const vsrc = `
void main() {
 gl_PointSize = 100.0;
 gl_Position = vec4(0, 0, 0, 1);
}
`;
const fsrc = `
precision mediump float;

uniform sampler2D rampTexture;
uniform float rampWidth;

void main() {
  vec2 st = gl_PointCoord;
  float mixValue = distance(st, vec2(0, 1));

  vec3 color = texture2D(
    rampTexture, 
    vec2((mixValue * (rampWidth - 1.) + .5) / rampWidth, 0.5)).rgb;
    
  gl_FragColor = vec4(color, 1);
}
`;

const programInfo = twgl.createProgramInfo(gl, [vsrc, fsrc]);
gl.useProgram(programInfo.program);

const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

function draw(gl, ramp, label) {
  const width = ramp.length;
  gl.bindTexture(gl.TEXTURE_2D, tex);
  const level = 0;
  const internalFormat = gl.RGB;
  const height = 1;
  const border = 0;
  const format = gl.RGB;
  const type = gl.UNSIGNED_BYTE;
  const rampData = new Uint8Array([].concat(...ramp).map(v => v * 255));
  gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border,
                format, type, rampData);
  twgl.setUniforms(programInfo, {
    rampTexture: tex,
    rampWidth: width,
  });
  gl.drawArrays(gl.POINTS, 0, 1);
  const div = document.createElement("div");
  const img = new Image();
  img.src = gl.canvas.toDataURL();
  div.appendChild(img);
  const inner = document.createElement("span");
  inner.textContent = label;
  div.appendChild(inner);
  document.body.appendChild(div);
}

const color1 = [1.0, 0.55, 0];
const color2 = [0.226, 0.000, 0.615];
const r = [1, 0, 0];
const g = [0, 1, 0];
const b = [0, 0, 1];
const w = [1, 1, 1];

draw(gl, [color1, color2], "color1->color2");
draw(gl, [r, g], "red->green");
draw(gl, [r, g, b], "r->g->b");
draw(gl, [r, b, r, b, r], "r->b->r->b->r");
draw(gl, [g, b, b, b, g], "g->b->b->b->g");
img { border: 1px solid black; margin: 2px; }
span { display: block; }
div { display: inline-block; text-align: center; }
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

注意:1维256x1纹理是铬,firefox和Android如何线性和径向梯度. 本文地址:https://www.itbaoku.cn/post/2091092.html

问题描述

I'm trying to achieve making a gradient color as the design apps (Photoshop for example) does, but can't get the exact result i want.

My shader creates very nice 'gradients' but also contains other colors that are different from the colors I want to switch in.

It looks nice but, my aim is adding blending functions later and make a kind of color correction shader. but first of all I have to get the right colors.

Here is my fragment shader http://player.thebookofshaders.com/?log=171119111216

uniform vec2 u_resolution;

void main() {

  vec2 st = gl_FragCoord.xy/u_resolution.xy;

  vec3 color1 = vec3(1.9,0.55,0);
  vec3 color2 = vec3(0.226,0.000,0.615);

  float mixValue = distance(st,vec2(0,1));
  vec3 color = mix(color1,color2,mixValue);

  gl_FragColor = vec4(color,mixValue);
}

And here is comparison of the results in shader and design apps

Thanks in advance..

推荐答案

Ther is a simple mistake when you set up the color value. You used 1.9 for the value of red color channel instead of 1.0, when you set up the orange color.

Change your code to:

vec3 color1 = vec3(1.0, 0.55, 0.0); // 1.0 insted of 1.9

Note, the final color channels are clamped to [0, 1], but since you use mix to interpolate the colors, a color channel above 1.0 raises the part of the color in the gradient.


Preview:

enter image description here


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.SetUniformF2  = function( progObj, name, arr ) { if(progObj.unifomLocation[name]) gl.uniform2fv( progObj.unifomLocation[name], arr ); }
  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, "u_resolution", vp )
      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", { premultipliedAlpha: true } );
      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 u_resolution;


void main()
{
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
  
    vec3 color1 = vec3(1.0,0.55,0);
    vec3 color2 = vec3(0.226,0.000,0.615);
  
    float mixValue = distance(st,vec2(0,1));
    
    vec3 color = mix(color1,color2,mixValue);
    
    gl_FragColor = vec4(color,1.0);
}
</script>

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

其他推荐答案

In response to just the title of your question you might also want to consider doing the mix in other color spaces. Your code is mixing in RGB space but you'll get different results in different spaces.

Example

const gl = document.createElement("canvas").getContext("webgl");
gl.canvas.width = 100;
gl.canvas.height = 100;
gl.viewport(0, 0, 100, 100);

const vsrc = `
void main() {
 gl_PointSize = 100.0;
 gl_Position = vec4(0, 0, 0, 1);
}
`;
const fRGB = `
precision mediump float;

uniform vec3 color1;
uniform vec3 color2;

void main() {
  vec2 st = gl_PointCoord;
  float mixValue = distance(st, vec2(0, 1));

  vec3 color = mix(color1, color2, mixValue);
    
  gl_FragColor = vec4(color, 1);
}
`;
const fHSV = `
precision mediump float;

uniform vec3 color1;
uniform vec3 color2;

// from: http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl

vec3 rgb2hsv(vec3 c) {
  vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
  vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
  vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));

  float d = q.x - min(q.w, q.y);
  float e = 1.0e-10;
  return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

vec3 hsv2rgb(vec3 c) {
  c = vec3(c.x, clamp(c.yz, 0.0, 1.0));
  vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
  vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
  return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

void main() {
  vec2 st = gl_PointCoord;
  float mixValue = distance(st, vec2(0, 1));

  vec3 hsv1 = rgb2hsv(color1);
  vec3 hsv2 = rgb2hsv(color2);
  
  // mix hue in toward closest direction
  float hue = (mod(mod((hsv2.x - hsv1.x), 1.) + 1.5, 1.) - 0.5) * mixValue + hsv1.x;
  vec3 hsv = vec3(hue, mix(hsv1.yz, hsv2.yz, mixValue));
  
  vec3 color = hsv2rgb(hsv);
    
  gl_FragColor = vec4(color, 1);
}

`;

const fHSL = `
precision mediump float;

uniform vec3 color1;
uniform vec3 color2;

const float Epsilon = 1e-10;

vec3 rgb2hcv(in vec3 RGB)
{
  // Based on work by Sam Hocevar and Emil Persson
  vec4 P = lerp(vec4(RGB.bg, -1.0, 2.0/3.0), vec4(RGB.gb, 0.0, -1.0/3.0), step(RGB.b, RGB.g));
  vec4 Q = mix(vec4(P.xyw, RGB.r), vec4(RGB.r, P.yzx), step(P.x, RGB.r));
  float C = Q.x - min(Q.w, Q.y);
  float H = abs((Q.w - Q.y) / (6. * C + Epsilon) + Q.z);
  return vec3(H, C, Q.x);
}

vec3 rgb2hsl(in vec3 RGB)
{
  vec3 HCV = rgb2hcv(RGB);
  float L = HCV.z - HCV.y * 0.5;
  float S = HCV.y / (1 - abs(L * 2. - 1.) + Epsilon);
  return vec3(HCV.x, S, L);
}

vec3 hsl2rgb(vec3 c)
{
  c = vec3(fract(c.x), clamp(c.yz, 0.0, 1.0));
  vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
  return c.z + c.y * (rgb - 0.5) * (1.0 - abs(2.0 * c.z - 1.0));
}

void main() {
  vec2 st = gl_PointCoord;
  float mixValue = distance(st, vec2(0, 1));

  vec3 hsl1 = rgb2hsl(color1);
  vec3 hsl2 = rgb2hsl(color2);
  
  // mix hue in toward closest direction
  float hue = (mod(mod((hsl2.x - hsl1.x), 1.) + 1.5, 1.) - 0.5) * mixValue + hsl1.x;
  vec3 hsl = vec3(hue, mix(hsl1.yz, hsl2.yz, mixValue));

  vec3 color = hsl2rgb(hsv);
    
  gl_FragColor = vec4(color, 1);
}
`;

const fLAB = `
precision mediump float;

uniform vec3 color1;
uniform vec3 color2;

// from: https://code.google.com/archive/p/flowabs/source

vec3 rgb2xyz( vec3 c ) {
    vec3 tmp;
    tmp.x = ( c.r > 0.04045 ) ? pow( ( c.r + 0.055 ) / 1.055, 2.4 ) : c.r / 12.92;
    tmp.y = ( c.g > 0.04045 ) ? pow( ( c.g + 0.055 ) / 1.055, 2.4 ) : c.g / 12.92,
    tmp.z = ( c.b > 0.04045 ) ? pow( ( c.b + 0.055 ) / 1.055, 2.4 ) : c.b / 12.92;
    return 100.0 * tmp *
        mat3( 0.4124, 0.3576, 0.1805,
              0.2126, 0.7152, 0.0722,
              0.0193, 0.1192, 0.9505 );
}

vec3 xyz2lab( vec3 c ) {
    vec3 n = c / vec3( 95.047, 100, 108.883 );
    vec3 v;
    v.x = ( n.x > 0.008856 ) ? pow( n.x, 1.0 / 3.0 ) : ( 7.787 * n.x ) + ( 16.0 / 116.0 );
    v.y = ( n.y > 0.008856 ) ? pow( n.y, 1.0 / 3.0 ) : ( 7.787 * n.y ) + ( 16.0 / 116.0 );
    v.z = ( n.z > 0.008856 ) ? pow( n.z, 1.0 / 3.0 ) : ( 7.787 * n.z ) + ( 16.0 / 116.0 );
    return vec3(( 116.0 * v.y ) - 16.0, 500.0 * ( v.x - v.y ), 200.0 * ( v.y - v.z ));
}

vec3 rgb2lab(vec3 c) {
    vec3 lab = xyz2lab( rgb2xyz( c ) );
    return vec3( lab.x / 100.0, 0.5 + 0.5 * ( lab.y / 127.0 ), 0.5 + 0.5 * ( lab.z / 127.0 ));
}


vec3 lab2xyz( vec3 c ) {
    float fy = ( c.x + 16.0 ) / 116.0;
    float fx = c.y / 500.0 + fy;
    float fz = fy - c.z / 200.0;
    return vec3(
         95.047 * (( fx > 0.206897 ) ? fx * fx * fx : ( fx - 16.0 / 116.0 ) / 7.787),
        100.000 * (( fy > 0.206897 ) ? fy * fy * fy : ( fy - 16.0 / 116.0 ) / 7.787),
        108.883 * (( fz > 0.206897 ) ? fz * fz * fz : ( fz - 16.0 / 116.0 ) / 7.787)
    );
}

vec3 xyz2rgb( vec3 c ) {
    vec3 v =  c / 100.0 * mat3( 
        3.2406, -1.5372, -0.4986,
        -0.9689, 1.8758, 0.0415,
        0.0557, -0.2040, 1.0570
    );
    vec3 r;
    r.x = ( v.r > 0.0031308 ) ? (( 1.055 * pow( v.r, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.r;
    r.y = ( v.g > 0.0031308 ) ? (( 1.055 * pow( v.g, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.g;
    r.z = ( v.b > 0.0031308 ) ? (( 1.055 * pow( v.b, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.b;
    return r;
}

vec3 lab2rgb(vec3 c) {
    return xyz2rgb( lab2xyz( vec3(100.0 * c.x, 2.0 * 127.0 * (c.y - 0.5), 2.0 * 127.0 * (c.z - 0.5)) ) );
}

void main() {
  vec2 st = gl_PointCoord;
  float mixValue = distance(st, vec2(0, 1));

  vec3 lab1 = rgb2lab(color1);
  vec3 lab2 = rgb2lab(color2);
    
  vec3 lab = mix(lab1, lab2, mixValue);
  
  vec3 color = lab2rgb(lab);
    
  gl_FragColor = vec4(color, 1);
}
`;

function draw(gl, shaders, color1, color2, label) {
  const programInfo = twgl.createProgramInfo(gl, shaders);
  gl.useProgram(programInfo.program);
  twgl.setUniforms(programInfo, {
    color1: color1,
    color2: color2,
  });
  gl.drawArrays(gl.POINTS, 0, 1);
  const div = document.createElement("div");
  const img = new Image();
  img.src = gl.canvas.toDataURL();
  div.appendChild(img);
  const inner = document.createElement("span");
  inner.textContent = label;
  div.appendChild(inner);
  document.body.appendChild(div);
}

const color1 = [1.0, 0.55, 0];
const color2 = [0.226, 0.000, 0.615];

draw(gl, [vsrc, fRGB], color1, color2, "rgb");
draw(gl, [vsrc, fHSV], color1, color2, "hsv");
draw(gl, [vsrc, fHSV], color1, color2, "hsl");
draw(gl, [vsrc, fLAB], color1, color2, "lab");
img { border: 1px solid black; margin: 2px; }
span { display: block; }
div { display: inline-block; text-align: center; }
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

I would also suggest passing your colors in in a ramp texture. An Nx1 texture and using

color = texture2D(
    rampTexture, 
    vec2((mixValue * (rampWidth - 1.) + .5) / rampWidth, 0.5)).rgb;

Then you can easily blend across 2 colors, 3 colors, 20 colors. You can space out the colors as well by repeating colors etc..

Example:

const gl = document.createElement("canvas").getContext("webgl");
gl.canvas.width = 100;
gl.canvas.height = 100;
gl.viewport(0, 0, 100, 100);

const vsrc = `
void main() {
 gl_PointSize = 100.0;
 gl_Position = vec4(0, 0, 0, 1);
}
`;
const fsrc = `
precision mediump float;

uniform sampler2D rampTexture;
uniform float rampWidth;

void main() {
  vec2 st = gl_PointCoord;
  float mixValue = distance(st, vec2(0, 1));

  vec3 color = texture2D(
    rampTexture, 
    vec2((mixValue * (rampWidth - 1.) + .5) / rampWidth, 0.5)).rgb;
    
  gl_FragColor = vec4(color, 1);
}
`;

const programInfo = twgl.createProgramInfo(gl, [vsrc, fsrc]);
gl.useProgram(programInfo.program);

const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

function draw(gl, ramp, label) {
  const width = ramp.length;
  gl.bindTexture(gl.TEXTURE_2D, tex);
  const level = 0;
  const internalFormat = gl.RGB;
  const height = 1;
  const border = 0;
  const format = gl.RGB;
  const type = gl.UNSIGNED_BYTE;
  const rampData = new Uint8Array([].concat(...ramp).map(v => v * 255));
  gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border,
                format, type, rampData);
  twgl.setUniforms(programInfo, {
    rampTexture: tex,
    rampWidth: width,
  });
  gl.drawArrays(gl.POINTS, 0, 1);
  const div = document.createElement("div");
  const img = new Image();
  img.src = gl.canvas.toDataURL();
  div.appendChild(img);
  const inner = document.createElement("span");
  inner.textContent = label;
  div.appendChild(inner);
  document.body.appendChild(div);
}

const color1 = [1.0, 0.55, 0];
const color2 = [0.226, 0.000, 0.615];
const r = [1, 0, 0];
const g = [0, 1, 0];
const b = [0, 0, 1];
const w = [1, 1, 1];

draw(gl, [color1, color2], "color1->color2");
draw(gl, [r, g], "red->green");
draw(gl, [r, g, b], "r->g->b");
draw(gl, [r, b, r, b, r], "r->b->r->b->r");
draw(gl, [g, b, b, b, g], "g->b->b->b->g");
img { border: 1px solid black; margin: 2px; }
span { display: block; }
div { display: inline-block; text-align: center; }
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

Note: a 1 dimensional 256x1 texture is how Chrome, Firefox, and Android render both linear and radial gradients. See src