Using stencils in P2D / P3D

I asked Andres Colubri if he could help me out with getting stencils to work in processing.
Here it is, and it’s freaking awesome.

// Adapted from https://learnopengl.com/Advanced-OpenGL/Stencil-testing
void setup() {
  size(600, 600, P3D);
  noStroke();
}

void draw() {
  PGL pgl = beginPGL();

  pgl.enable(PGL.STENCIL_TEST);
  pgl.stencilOp(PGL.KEEP, PGL.KEEP, PGL.REPLACE); 

  background(100);
  lights();
  
  pgl.stencilMask(0);   
  fill(128);
  drawFloor();
  flush();

  pgl.stencilFunc(PGL.ALWAYS, 1, 255); 
  pgl.stencilMask(255);
  drawBox(1);
  flush(); // Flush the geometry drawn until now to the GPU 

  pgl.stencilFunc(PGL.NOTEQUAL, 1, 255);
  pgl.stencilMask(0);
  pgl.disable(PGL.DEPTH_TEST);
  
  noLights(); // Disable lights because the outline does not have lighting
  fill(255,0,0);
  drawBox(1.1);

  pgl.stencilMask(255);
  pgl.enable(PGL.DEPTH_TEST);   

  endPGL();
}

void drawBox(float scalef) {
  pushMatrix();
  translate(width/2, height/2);
  rotateX(0.01 * frameCount);
  rotateY(0.01 * frameCount);
  scale(scalef);
  box(180);
  popMatrix();
}

void drawFloor() {
  pushMatrix();
  translate(width/2, 0.8 * height);
  scale(1, 0.1, 1);
  box(300);
  popMatrix();
}
3 Likes

What’s this please? Can you elaborate?

Could you please share single_color.glsl ?

The shader was in the first post, but I changed it so a shader is not needed anymore.
Stencils can be used to discard fragments (pixels). In the above example the outline is drawn after the box is drawn and not the other way around. I need to use stencils for clipping cause the opengl scissor does not work under an angle.

I don’t get it. Your code says sh = loadShader("single_color.glsl"); but you don’t provide a link to a glsl file, or the contents of that file. If you don’t need it, why are you loading it? If you do need it, can you share it?

I forgot to remove that line.

1 Like

Thanks for sharing this! Cool visual effect.

I have never used beginPGL / endPGL in a sketch before – it took me a while to figure out how the sketch builds the scene in three stages (the platform, the box, and the unlit scaled red box to appear “behind” it). Particularly interesting that the outline effect is a 3D object that then aligns correctly through compositing.

If I was diving deeper into this stuff, I would probably begin reading here…

Chrisir

For my own understanding I created an annotated version of this that lets you scale the box and outline interactively using mouseX / mouseY.

The outline is impressive – although the illusion breaks a bit if they both cross too far into the platform and you start seeing them double-intersecting.

// Adapted from https://learnopengl.com/Advanced-OpenGL/Stencil-testing
// https://discourse.processing.org/t/using-stencils-in-p2d-p3d/13207/7

PGL pgl;
void setup() {
  size(600, 600, P3D);
  noStroke();
}

void draw() {
  background(100);
  lights();
  fill(128);
  
  float boxScaleX = 2*mouseX/(float)width;
  float boxScaleY = boxScaleX - 0.1 + mouseY/(float)height;
  
  // setup
  pgl = beginPGL();
  pgl.enable(PGL.STENCIL_TEST);
  
  // draw floor
  pgl.stencilOp(PGL.KEEP, PGL.KEEP, PGL.REPLACE); 
  pgl.stencilMask(0);   
  drawFloor();
  flush();

  // draw box
  pgl.stencilFunc(PGL.ALWAYS, 1, 255); 
  pgl.stencilMask(255);
  drawBox(boxScaleX);
  flush(); // Flush the geometry drawn until now to the GPU 

  // draw box stencil outline
  pgl.stencilFunc(PGL.NOTEQUAL, 1, 255);
  pgl.stencilMask(0);
  pgl.disable(PGL.DEPTH_TEST);
  noLights(); // Disable lights because the outline does not have lighting
  fill(255, 0, 0);
  drawBox(boxScaleY);

  // teardown
  pgl.stencilMask(255);
  pgl.enable(PGL.DEPTH_TEST);   
  flush();
  endPGL();
}

void drawBox(float scalef) {
  pushMatrix();
  translate(width/2, height/2);
  rotateX(0.01 * frameCount);
  rotateY(0.01 * frameCount);
  scale(scalef);
  box(180);
  popMatrix();
}

void drawFloor() {
  pushMatrix();
  translate(width/2, 0.8 * height);
  scale(1, 0.1, 1);
  box(300);
  popMatrix();
}

I’m curious what the difference is between stencilOp and stencilFunc.