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:

, .

, , ...
, (.. ) . 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
class Matrix(val content : Seq[Seq[Double]]) {
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 {
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
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
)
}
})
}
}
? , , . , . , , () ...