Why is my perspective unable to display my cube faces?

I wrote a program in which some points are expressed, expressed in three-dimensional coordinates, and which must be drawn in a two-dimensional canvas. To do this, I use perspective projection, uniform coordinates and similar triangles. However, my program does not work, and I really do not know why.

I followed two tutorials. I really understood the geometric definitions and properties that I read. However, my implementation does not work ... I will write links to both of these courses little by little to make your reading more comfortable :).

Overview: geometric reminders

Perspective projection is performed in accordance with this workflow (see these 2 courses - I wrote the relevant links about the latter (HTML bindings) later in this post):

  • Definition of drawing points expressed in accordance with the world coordinate system; Definition of a projection matrix, which is a transformation matrix that “converts” a point expressed in accordance with the world coordinate system to a point expressed in accordance with the camera coordinate system (NB: I believe that this matrix can be understood as a three-dimensional “camera” object )

  • The product of these points with this matrix (as defined in the corresponding part below in this document): the product of these points, expressed in the world, leads to the transformation of these points into a camera coordinate system. Note that the points and matrix are expressed in 4D (the concept of homogeneous coordinates).

  • Using the concept of similar triangles for a project (only calculation is performed at this stage), points expressed in the camera are displayed on the canvas (using their 4D coordinates). After this operation, the points are now expressed in 3D (the third coordinate is calculated, but is not actually used on the canvas). The 4th coordinate is deleted because it is not useful. Please note that the 3rd coordinate will not be useful, except for the processing of z-battle (although I do not want to do this).

  • : , ( ).

-,

, , . , -, .

, , - , "" PNG:

The result that I expect is a cube displayed in part

, .

The faces of my cube are odd, as if the prospect were not well used.

, , ...

, (.. ) . fov, ( ).

, ( ), , , ( x y) , ( x y) , ( ) 1 z .

Scastie ()

: X11 Scastie, , , .

https://scastie.scala-lang.org/N95TE2nHTgSlqCxRHwYnxA

, ? , .

Ref.: self

val world_cube_points : Seq[Seq[Double]] = Seq(
  Seq(100, 300, -4, 1), // top left
  Seq(100, 300, -1, 1), // top left z+1
  Seq(100, 0, -4, 1), // bottom left
  Seq(100, 0, -1, 1), // bottom left z+1
  Seq(400, 300, -4, 1), // top right
  Seq(400, 300, -1, 1), // top right z+1
  Seq(400, 0, -4, 1), // bottom right
  Seq(400, 0, -1, 1) // bottom right z+1
)

()

Ref.: https://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix/building-basic-perspective-projection-matrix, . " "

, : fov, .

new Matrix(Seq(
  Seq(1, 0, 0, 0),
  Seq(0, 1, 0, 0),
  Seq(0, 0, -1, 0),
  Seq(0, 0, -1, 0)
))

: P(x;y;z;w), , : P'(x;y;-z;-z).

-, , : .

Ref.: https://github.com/ssloy/tinyrenderer/wiki/Lesson-4:-Perspective-projection#homogeneous-coordinates

/**
  * Matrix in the shape of (use of homogeneous coordinates) :
  * c00 c01 c02 c03
  * c10 c11 c12 c13
  * c20 c21 c22 c23
  *   0   0   0   1
  *
  * @param content the content of the matrix
  */
class Matrix(val content : Seq[Seq[Double]]) {

  /**
    * Computes the product between a point P(x ; y ; z) and the matrix.
    *
    * @param point a point P(x ; y ; z ; 1)
    * @return a new point P'(
    *         x * c00 + y * c10 + z * c20
    *         ;
    *         x * c01 + y * c11 + z * c21
    *         ;
    *         x * c02 + y * c12 + z * c22
    *         ;
    *         1
    *         )
    */
  def product(point : Seq[Double]) : Seq[Double] = {
    (0 to 3).map(
      i => content(i).zip(point).map(couple2 => couple2._1 * couple2._2).sum
    )
  }

}

,

Ref. 1/2: . " " https://www.scratchapixel.com/lessons/3d-basic-rendering/computing-pixel-coordinates-of-3d-point/mathematics-computing-2d-coordinates-of-3d-points

Ref. 2/2: https://github.com/ssloy/tinyrenderer/wiki/Lesson-4:-Perspective-projection#time-to-work-in-full-3d

NB: , ( : ).

class Projector {

  /**
    * Computes the coordinates of the projection of the point P on the canvas.
    * The canvas is assumed to be 1 unit forward the camera.
    * The computation uses the definition of the similar triangles.
    *
    * @param points the point P we want to project on the canvas. Its coordinates must be expressed in the coordinates
    *          system of the camera before using this function.
    * @return the point P', projection of P.
    */
  def drawPointsOnCanvas(points : Seq[Seq[Double]]) : Seq[Seq[Double]] = {
    points.map(point => {
      point.map(coordinate => {
        coordinate / point(3)
      }).dropRight(1)
    })

  }

}

, .

Ref.: . " " https://www.scratchapixel.com/lessons/3d-basic-rendering/computing-pixel-coordinates-of-3d-point/mathematics-computing-2d-coordinates-of-3d-points

import java.awt.Graphics
import javax.swing.JFrame

/**
  * Assumed to be 1 unit forward the camera.
  * Contains the drawn points.
  */
class Canvas(val drawn_points : Seq[Seq[Double]]) extends JFrame {

  val CANVAS_WIDTH = 820
  val CANVAS_HEIGHT = 820
  val IMAGE_WIDTH = 900
  val IMAGE_HEIGHT = 900

  def display = {
    setTitle("Perlin")
    setSize(IMAGE_WIDTH, IMAGE_HEIGHT)
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
    setVisible(true)
  }

  override def paint(graphics : Graphics): Unit = {
    super.paint(graphics)
    drawn_points.foreach(point => {

      if(!(Math.abs(point.head) <= CANVAS_WIDTH / 2 || Math.abs(point(1)) <= CANVAS_HEIGHT / 2)) {
        println("WARNING : the point (" + point.head + " ; " + point(1) + ") can't be drawn in this canvas.")
      } else {
        val normalized_drawn_point = Seq((point.head + (CANVAS_WIDTH / 2)) / CANVAS_WIDTH, (point(1) + (CANVAS_HEIGHT / 2)) / CANVAS_HEIGHT)
        graphics.fillRect((normalized_drawn_point.head * IMAGE_WIDTH).toInt, ((1 - normalized_drawn_point(1)) * IMAGE_HEIGHT).toInt, 5, 5)

        graphics.drawString(
          "P(" + (normalized_drawn_point.head * IMAGE_WIDTH).toInt + " ; "
          + ((1 - normalized_drawn_point(1)) * IMAGE_HEIGHT).toInt + ")",
          (normalized_drawn_point.head * IMAGE_WIDTH).toInt - 50, ((1 - normalized_drawn_point(1)) * IMAGE_HEIGHT).toInt - 10
        )
      }
    })
  }

}

? , , . , . , , () ...

+4
2

, : fov, .

, . , .

z-, , . , " frustrum", , .

. http://www.songho.ca/opengl/gl_projectionmatrix.html , .

+1

Scratchapixel :

... , :

enter image description here

y' - y P'. :

enter image description here

, , , z . x....

:

def drawPointsOnCanvas(points : Seq[Seq[Double]]) : Seq[Seq[Double]] = {
    points.map(point => {
      point.map(coordinate => {
        coordinate / point(3)
                     ^^^^^^^^
    ...

(3) 4- point, .. W-, Z-. , coordinate / point(2)?

+1

Source: https://habr.com/ru/post/1694408/


All Articles