Find the correct order of drawing a box in perspective

Hello! Im trying to learn about perspective using code and create my own drawing system.

I have this piece of code and my problem is that i dont know how to sort the sides of the cube to draw them correctly according to the vanish point (in what order should i draw the sides). My intuition says that i need to establish a viewer point of view and sort every side by the distance. But i dont know how to emulate that viewer point in perspective.

Thanks!

let persp;
let cube 

function setup() {
  createCanvas(400, 400);
  persp = new Perspective_class(createVector(width / 2 - 100, height / 2));
  cube = new Cube(0,createVector(width/2,100),100,200,0.2)
}

function draw() {
  background(220);
  stroke(0);
  line(0, persp.vanish.y, width, persp.vanish.y);
  ellipse(persp.vanish.x, persp.vanish.y, 10);
  
  cube.draw()
}

class Perspective_class {
  constructor(pos) {
    this.vanish = pos;
  }
}

class Polygon {
  constructor(index, points) {
    this.index = index;
    this.points = points;
    
    console.log(this.points)
  }

  draw() {
    fill(random(240, 255), 240, random(255, 250));
    stroke(0);

    beginShape();
    for (let i = 0; i < 4; i++) {
      vertex(this.points[i].x, this.points[i].y);
    }
    endShape(CLOSE);
  }
}

class Cube {
  constructor(index, position, w, h, l) {
    this.position = position;
    this.w = w;
    this.h = h;
    this.l = l;
    this.others = [];
    this.index = index;
    this.polygons = []
    this.points = [
      createVector(this.position.x, this.position.y),
      createVector(this.position.x, this.position.y + this.h),
      createVector(this.position.x + this.w, this.position.y + this.h),
      createVector(this.position.x + this.w, this.position.y),
    ];
    this.generateSides()
  }

  generateSides() {
    this.depth(this.points)
    this.back();
    this.front();
    this.left();
    this.right();
    this.bottom();
    this.top();
      }

  back() {
    this.polygons.push(
      new Polygon(0, [
        this.others[0],
        this.others[1],
        this.others[2],
        this.others[3],
      ])
    );
  }

  front() {
    this.polygons.push(
      new Polygon(1, [
        this.points[0],
        this.points[1],
        this.points[2],
        this.points[3],
      ])
    );
  }

  left() {
    this.polygons.push(
      new Polygon(2, [
        this.others[0],
        this.others[1],
        this.points[1],
        this.points[0],
      ])
    );
  }

  right() {
    this.polygons.push(
      new Polygon(3, [
        this.points[3],
        this.points[2],
        this.others[2],
        this.others[3],
      ])
    );
  }

  top() {
    this.polygons.push(
      new Polygon(4, [
        this.others[0],
        this.points[0],
        this.points[3],
        this.others[3],
      ])
    );
  }

  bottom() {
    this.polygons.push(
      new Polygon(5, [
        this.points[1],
        this.others[1],
        this.others[2],
        this.points[2],
      ])
    );
  }

  depth(points) {
    for (let p of points) {
      let a = lerpVector(p, persp.vanish, this.l);
      this.others.push(a);
    }
  }

  draw() {
    for (let poly of this.polygons) {
      poly.draw();
    }
  }
}

function lerpVector(a, b, f) {
  let aa = lerp(a.x, b.x, f);
  let bb = lerp(a.y, b.y, f);
  return createVector(aa, bb);
}
2 Likes

Did you try

createCanvas(100, 100, WEBGL);
1 Like

I am using p5.svg for save . svgs! But how could using WEBGL help me? In defining a camera and a perspective using the functions? I was trying some alternate way

I am sure, you are well aware of this.

You are working in 2D context and I suggested 3D context where such things are handled automatically.

When you want to simulate 3D in 2D, that’s possible.

Here you can start by draw what’s in the background first and then draw what’s in the foreground (covering the first)

the maths: 3D projection - Wikipedia

2 Likes

I still haven’t figured out how I can know what’s ahead and what’s behind with this information. I was studying this video but it seems that it does not develop the projection of the camera, so I still do not know how to solve the problem. Any ideas?

[image]

There are two things you are asking here. One is how to understand perspective projection and the other is depth sorting of geometry. Perspective projection is by far the easier of the two.

For the most part, we describe the objects we’re drawing using triangles defined by their vertices. For convenience, we usually describe the positions of the vertices in object space, centered around the origin. We then place that object (or many copies of the object) in world space by transforming it with translate, scale, and rotate to some place and orientation. Mathematically, we can combine those transformations into a single matrix so that when we multiply each vertex by that matrix, we get its new position in the new coordinate space.

Similarly, we can think of the camera as an object in the world with its own position and orientation. To see all the objects in the world, we want to know where they are relative to the camera which means that we need to apply to them an additional transformation that “un-does” the camera transformations. We do this by applying the inverse of the camera transformation matrix to compute the vertex positions in what is now still 3D camera space with the camera’s eye sitting at the origin looking down the z-axis. Different code bases differ on whether you are looking down the +z or -z axis. Often camera space uses a left-handed coordinate system with +x to the right, +y up, and +z going away in front of you.

Finally, we get to perspective projection. In camera space, just divide the x- and y-coordinates by z. That’s it. If you imagine parallel lines that are going into the screen, points along those lines that are further away (deeper z values) get drawn further towards the center of the screen. That’s one-point perspective. One-point and two-point perspective are just special cases of three-point where the objects happen to line up with the camera direction.

Depth sorting your geometry can, in simple cases, be quite easy and in general be extremely messy. In the simple case, you draw a closed convex object, like your box. For each polygonal face, compute the normal (perpendicular vector) as the cross product of any two edges. If that normal faces away from the camera then that face is on the back of the object and will be hidden by the other sides that face us, so just don’t draw it. The front-facing faces can be drawn in any order and since the object is convex, none of them will obscure each other. You can get pretty far pretty easily with just convex objects.

More generally, you have a soup of triangles. Each lies in a place. If all of triangle A’s vertices are on one side of triangle B’s plane or vice-versa, then we can sort them based on which side also contains the camera. If A has points on both sides of B and B has points on both sides of A then you might have to define some plane that slices between them. But what then if the two triangles intersect. Or you could have 3 triangles don’t intersect but from the camera view overlap each other in a circle so that A is over B and B is over C, but C is over A. In those cases, you could have to split at least one of the triangles so as to draw them in order. In general, it’s a complete mess which is why for the most part, we don’t to any of it that way.

Instead, we throw all the triangles at the graphics card in random order. It transforms the vertices into camera space, applies the perspective transformation to divide by z, and then scan-converts each triangle to pixels including the depth at each pixel. We store the depth for each pixel on the screen in the z-buffer. When drawing a new triangle, we only draw its pixel color if its new depth is closer than the depth already stored at that screen pixel. All done in hardware very very fast.

1 Like