My impracticable perspective projection

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 after this workflow (see these 2 courses - I wrote the appropriate links (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: this matrix can also be understood as a camera)

  • The product of these points with this matrix (as defined in the corresponding part below): the product of these points 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).

  • ( ) , ( 4D-): 3D ( , )

  • : , ( ).

-,

, , . , -, .

.

enter image description here

Scastie ()

: X11 Scastie, , , .

https://scastie.scala-lang.org/2LQ1wSMBTWqQQ7hql35sOg

, ? , .

Ref.: self

val world_cube_points : Seq[Seq[Double]] = Seq(
  Seq(0, 40, 0, 1),
  Seq(0, 40, 10, 1),
  Seq(0, 0, 0, 1),
  Seq(0, 0, 10, 1),
  Seq(20, 40, 0, 1),
  Seq(20, 40, 10, 1),
  Seq(20, 0, 0, 1),
  Seq(20, 0, 10, 1)
)

()

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

val matrix_world_to_camera : Matrix = new Matrix(Seq(
  Seq(1, 0, 0, 0),
  Seq(0, 1, 0, 0),
  Seq(0, 0, 1, 0),
  Seq(0, 0, -1, 1)
))

-, , : .

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 = 60
  val CANVAS_HEIGHT = 60
  val IMAGE_WIDTH = 55
  val IMAGE_HEIGHT = 55

  def display = {
    setTitle("Perlin")
    setSize(CANVAS_WIDTH, CANVAS_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.drawRect(normalized_drawn_point.head.toInt * IMAGE_WIDTH, (1 - normalized_drawn_point(1).toInt) * IMAGE_HEIGHT, 1, 1)
      }
    })
  }

}

...

object Main {
  def main(args : Array[String]) : Unit = {
    val projector = new Projector()

    val world_cube_points : Seq[Seq[Double]] = Seq(
      Seq(0, 40, 0, 1),
      Seq(0, 40, 10, 1),
      Seq(0, 0, 0, 1),
      Seq(0, 0, 10, 1),
      Seq(20, 40, 0, 1),
      Seq(20, 40, 10, 1),
      Seq(20, 0, 0, 1),
      Seq(20, 0, 10, 1)
    )

    val matrix_world_to_camera : Matrix = new Matrix(Seq(
      Seq(1, 0, 0, 0),
      Seq(0, 1, 0, 0),
      Seq(0, 0, 1, 0),
      Seq(0, 0, -1, 1)
    ))

    val points_to_draw_on_canvas = projector.drawPointsOnCanvas(world_cube_points.map(point => {
      matrix_world_to_camera.product(point)
    }))
    new Canvas(points_to_draw_on_canvas).display

  }
}

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

+1
1

toInt ( , [0, 1]):

normalized_drawn_point.head.toInt * IMAGE_WIDTH
                            ----- 

0 1, . :

(normalized_drawn_point.head * IMAGE_WIDTH).toInt

( * (IMAGE_WIDTH - 1), , . .)

+1

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


All Articles