о том, как он построен, и от каких переменных камеры это зависит.
исал программу, которая вводит некоторые точки, выраженные в трехмерных координатах, и которые должны быть нарисованы на двухмерном холсте. Я использую перспективную проекцию, однородные координаты и подобные треугольники, чтобы сделать это. Однако моя программа не работает, и я на самом деле не знаю, почему.
Я следовал двум урокам. Я действительно понял геометрические определения и свойства, которые я прочитал. Однако моя реализация терпит неудачу ... Я буду писать ссылки на эти оба курса понемногу, чтобы сделать ваше чтение более удобным :).
Обзор: геометрические напоминанияПерспективная проекция выполняется в соответствии с этим рабочим процессом (см. Эти 2 курса - я написал соответствующие ссылки (якоря HTML) ниже, в этом посте):
Определение точек для рисования, выраженных в соответствии с мировой системой координат; Определение матрицы проекции, которая представляет собой матрицу преобразования, которая «преобразует» точку, выраженную в соответствии с мировой системой координат, в точку, выраженную в соответствии с системой координат камеры (примечание: эту матрицу также можно понимать как камеру)
Произведение этих точек с этой матрицей (как определено в соответствующей части ниже): произведение этих точек приводит к преобразованию этих точек в систему координат камеры. Обратите внимание, что точки и матрица выражены в 4D (концепция однородных координат).
Использование аналогичной концепции треугольников для проецирования (на этом этапе выполняется только вычисление) на холст выраженные в камере точки (с использованием их четырехмерных координат): теперь они выражаются в трехмерном формате (третья координата вычисляется, но фактически не используется в холст)
Последний шаг: растеризация, чтобы на самом деле нарисовать пиксели на холсте (на этом этапе выполняется другое вычисление И отображение).
Во-первых, проблемаНу, я хочу нарисовать куб, но он не появляется. Спроецированные точки, кажется, нарисованы на тех же координатах.
Вместо моего куба виден только один черный пиксель.
Scastie (фрагмент)Примечание: поскольку X11 не активирован в Scastie, окно, которое я хочу создать, не будет показано.
https://scastie.scala-lang.org/2LQ1wSMBTWqQQ7hql35sOg
ЗаписиВозможно, проблема связана с записями? Ну, я даю вам их.
Очки кубаСсылка : себя
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)
)
Матрица преобразования (проекции)Ссылка :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)
))
Во-вторых, первая операция, которую выполняет моя программа: простое произведение точки с матрицей.Ссылка :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
)
}
}
Затем используйте похожие треугольникиСсылка 1/2: часть «Важность преобразования точек в пространство камеры»https://www.scratchapixel.com/lessons/3d-basic-rendering/computing-pixel-coordinates-of-3d-point/mathematics-computing-2d-coordinates-of-3d-points
Ссылка 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)
})
}
}
Наконец, рисование проецируемых точек на холсте.Ссылка : Часть. "Из пространства экрана в пространство растра"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
}
}
ВопросЧто не так с моей программой? Я понял геометрические концепции, объясненные этими обоими уроками, которые я внимательно прочитал. Я уверен, что мой продукт работает. Я думаю, что или растеризация, или записи (матрица) могут быть неправильными ...