代码之家  ›  专栏  ›  技术社区  ›  Bahadır

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

  •  12
  • Bahadır  · 技术社区  · 7 年前

    我试图像设计应用程序(例如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 回复  |  直到 7 年前
        1
  •  23
  •   gman    3 年前

    为了回答问题的标题,您可能还想考虑在其他颜色空间中进行混合。您的代码在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{边框:1px实心;边距:2px;}
    span{display:block;}
    div{显示:内联块;文本对齐:居中;}
    <脚本src=”https://twgljs.org/dist/4.x/twgl.min.js“>lt;/script>

    注:一维256x1纹理是Chrome、Firefox和Android渲染线性和径向渐变的方式。 See src

        2
  •  6
  •   Rabbid76    7 年前

    设置颜色值时有一个简单的错误。设置橙色时,您使用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


    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>