P5.js:如何以编程方式计算两种颜色之间的 BURN 混合模式?

P5.js: how to programatically calculate BURN blendMode between 2 colors?

我正在做一些 blendMode(BURN) 来绘制一些形状。其他形状我需要直接在之前形状的结果颜色中绘制,所以我需要自己生成混合结果颜色。

我正在寻找类似的东西:

let blendedColor = blendColorsBurn(color1, color2);

没有内置函数。在 2d 模式下 p5.js 利用 canvas 的 globalCompositeOperation property,当设置为 color-burn 时执行以下颜色混合操作:

Divides the inverted bottom layer by the top layer, and then inverts the result.

这听起来很简单,但我想验证它的意思,所以我决定尝试快速测试实现。按 shift 和 control 键分别查看内置 BURN blendMode 与我的计算。

let existingContent;
let newContent;

let manualBurn;

let scaleFactor = 1;

function preload() {
  existingContent = loadImage("https://www.paulwheeler.us/files/existing-content.png");
  newContent = loadImage("https://www.paulwheeler.us/files/new-content.png");
}

function setup() {
  createCanvas(windowWidth, windowHeight);
  noLoop();

  scaleFactor = height / newContent.height;
  manualBurn = createImage(newContent.width, newContent.height);

  // Divides the inverted bottom layer by the top layer, and then inverts the result.
  for (let x = 0; x < newContent.width; x++) {
    for (let y = 0; y < newContent.height; y++) {
      const c1 = existingContent.get(x, y);
      const c2 = newContent.get(x, y);

      if (alpha(c2) > 0) {
        let a = alpha(c1) / 255;
        // Inverted bottom layer
        let [inv_r, inv_g, inv_b, inv_a] = [
          // Subtracting from alpha instead of 1 is to deal with pre-multiplied alpha
          a - red(c1) / 255,
          a - green(c1) / 255,
          a - blue(c1) / 255,
          1 - alpha(c1) / 255,
        ];

        // divided by the top layer
        let [div_r, div_g, div_b, div_a] = [
          inv_r / (red(c2) / 255),
          inv_g / (green(c2) / 255),
          inv_b / (blue(c2) / 255),
          inv_a / (alpha(c2) / 255),
        ];

        // inverted
        let out = [255 * (1 - div_r), 255 * (1 - div_g), 255 * (1 - div_b), max(alpha(c2), 255 * (1 - div_a))];

        manualBurn.set(x, y, out);
      } else {
        manualBurn.set(x, y, c1);
      }
    }
  }

  manualBurn.updatePixels();
}

function keyPressed() {
  redraw();
}

function keyReleased() {
  redraw();
}

function draw() {
  clear();
  scale(scaleFactor);
  if (keyIsDown(CONTROL)) {
    blendMode(BLEND);
    image(manualBurn, 0, 0);
  } else {
    image(
      existingContent,
      0,
      0
    );
    if (keyIsDown(SHIFT)) {
      blendMode(BURN);
      image(newContent, 0, 0);
    }
  }
}
html, body {
  margin: 0;
  padding: 0;
}
body {
  background-image: url("https://www.paulwheeler.us/files/checkerboard.jpeg");
  background-repeat: repeat;
  background-size: 200px;
}
canvas {
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

肯定有一些陷阱:

  1. 这可能很明显,但必须使用 0 到 1 之间的值来完成此数学运算,而不是 0 到 255 之间的值。
  2. colorMode(RGB, 1, 1, 1, 1) 不起作用,因为 red()green()blue() 函数四舍五入为整数 ‍♂️
  3. 当反转颜色时,您需要从 alpha 值而不是 1 开始,因为预乘了 alpha。
  4. 从描述上看并不明显,但似乎刻录图像 alpha 优先。