Masking shader with alpha blending

Hi

I have a working example in the p5js online editor (adapted from GitHub - b2renger/p5js-shaders) using shaders for masking, but I would like to add alpha blending to the sketch allowing me to more smoothly blend the layers instead of the on/off style of the example right now (as shown in the screenshot below).

Any ideas?

Screenshot 2022-02-12 at 17.56.30

I’m sure there are numerous approaches, but here’s one idea:

Instead of using the color each pixel in the “drawing” as a discreet indicator of which source to use (depending on whether the pixel is red, green, or blue), use the red green and blue channels of the drawing as percentages indicating how much of each source to use:

  // Normalize the color channels to use as multipliers so that the sum maxes
  // out at 1.0
  vec3 dnorm = d.rgb / (d.r + d.g + d.b);
  
  // Combine the 3 sources using the R G and B channels of the drawing as masks
  // representing how much of each source to use.
  gl_FragColor = vec4(
    // Premultiplying the alpha has the effect of drawing over a black background
    (s1.r * dnorm.r + s2.r * dnorm.g + s3.r * dnorm.b) * d.a,
    (s1.g * dnorm.r + s2.g * dnorm.g + s3.g * dnorm.b) * d.a,
    (s1.b * dnorm.r + s2.b * dnorm.g + s3.b * dnorm.b) * d.a,
    1.0
  );

Now, with the existing drawing code this would result in exactly the same effect you already have, however, now you can modify your drawing code to use alpha and blend together the three colors. This could be as simple as giving the drawingColor a low alpha value, but if you want to get fancy you could use a linear gradient (note: it makes the most sense not to use WebGL for the drawing graphics context, because it will work just as well as a textured, and WebGL doesn’t support gradients).

  // Example of a gradient creation in setup():
  g1 = drawing.drawingContext.createRadialGradient(0, 0, 5, 0, 0, 150);
  g1.addColorStop(0, 'rgba(255, 0, 0, 0.05)');
  g1.addColorStop(0.5, 'rgba(255, 0, 0, 0.01)');
  g1.addColorStop(1, 'rgba(255, 0, 0, 0.0)');

  // Wherever you draw your circle (I moved this to mouseDragged)
  drawing.push();
  drawing.translate(mouseX, mouseY);
  drawing.drawingContext.fillStyle = drawingGradient;
  drawing.ellipse(0, 0, 150);
  drawing.pop();

Here’s a working example (click and drag the mouse to paint).

3 Likes

Oh awesome, that does the trick! Thanks so much :pray: