着色器中的矩形/正方形渐变

问题描述

我的第一个问题已经结束。从那时起,我添加了一些代码并取得了一些进展。但是,生成的渐变是圆形的,我目前不知道如何将其转换为正方形。

这是我当前的结果:

目标结果(以及以下几行):

这是我的片段着色器:

precision highp float;
        varying vec2 vTextureCoord;
        uniform float centerX;
        uniform float centerY;
        uniform vec4 colors[4];
        uniform float steps[4];

        void main() {
          vec3 map = vec3(vTextureCoord,1);
          vec2 uv = map.xy;

          float dist = distance(vTextureCoord,vec2(centerX,centerY));
          highp vec4 col = colors[0];
          for (int i = 1; i < 4; ++i) {
              col = mix(col,colors[i],smoothstep(steps[i - 1],steps[i],dist));
          }
          float factor = max(abs(uv.x - centerX),abs(uv.y - centerY));
          float c = 1. - max(abs(uv.x - centerX),abs(uv.y - centerY));
          vec4 finalColor = vec4((col.r - factor),(col.g - factor),(col.b - factor),1.);

          gl_FragColor = finalColor;
        }

传递的参数是:

颜色:[[1、0、0],[1、1、1],[1、0、0],[0、1、0]]

步骤:[0,0.29,0.35,1]

解决方法

可以通过计算两个轴的最大距离来获得方形梯度:

float dist = distance(vTextureCoord,vec2(centerX,centerY));

vec2 distV = vTextureCoord - vec2(centerX,centerY);
float dist = max(abs(distV.x),abs(distV.y));

完整示例:

(function loadscene() {    

var canvas,gl,vp_size,prog,bufObj = {};

function initScene() {

    canvas = document.getElementById( "ogl-canvas");
    gl = canvas.getContext( "experimental-webgl" );
    if ( !gl )
      return;

    progDraw = gl.createProgram();
    for (let i = 0; i < 2; ++i) {
        let source = document.getElementById(i==0 ? "draw-shader-vs" : "draw-shader-fs").text;
        let shaderObj = gl.createShader(i==0 ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER);
        gl.shaderSource(shaderObj,source);
        gl.compileShader(shaderObj);
        let status = gl.getShaderParameter(shaderObj,gl.COMPILE_STATUS);
        if (!status) alert(gl.getShaderInfoLog(shaderObj));
        gl.attachShader(progDraw,shaderObj);
        gl.linkProgram(progDraw);
    }
    status = gl.getProgramParameter(progDraw,gl.LINK_STATUS);
    if ( !status ) alert(gl.getProgramInfoLog(progDraw));
    progDraw.inPos = gl.getAttribLocation(progDraw,"inPos");
    progDraw.u_time = gl.getUniformLocation(progDraw,"u_time");
    progDraw.u_resolution = gl.getUniformLocation(progDraw,"u_resolution");
    gl.useProgram(progDraw);

    var pos = [ -1,-1,1,1 ];
    var inx = [ 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 );
    gl.enableVertexAttribArray( progDraw.inPos );
    gl.vertexAttribPointer( progDraw.inPos,gl.FLOAT,false,0 ); 
    
    gl.enable( gl.DEPTH_TEST );
    gl.clearColor( 0.0,0.0,1.0 );

    window.onresize = resize;
    resize();
    requestAnimationFrame(render);
}

function resize() {
    //vp_size = [gl.drawingBufferWidth,gl.drawingBufferHeight];
    vp_size = [window.innerWidth,window.innerHeight];
    //vp_size = [256,256]
    canvas.width = vp_size[0];
    canvas.height = vp_size[1];
}

function render(deltaMS) {

    gl.viewport( 0,canvas.width,canvas.height );
    gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
   
    gl.uniform1f(progDraw.u_time,deltaMS/1000.0);
    gl.uniform2f(progDraw.u_resolution,canvas.height);
    gl.drawElements( gl.TRIANGLES,bufObj.inx.len,gl.UNSIGNED_SHORT,0 );
    
    requestAnimationFrame(render);
}  

initScene();

})();
<script id="draw-shader-vs" type="x-shader/x-vertex">
#version 100

attribute vec2 inPos;
varying vec2 ndcPos;

void main()
{
    ndcPos = inPos;
    gl_Position = vec4( inPos.xy,1.0 );
}
</script>

<script id="draw-shader-fs" type="x-shader/x-fragment">
precision mediump float;

varying vec2 ndcPos;  // normaliced device coordinates in range [-1.0,1.0]
uniform float u_time;
uniform vec2 u_resolution;

#define FILL

void main()
{
    vec4 colors[4];
    colors[0] = vec4(1.0,1.0);
    colors[1] = vec4(0.0,1.0,1.0);
    colors[2] = vec4(0.0,1.0);
    colors[3] = vec4(1.0,1.0);

    float steps[4];
    steps[0] = 0.2;
    steps[1] = 0.4;
    steps[2] = 0.6;
    steps[3] = 0.8;

    vec2 uv = ndcPos.xy;
    uv.x *= u_resolution.x / u_resolution.y;
    
    vec2 vTextureCoord = uv;
    float centerX = 0.0;
    float centerY = 0.0;

    //float dist = distance(vTextureCoord,centerY));
    vec2 distV = vTextureCoord - vec2(centerX,centerY);
    float dist = max(abs(distV.x),abs(distV.y));
    
    highp vec4 col = colors[0];
    for (int i = 1; i < 4; ++i) {
        col = mix(col,colors[i],smoothstep(steps[i - 1],steps[i],dist));
    }
    float factor = max(abs(uv.x - centerX),abs(uv.y - centerY));
    float c = 1. - max(abs(uv.x - centerX),abs(uv.y - centerY));
    vec4 finalColor = vec4((col.r - factor),(col.g - factor),(col.b - factor),1.);

    gl_FragColor = finalColor;
}
</script>

<canvas id="ogl-canvas" style="border: none"></canvas>

,

使用纹理坐标

下面是一个示例,该示例使用0-1范围内的纹理坐标,该坐标位于0.5,0.5处。因此,要计算梯度,我们必须将距中心的距离从0-0.5到0-1进行归一化。这可以通过除以0.5或将倒数(如示例)乘以2(因为乘积始终是更好的选择)来完成比司)

简化着色器

同样,您计算每个片段的渐变颜色的方法在计算上也很昂贵。对于每个渐变(在这种情况下为3),您依次调用smoothstepmix,但是对于每个片段2,这些计算都不会对计算出的颜色产生任何影响。

下面的示例通过检查距离是否在特定的渐变范围内,并且只有在该范围内然后计算分配给gl_FragColor的颜色然后跳出循环,来减少计算量

如果您希望渐变按照第一张图片和代码(以及可接受的答案)所建议的那样变暗到边缘,或者您要在问题中使用的第二张图片,则我无法锻炼。该示例假定您要第二张图像。

const shaders = {
vs: `attribute vec2 vert;
varying vec2 uv;
void main() {
    uv = (vert  + 1.0) / 2.0; // normalize texture coords
    gl_Position = vec4(vert,1.0);
}`,fs: `precision mediump float;
varying vec2 uv;  
uniform vec3 colors[4];
uniform float steps[4];
void main(){
    vec2 gradXY = abs(uv - 0.5);  // 0.5 is centerX,centerY
    float dist = pow(max(gradXY.x,gradXY.y) * 2.0,2.0);
    float start = steps[0];
    for (int i = 1; i < 4; i++) {
        float end = steps[i]; 
        if (dist >= start && dist <= end) {
            gl_FragColor = vec4(mix(colors[i - 1],(dist-start) / (end-start)),1);
            break;
        }
        start = end;
    }
}`,};
const F32A = a => new Float32Array(a),UI16A = a => new Uint16Array(a);
const GLBuffer = (data,type = gl.ARRAY_BUFFER,use = gl.STATIC_DRAW,buf) => (gl.bindBuffer(type,buf = gl.createBuffer()),gl.bufferData(type,data,use),buf);
const GLLocs = (shr,type,...names) => names.reduce((o,name) => (o[name] = (gl[`get${type}Location`])(shr,name),o),{});
const GLShader = (prg,source,type = gl.FRAGMENT_SHADER,shr) => {
    gl.shaderSource(shr = gl.createShader(type),source);
    gl.compileShader(shr);
    gl.attachShader(prg,shr);
}
var W;    
const gl = canvas.getContext("webgl");
requestAnimationFrame(render);
addEventListener("resize",render);
const prog = gl.createProgram();
GLShader(prog,shaders.vs,gl.VERTEX_SHADER);
GLShader(prog,shaders.fs);
gl.linkProgram(prog);
gl.useProgram(prog);
const locs = GLLocs(prog,"Uniform","colors","steps");
const vert = GLLocs(prog,"Attrib","vert").vert;
GLBuffer(F32A([-1,1]));
GLBuffer(UI16A([1,3,3]),gl.ELEMENT_ARRAY_BUFFER);
gl.enableVertexAttribArray(vert);
gl.vertexAttribPointer(vert,0); 
function render() {
    gl.viewport(0,W = canvas.width = Math.min(innerWidth,innerHeight),canvas.height = W);
    gl.uniform3fv(locs.colors,F32A([1,0]));
    gl.uniform1fv(locs.steps,F32A([0,1/3,2/3,1]));
    gl.drawElements(gl.TRIANGLES,6,0);
}
body {
       margin: 0px;
    }
    canvas {
       position: absolute;
       top: 0px;
       left: 0px;
       background: black;
    }
<canvas id="canvas"></canvas>

问题模棱两可

您的问题中有许多歧义,以下将解决

示例行中的pow函数...

float dist = pow(max(gradXY.x,2.0);

...是计算col时使用smoothstep(steps[i - 1],dist)的近似值(假设dist范围为0-0.5)。如果您想要完整的Hermite曲线,可以将线替换为...

float distL = max(gradXY.x,gradXY.y) * 2.0;
float dist = distL * distL * (3.0 - 2.0 * distL);

..,如果要像问题中那样使边缘变暗,则在计算碎片颜色时,请使用第一行的下一行。注意如果使用示例代码,则假设颜色是vec4而不是vec3,请进行适当的修改。

FragColor = mix(colors[i - 1],(dist-start) / (end-start)) - vec4(vec3(distL * 0.5),0);

或者如果不使用Hermite曲线

FragColor = mix(colors[i - 1],(dist-start) / (end-start)) - vec4(vec3(dist * 0.5),0);