No shading after flipping x axis wth scale(-1, 1, 1). How to fix this?

The following simple code works perfectly. But the shading is gone if I enable scale(-1, 1, 1). What is the proper way to flip the object with respect to y-z plane without losing shading? I guess this may be caused by culling but I couldn’t find how to fix it. What I actually want to draw is a more complicated object so I’d like to do this without modifying the object itself (so in this example, without changing the codes below // draw shape).

void setup() {
  size(400, 400, P3D);
}

void draw() {
  background(0);
  lights();
  noStroke();
  fill(128);
  translate(width/2, height/2);

  //scale(-1, 1, 1);

  // draw shape
  rotateX(-PI/6);
  rotateY(PI/8);
  box(100);
  translate(-30, 0);
  sphere(60);
}
1 Like

Hello,

  lights(); 
  directionalLight(128, 128, 128, 0, 0, 1); 

Works but too late for me to think about!

:)

1 Like

Thank you so much for your quick answer at the late night, glv! Adding the directionalLight line solved the issue.

My understanding is that the cause was that the direction of the default directional light added by lights() was changed to the opposite by scale(-1, 1, 1) and the visible side got dark. Adding another directional light of the opposite direction fixed the issue. But I still don’t understand why this happens. I thought scale(-1, 1, 1) should flip things only along x axis directions. Why does scale(-1, 1, 1) changes the direction of the directional light from toward -z to toward +z?

It’s not that it changes the light. It changes the normal vectors associated with each vertex of your surface. Since those are calculated using the cross product of the plane your vertexes make, if you change the scale as you have, the cross product vector suddenly has an extra minus sign and thus points in the opposite direction. And since the lighting uses those normal vectors to work out how to light the surface, once they change direction, the lighting appears to change.

The fix above is a second light that’s pointing “from behind” that actually lights the surface “backwards”.

Or something. You might want to google “normal vectors for lighting surfaces” to learn more.

2 Likes

TfGuy44, thank you very much for the explanation. Now I understand that with scale(-1, 1, 1), the directional light stays the same but the front and back of each surface is flipped. If so, what is the correct way to mirror flip an object without flipping the normal vectors?

Guessing here, so don’t hold me to this.

The “proper” fix to me would be to also scale the lights().

That is, move the call to scale() before the call to lights(). This also means you’ll want to center your object by moving it in the negative X direction.

A related discussion:
https://forum.processing.org/two/discussion/25211/lights-and-shapes3d-library

Integrated @TfGuy44 suggestion along with a toggle to see the differences:

boolean toggle = true;

void setup() 
  {
  size(400, 400, P3D);
  textSize(24);
  }

void draw() 
  {
  background(0);

  if (toggle)  
    {
    scale(1, 1, 1);
    translate(width/2, height/2);
    lights(); 
    text("Lights 1", 0, 150);
    }
  else
    {
    scale(-1, 1, 1);
    translate(-width/2, height/2); 
    lights();
    directionalLight(128, 128, 128, 0, 0, 1);
    
    //ambientLight(128, 128, 128); 
    //lightSpecular(0, 0, 0);
    //lightFalloff(1, 0, 0);  
    text("Lights 2", 0, 150);
    }

  push();
  noStroke();
  fill(128);
 
  // draw shape
  rotateX(-PI/6);
  rotateY(PI/8);
  box(100);
  translate(-30, 0);
  sphere(60);
  pop();
  }
  
void keyPressed()
  {
  toggle = !toggle;
  }

References:
https://processing.org/reference/normal_.html
https://processing.org/reference/lights_.html

:)

Thank you guys. But it seems that calling scale(-1, 1, 1) before lights() does not solve the issue. The code by glv still depends on the additional directionalLight.

Actually, what I originally wanted to do was to mirror flip an object, not the axis (sorry the topic title was eventually incorrect). In the following example, I have a half teapot object as a PShape. I want to show the other half together in the same scene. As you can see by switching the camera position, the half made by scale(-1, 1, 1) appears as if it’s under a different lighting environment. I understood that this is caused because surface normals are flipped as you suggested.

Adding another directional light makes the normal flipped surfaces visible. But it also affects the rendering of the original half, so I’d like to avoid it.

So the corrected question would be: What is the correct way to make a mirror flipped object from a PShape and show it in the same scene? The teapot is just an example here. I know I can get a model of the whole teapot.

// curl -OL https://www.dropbox.com/s/u46rapjoqrf32xk/half_teapot.obj 

PShape model;

boolean add_opposite_directional_light = false;
int camera_pos = 0;

void setup() {
  size(1024, 768, P3D);

  model = loadShape("half_teapot.obj");
  model.scale(2000);
}

void draw() {
  background(0);
  lights();
  if (add_opposite_directional_light) {
    directionalLight(128, 128, 128, 0, 0, 1);
  }

  translate(width/2, height/2);
  switch(camera_pos) {
  case 0:
    camera(0, -height/5, height, 0, 0, 0, 0, 1, 0);
    break;
  case 1:
    camera(height, -height/5, height, 0, 0, 0, 0, 1, 0);
    break;
  case 2: 
    camera(height, -height/5, -height, 0, 0, 0, 0, 1, 0);
    break;
  }

  push();
  stroke(#ff0000);
  line(0, 0, 0, 200, 0, 0);
  stroke(#00ff00);
  line(0, 0, 0, 0, 200, 0);
  stroke(#0000ff);
  line(0, 0, 0, 0, 0, 200);
  stroke(#999999);
  pop();

  push();
  translate(-200, 0, 0);

  // draw left half
  rotateY(PI);
  rotateX(PI/2);
  shape(model);
  pop();

  push();
  translate(200, 0, 0);

  // draw right half
  scale(1, 1, -1);
  rotateY(PI);
  rotateX(PI/2);
  shape(model);
  pop();
}

void keyPressed() {
  switch (key) {
  case 'l':
    add_opposite_directional_light = !add_opposite_directional_light;
    break;
  case 'c':
    camera_pos = (camera_pos + 1) % 3;
    break;
  }
}
1 Like

Hello,

I was tinkering with the your code and sharing some of my initial work.
Not quite there but you may glean some insights.
I have some work in progress that is creating a new shape from vertices but that will have to wait for a rainy day.

Click for Code
// curl -OL https://www.dropbox.com/s/u46rapjoqrf32xk/half_teapot.obj 

PShape tp, s;
float angle;

void setup() 
  {
  size(600, 500, P3D);

  tp = loadShape("half_teapot.obj");
  tp.scale(2000, 2000, 2000);
  
  s = loadShape("half_teapot.obj");
  s.scale(2000, 2000, 2000);
  s.rotate(TAU/2, 0, 1, 0);
  
  println(s.getChildCount());
  
  childVertices(s);
  }

void draw() 
  {
  background(0);
  lights();
  translate(width/2, height/2);
  
  angle += TAU/360;
  
  rotateY(angle);
  rotateX(TAU/16);
  
  push();
  rotateZ(TAU/2);
  tp.setFill(color(255, 255, 0));
  shape(tp, 0, 0);
  pop();
  
  //s.rotate(TAU/2, 0, 1, 0); //Woops!
  s.setFill(color(255, 255, 0));
  shape(s, 0, 0);
  }
  

// Adapted from:
// https://forum.processing.org/two/discussion/18087/get-vertex-from-an-obj-file
// jeremydouglass You da man!
void childVertices(PShape shape) 
  {
  for (int i=0; i<shape.getChildCount(); i++) 
    {   
    PShape child = shape.getChild(i);
    for (int j=0; j<child.getVertexCount(); j++) 
      {
      PVector vert = child.getVertex(j);
      vert.x = vert.x;
      vert.y = vert.y;
      vert.z = -vert.z;
      child.setVertex(j,vert);    
     }
   }
  }

References:

:)

1 Like