Different behaviors of default vs. P2D renderer with low-alpha curves

Hi all,

I’m trying to understand a difference in behavior between the default 2D renderer and the P2D renderer. In short, the default renderer seems to generate a “softer” texture when overlapping many curves with low alpha value.

The following example code shows the difference I’m seeing:

boolean defrender = false;

void settings() {
  if (defrender) {
    size(600, 200);
  } else {
    size(600, 200, P2D);
  }
}

void setup() {
  if (defrender) {
    stroke(255, 32);
  } else {
    stroke(255, 16);
  }

  randomSeed(1);
  background(0);
  noFill();

  for (float i = 1; i < 50; i++) {
    final float dt = random(TWO_PI);
    beginShape();
    for (float j = 0; j < 600; j++) {
      curveVertex(j, 100 + 100 * sin(dt + TWO_PI * i * j / 600));
    }
    endShape();
  }
}

void draw() {
  save((defrender ? "default" : "p2d") + ".png");
  exit();
}

If I run it with defrender = false, I get the upper half of the following image, whereas with defrender = true, I get the lower half:

combined

A couple things about this stand out to me:

  • I have to cut the alpha value in half to get a similar-looking result when using the P2D renderer. (Running it with alpha = 32 and the P2D renderer gives a result that is clearly much brighter.)
  • The P2D renderer (top) seems to cause individual line overlaps to stand out much more than the default renderer (bottom).

For the thing I’m developing, I prefer the look of the default renderer in this situation, though the P2D renderer has functionality that I’m relying on elsewhere. Does anyone have ideas about how I can get the P2D renderer to behave more like the default one in this case? Also, any ideas why P2D is roughly twice as bright for a given alpha value?

Thanks in advance for any thoughts. I’m using Processing 3.5.3 with MacOS 10.13.6.

1 Like

I don’t know specifically re:alpha, but I can tell you:

The default renderer and P2D have many, many differences in capabilities and behaviors, as they are entirely different architectures.

The default renderer is JAVA2D. The P2D renderer is OpenGL, specifically JOGL 2.3. You can find some more pointers to specifics and a bit of history on P2D (/P3D) here:

You can also find the different renderers in the processing source – although some of the differences you are interested in may be located in JOGL or Java2D themselves, which are external dependencies of Processing (although I am not an expert).

JAVA2D uses JAVA AWT (Abstract Window Toolkit) and is basically a simple 2D rendering engine. There was a Java 3D library (worked with AWT) which sat on top of OpenGL and DirectX but this has been superseded by JavaFX.

OpenGL supports both 2D and 3D rendering but in truth OpenGL is about rendering triangles and is not very good with lines. With regard to alpha I seem to remember that creating semi-transparent surfaces was a multi step process but my memory might be playing tricks on me and time has moved on since I did that.

1 Like

Thanks (both of you) for taking a look at this. I figured the answer would be something like that, and realistically it’s probably not worth my time to invest the effort to deeply understand the mechanics of the backends.

So then, thoughts on a workaround. The project I’m building has one common file (with settings() / setup() / draw() etc) that is used by dozen of small subprojects. Each of the subprojects draws something generally circular, but each one is different. I’d like to be able to specify, for a given subproject, whether it should use the P2D renderer. A straightforward way of doing that would be to add a boolean to each subproject, but I’m probably going to wind up using JAVA2D for all but a handful of them, and I’d rather not have to add the variable in the overwhelmingly common case. Is there a way to declare something like an optional variable, which if it exists would override a default behavior? (I’m thinking of something like the concept of weak symbols in C, if that’s familiar.)

I’m not sure I understand.

The default is:

size(600, 200);

…and, if you want to override that, you can pass:

size(600, 200, P2D);

…so, if you wanted to abstract, that you could make it:

String RENDERER = JAVA2D; // or P2D
setup(){
  size(600, 200, RENDERER);
}

…is this what you are talking about? In what code file(s) are you hoping to do this configuration, and for what outputs run in what contexts – are the .pde files runing in the PDE, or is this an IntelliJ / Eclipse project…?

1 Like

Yes, something much like that. setup() lives in main.pde, and main.pde is linked into many other directories so it can be used by many other files (c00.pde through c50.pde, each one as a separate Processing project). I would like to add code to one or two of the c*.pde files to indicate that main.pde should use P2D for that project, in such a way that I don’t have to modify the other 49 c*.pde’s (to indicate that they should use the default of JAVA2D) and that main.pde will work with all of them.

This is running either in the PDE or using processing-java.

Hmm. Sounds complicated, given that PDE files are concatenated into a into a single text file before being translated into .java. When you say “linked” you mean linux file system hard links?

You could do it like this:

templates/
  main.pde
  settingsJAVA2D.pde
  settingsP2D.pde

myJAVA2Dsketch /
  main.pde  (hard link)
  myJAVA2Dsketch.pde
  settingsJAVA2D.pde  (hard link)

myP2Dsketch /
  main.pde  (hard link)
  myP2Dsketch.pde
  settingsP2D.pde  (hard link)

In template/main.pde, define setup() and shared functions / classes. Your setup should not contain size().

In template/settingsJAVA2D.pde and in template/settingsP2D.pde, define settings(). It should contain size().

When you create a new sketch, hard link the appropriate settings file from the template directory – or override by not hard linking it, and just creating a new one.

The settings() function is new with Processing 3.0. It’s not needed in most sketches. It’s only useful when it’s absolutely necessary to define the parameters to size() with a variable.

As an alternate approach, you might consider using two different branches of a git repository to maintain your template, with a different settings in each, all maintained under a single main.pde file. To create a new sketch, pull from the appropriate branch. To update all your sketches with new changes to main.pde, loop over your sketches and git pull.

1 Like

Yep, I’m using file system hardlinks. Eventually I’ll switch to some sort of repo-based approach, but this works for now.

Your suggestion makes sense, and sounds about as simple as anything. I’ll probably actually just have something like a “rendererJAVA2D.pde” and “rendererP2D.pde”, which each define a single String that’s set to either JAVA2D or P2D, so I can use that value in multiple places. (I also call createGraphics() in a few places, and if I’m using P2D for the main renderer I want to use it there as well.)

Thanks again for your help.

Great. If that’s ALL you are doing then can just assign that global variable in your mySketch.pde and use a single generic main.pde which loads the variable in settings() – then only two files and one hardlink required per sketch. I thought you were going to have more customization than that.

Notice that JAVA2D / P2D isn’t actually a string – it is a PConstant that references an int.

So you can just use String RENDERER = JAVA2D; // or P2D as in my example above into your sketch pde, and then put settings(){ size(600, 200, RENDERER); } in the shared main.pde.

Actually, all renderer constants are of datatype String: :roll_eyes:
Processing.GitHub.io/processing-javadocs/core/processing/core/PConstants.html#JAVA2D

Some of the sketches I post in the forum got something like static final String RENDER = FX2D; for example. :smirk:

1 Like

Oops! Should have actually checked, not gone from memory. Fixing that above.

1st time I had to deal w/ it, many years ago, I was also surprised it was a String instead of an int, as most constants in PConstants are. :open_mouth:

Why not have each c??.pde file have its own offscreen buffer either JAVA2D or P2D which the code draws to. In the main sketch you simply copy the buffer to the display.

1 Like