GLSL - 图像片段着色器 manipulation/blending

GLSL - fragment shaders for image manipulation/blending

我正在尝试构建一个着色器,让您可以像在 photoshop 中一样,通过对顶部图像应用渐变不透明蒙版来组合两个图像。我已经到了可以将蒙版图像覆盖在另一个图像上的地步,但作为新手,我对一些事情感到困惑。

似乎着色器中的图像有时会倾斜以适应 canvas 大小,并且它们总是从位置 0,0 开始。我尝试了一些我发现的片段来尝试缩放纹理,但总是以不令人满意的结果告终。

我很好奇是否有一种标准方法可以在视图中调整大小、倾斜和平移 纹理,或者 GLSL 中的图像是否必须以某种方式受到限制从而停止我无法实现我的目标。

我也不确定我是如何应用 gradient/mask 以及它是否是正确的方法,因为目前我对渐变的形状没有太多控制.

这是我目前的情况:

  precision highp float;
  varying vec2 uv;
  uniform sampler2D originalImage;
  uniform sampler2D image;
  uniform vec2 resolution;

  void main(){

    float mask;
    vec4 result;

    vec2 position = gl_FragCoord.xy / ((resolution.x + resolution.y) * 2.0 );

    mask = vec4(1.0,1.0,1.0,1.0 - position.x).a;

    vec4 B = texture2D(image,uv);
    vec4 A = texture2D(originalImage,uv) * (mask);

    result = mix(B, A, A.a);

    gl_FragColor = result;
  }

生成这样的图像:

我希望能够独立更改图像的位置,并确保它们符合适当的尺寸。

我试过像这样天真地改变位置:

vec2 pos = uv;
pos.y = pos.y + 0.25;
texture2D(image, pos)

这确实改变了纹理,但会导致一堆奇怪的线条拖拽:

我试过这样摆脱它们:

gl_FragColor = uv.y < 0.25 ? vec4(0.0,0.0,0.0,0.0) : result;

但它什么也没做

当图像大小不同时,您确实需要决定要发生什么。你可能想要的是它看起来没有图像所以检查你的 UV 坐标并在图像之外使用 0,0,0,0

//vec4 B = texture2D(image,uv);

vec4 getImage(sampler2D img, vec2 uv) {
  if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
    return vec4(0);
  }
  return texture2D(img, uv);
}

vec4 B = getImage(image, uv);

至于 size/skew/translate 图像使用矩阵的标准方法

uniform mat4 u_imageMatrix;

...

vec2 newUv = u_imageMatrix * vec4(uv, 0, 1).xy;

An example of implementing canvas 2d's drawImage using a texture matrix.

虽然我不认为大多数图像处理 programs/library 总的来说会尝试在着色器中完成所有操作。相反,他们会用非常非常简单的原语来构建图像。我最好的猜测是他们使用的着色器只是 A * MASK 然后绘制 B 然后是 A * MASK 并混合。

换句话说,如果您在 photoshop 中有 30 个图层,它们将不会生成一个单独的着色器来计算一个巨大的着色器中的最终图像,同时吸收所有 30 个图层。相反,每一层都将使用更简单的着色器单独应用。

我还希望他们为遮罩创建纹理,而不是在着色器中使用数学。这样掩码可以是任意复杂的,而不仅仅是一个 2 档斜坡。

注意我不是说你做错了。你可以自由地做任何你想做的事。我只是说我怀疑如果你想构建一个通用的图像处理库,你会通过组合更小的构建块获得更大的成功,而不是尝试在着色器中做更复杂的事情。

ps:我觉得getImage可以简化为

vec4 getImage(sampler2D img, vec2 uv) {
  return texture2D(img, uv) * step(0.0, uv) * step(-1.0, -uv);
}