在片段着色器中创建渐变颜色

时间:2017-11-19 11:51:05

标签: glsl webgl fragment-shader

我试图像设计应用程序(例如Photoshop)那样尝试制作渐变色,但却无法获得我想要的确切结果。

我的着色器创造了非常漂亮的渐变效果'但也包含与我想要切换的颜色不同的其他颜色。

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

这是我的片段着色器 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);
}

这是comparison of the results in shader and design apps

提前致谢..

2 个答案:

答案 0 :(得分:5)

在回答问题的标题时,您可能还想考虑在其他颜色空间中进行混音。您的代码在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>
&#13;
&#13;
&#13;

我还建议在渐变纹理中传递颜色。 Nx1纹理并使用

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

然后您可以轻松地混合2种颜色,3种颜色,20种颜色。您也可以通过重复颜色等来区分颜色。

示例:

&#13;
&#13;
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");
&#13;
img { border: 1px solid black; margin: 2px; }
span { display: block; }
div { display: inline-block; text-align: center; }
&#13;
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
&#13;
&#13;
&#13;

答案 1 :(得分:3)

设置颜色值时,Ther是一个简单的错误。当您设置橙色时,使用1.9作为红色通道的值而不是1.0。

将您的代码更改为:

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

请注意,最终的颜色通道被固定为[0,1],但由于您使用mix来插值颜色,因此大于1.0的颜色通道会提升渐变中颜色的部分。


预览:

enter image description here


&#13;
&#13;
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);
  }
&#13;
<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>
&#13;
&#13;
&#13;