blendMode(DIFFERENCE) in P2D / P3D

This image is very easy to do using blendMode(DIFFERENCE).


Unfortunately it’s not supported in P2D / P3D, so here a workaround in case someone needs this:

import com.jogamp.opengl.*;
com.jogamp.opengl.GL3 gl;
void setup() {
  size(600, 600, P2D);
  noStroke();
  smooth(8);
  fill(255);
  
  // DIFFERENCE is not supported in P2D / P3D
  //blendMode(DIFFERENCE);
  
  gl = GLContext.getCurrentGL().getGL3();
}

void draw() {
  background(255);
  // This provides blendMode(DIFFERENCE) in P2D / P3D
  gl.glEnable(GL3.GL_BLEND);
  gl.glBlendFunc(GL3.GL_ONE_MINUS_DST_COLOR, GL3.GL_ZERO);
  
  for (int i = 0; i < 20; i++) {
    float sz = 50 + (i % 4) * 50;
    float x = width * noise(i * 0.527 + millis() * 0.0003);
    float y = height * noise(i * 0.729 + millis() * 0.0001);
    circle(x, y, sz);
  }
}

ported from openFrameworks

4 Likes

That’s excellent, here’s the same sketch translated for JRubyArt:-

java_import 'com.jogamp.opengl.GL3'
java_import 'com.jogamp.opengl.GLContext'

attr_reader :gl
def setup
  sketch_title 'Blend Mode Difference P2D'
  noStroke
  fill(255)
  # DIFFERENCE is not supported in P2D / P3D
  # blendMode(DIFFERENCE)
  @gl = GLContext.getCurrentGL.getGL3
end

def draw
  background(255)
  # This provides blendMode(DIFFERENCE) in P2D / P3D
  gl.glEnable(GL3::GL_BLEND)
  gl.glBlendFunc(GL3::GL_ONE_MINUS_DST_COLOR, GL3::GL_ZERO)
  20.times do |i|
    sz = 50 + (i % 4) * 50
    x = width * noise(i * 0.527 + millis * 0.0003)
    y = height * noise(i * 0.729 + millis * 0.0001)
    circle(x, y, sz)
  end
end


def settings
  size(600, 600, P2D)
  smooth(8)
end

2 Likes

The RaspberryPI only officially supports GL2 (this may change for RaspberryPI4) anyway I managed to get sketch with PiCrate on RaspberryPI4:-

 # frozen_string_literal: true

require 'picrate'

class BlendModeSketch < Processing::App
  java_import 'com.jogamp.opengl.GL3'
  java_import 'com.jogamp.opengl.GLContext'

  attr_reader :gl
  def setup
    sketch_title 'Blend Mode Difference P2D'
    noStroke
    fill(255)
    # DIFFERENCE is not supported in P2D / P3D
    # blendMode(DIFFERENCE)
    @gl = GLContext.getCurrentGL.getGL2
  end

  def draw
    background(255)
    # This provides blendMode(DIFFERENCE) in P2D / P3D
    gl.glEnable(GL3::GL_BLEND)
    gl.glBlendFunc(GL3::GL_ONE_MINUS_DST_COLOR, GL3::GL_ZERO)
    20.times do |i|
      sz = 50 + (i % 4) * 50
      x = width * noise(i * 0.527 + millis * 0.0003)
      y = height * noise(i * 0.729 + millis * 0.0001)
      circle(x, y, sz)
    end
  end

  def settings
    size 600, 600, P3D
    smooth 8
  end
end

BlendModeSketch.new

2 Likes

That’s not a true DIFFERENCE blend though. You can only achieve that with shaders.

I’ve refactored @hamoid’s Java Mode sketch just a little: :coffee:

// Discourse.Processing.org/t/blendmode-difference-in-p2d-p3d/17541/5
// Forum.OpenFrameworks.cc/t/blend-mode-invertcolor/34415/8

// Hamoid (2020/Feb/05)

import com.jogamp.opengl.GLContext;
import com.jogamp.opengl.GL3;

//static final float[] FS = { .527, 3e-4, .729, 1e-4 };

static final String RENDER = P2D;
static final color C = #00FFFF; // cyan

static final int
  SMOOTH = 8, BLENDER = DIFFERENCE, 
  BALLS = 20, DIAM = 50, 

  SFACTOR = GL3.GL_ONE_MINUS_DST_COLOR, 
  DFACTOR = GL3.GL_ZERO;

void setup() {
  size(600, 600, RENDER);
  smooth(SMOOTH);

  fill(C);
  noStroke();

  blendMode(BLENDER); // DIFFERENCE isn't supported by OPENGL

  GLContext.getCurrentGL().getGL3().glBlendFunc(SFACTOR, DFACTOR);
}

void draw() {
  background(C);

  final int w = width, h = height, m = millis();

  for (int i = 0; i < BALLS; ++i) {
    final float
      x = noise(.527 * i + 3e-4 * m) * w, 
      y = noise(.729 * i + 1e-4 * m) * h, 
      d = i % 4 * DIAM + DIAM;

    circle(x, y, d);
  }
}

And then made a Python Mode version out of it: :snake:

# Discourse.Processing.org/t/blendmode-difference-in-p2d-p3d/17541/5
# Forum.OpenFrameworks.cc/t/blend-mode-invertcolor/34415/8

# Hamoid (2020/Feb/05)

from com.jogamp.opengl import GLContext, GL3

FS = .527, 3e-4, .729, 1e-4

RENDER, SMOOTH, BLENDER = P2D, 8, DIFFERENCE

BALLS, DIAM, C = 20, 50, 0xffFFFF00 # yellow
BALLS_RANGE = tuple(range(BALLS))

SFACTOR, DFACTOR = GL3.GL_ONE_MINUS_DST_COLOR, GL3.GL_ZERO

def setup():
    size(600, 600, RENDER)
    smooth(SMOOTH)

    fill(C), noStroke()
    blendMode(BLENDER) # DIFFERENCE isn't supported by OPENGL

    GLContext.getCurrentGL().getGL3().glBlendFunc(SFACTOR, DFACTOR)


def draw():
    background(C)

    w = width
    h = height
    m = millis()

    for i in BALLS_RANGE:
        x = noise(FS[0] * i + FS[1] * m) * w
        y = noise(FS[2] * i + FS[3] * m) * h
        d = i % 4 * DIAM + DIAM

        circle(x, y, d)
1 Like

Assuming JAVA2D provides “True DIFFERENCE ®” :stuck_out_tongue: , I see what you mean:

JAVA2D DIFFERENCE

P2D difference with glBlendFunc

1 Like

Yes, DIFFERENCE should be abs(src - dst). The Java2D version actually uses the code from PImage here. It’s mostly right, although as Processing doesn’t use premultiplied alpha (unlike the extension in PraxisLIVE) most blending is a bit off when there’s transparent surfaces involved.

The equivalent software code in PraxisLIVE is here if you’re interested. And the shader support for DIFFERENCE blending is here. The latter shows how to use multiple PImage to do this, but also requires my extended PShader class that sets up the correct uniforms for additional texture inputs.

Unfortunately, doing what you’re doing above would have a lot of overhead with the shader approach as you’d have to keep swapping what surface you’re drawing to.

Incidentally, there’s an interesting slide set from an nvidia engineer on OpenGL, blend modes and premultiplied alpha here - https://www.slideshare.net/Mark_Kilgard/blend-modes-for-opengl

3 Likes