OpenGL Shader-mix混合和形状应用

4,925 阅读2分钟

「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战

前期练习

OpenGL有内置函数mix是一个特殊线性插值函数,两个参数值基于第三个参数插值genType mix(genType x,genType y,float a),即(x*(1-a)+y*a)。简单理解就是a的值决定了xy的强弱关系。a取值范围在[0,1]之间,a值越大,结果值中y占比会越大;a值越小,结果值中y占比会越小;

如下代码使用mix函数输入两个vec4向量分别表示红色和白色,a值从0.1-0.8。可以看到a值越来越大时最后呈现结果上颜色越来越趋向白色。但a值是1.0时呈现结果就是白色

void main() {
    vec2 uv = gl_FragCoord.xy / iResolution.xy;
    gl_FragColor = mix(vec4(1.0,0.0,0.0,1.0),vec4(1.0,1.0,1.0,1.0),0.8);
}
a=0.1a=0.5a=0.8a=1.0
image.pngimage.pngimage.pngimage.png

mix函数结合绘制形状

前面已知使用公式绘制形状后,下面去实现如何在已有纹理上去覆盖形状内容,得到奇特的图片效果。

圆形混合

已知绘制圆形函数,如sdfCircle函数所示,但d值大于0时表示超出了圆的绘制范围,若小于0时表示在圆范围内,及sdfCircle函数返回值是true/false。将sdfCircle函数结果带入到mix函数作为a的入参true是0.0不混合newColor,1.0表示混合newColor并且是百分百占比也就是完全显示圆形内容。

bool sdfCircle(vec2 uv,float r){
    uv -= 0.5; // x: <-0.5, 0.5>, y: <-0.5, 0.5>
    uv.x *= iResolution.x/iResolution.y; // x: <-0.5, 0.5> * aspect ratio, y: <-0.5, 0.5>
    uv.x += 0.2;
    uv.y -= 0.2;
    float d = length(uv) - r;
    return d > 0.; // 大于0超出画圆范围,小于0在画圆范围内
}
void main() {
    vec2 uv = gl_FragCoord.xy / iResolution.xy;
    bool isCircle = sdfCircle(uv,0.25);
    vec4 color = texture(iChannel3,uv);
    float blackwhite = (color.r + color.g + color.b) * 0.333;
    vec3 col = vec3(blackwhite);
    vec4 newColor = mix(color,vec4(0.,0.,1.0,1.0),0.5);
    // newColor.g = newColor.b;
    // newColor.b = newColor.b + 100.0;
    gl_FragColor = mix(vec4(col,1.0),newColor,isCircle ? 0.: 1.0);
}

实现结果上圆形图案0.5透明度覆盖在纹理图片之上。 image.png

矩形混合

矩形混合实现方式和圆形是一样的。不同之处在于这里混合覆盖了两个叠加的矩形图形。因为底色纹理是黑色背景,所以如例子中多个矩形叠加混合效果时,红色矩形和绿色矩形分别先和纹理图片混合后再进行两次mix函数将两个矩阵混合到整体纹理之上。

bool sdfSquare(vec2 uv,float size,float offset){
    uv -= 0.5; // x: <-0.5, 0.5>, y: <-0.5, 0.5>
    uv.x *= iResolution.x/iResolution.y; // x: <-0.5, 0.5> * aspect ratio, y: <-0.5, 0.5>
    uv.x += offset;
    uv.y -= offset;
    float d = max(abs(uv.x),abs(uv.y)) - size;
    return d > 0.; // 大于0超出画圆范围,小于0在画圆范围内
}
void main() {
    vec2 uv = gl_FragCoord.xy / iResolution.xy;
    // 纹理图片
    vec4 color = texture(iChannel3,uv);
    float blackwhite = (color.r + color.g + color.b) * 0.333;
    vec4 col = vec4(vec3(blackwhite),1.0);
    //色块红 
    bool isRedSquare = sdfSquare(uv,0.3,0.1);
    vec4 redColor = mix(color,vec4(0.6118, 0.0824, 0.0824, 1.0),0.5);
    //色块绿
    bool isGreenSquare = sdfSquare(uv,0.15,-0.25);
    vec4 greenColor = mix(color,vec4(0.4, 0.7412, 0.0824, 0.877),0.5);
    //混合模式
    vec4 square = mix(col,redColor,isRedSquare? 0.: 0.75);
    square = mix(square,greenColor,isGreenSquare? 0.: 0.75);
    gl_FragColor = square;
}

image.png