Я написал программу, которая принимает некоторые точки, выраженные в трехмерных координатах, которые должны быть нарисованы на двухмерном холсте. Для этого я использую перспективную проекцию, однородные координаты и подобные треугольники. Однако моя программа не работает, и я даже не знаю почему.
Я следил за двумя уроками. Я действительно понял геометрические определения и свойства, которые я прочитал. Однако моя реализация терпит неудачу ... Я буду писать ссылки на оба этих курса понемногу, чтобы вам было удобнее читать :).
Обзор: геометрические напоминания
Перспективная проекция выполняется в соответствии с этим рабочим процессом (см. Эти 2 курса - я написал соответствующие ссылки о последних (привязки HTML) ниже, в этом посте):
Определение точек для рисования, выраженных в мировой системе координат; Определение матрицы проекции, которая представляет собой матрицу преобразования, которая «преобразует» точку, выраженную в соответствии с мировой системой координат, в точку, выраженную в соответствии с системой координат камеры (примечание: я считаю, что эту матрицу можно понимать как трехмерную объект "камера")
Произведение этих точек с этой матрицей (как определено в соответствующей части ниже в этом документе): произведение этих выраженных в мире точек приводит к преобразованию этих точек в систему координат камеры. Обратите внимание, что точки и матрица выражаются в 4D (концепция однородных координат).
Использование аналогичной концепции треугольников для проецирования (на этом этапе выполняются только вычисления) на холсте точек, отображаемых в камере (с использованием их 4-мерных координат). После этой операции точки теперь выражаются в 3D (третья координата вычисляется, но фактически не используется на холсте). 4-я координата удалена, потому что она бесполезна. Обратите внимание, что третья координата не будет полезна, кроме как для обработки z-боя (хотя я не хочу этого делать).
Последний шаг: растеризация, чтобы фактически нарисовать пиксели на холсте (на этом шаге выполняются другие вычисления и отображение).
Во-первых, проблема
Ну, я хочу нарисовать куб, но перспектива не работает. Спроецированные точки кажутся нарисованными без перспективы.
На какой результат мне стоит рассчитывать
Результат, которого я ожидаю, - это куб, отображаемый в части «Изображение» ниже PNG:
То, что я вывожу
Грани моего куба странные, как будто перспектива использовалась неправильно.
Думаю, я знаю, почему у меня такая проблема ...
Я думаю, что моя матрица проекции (например, камера) не имеет хороших коэффициентов. Я использую очень простую матрицу проекции без концепции fov, близкой к и дальние плоскости отсечения (как вы можете видеть ниже).
Действительно, чтобы получить ожидаемый результат (как определено ранее), камеру следует разместить, если я не ошибаюсь, в центре (по осям x и y). куба, выраженного в соответствии с мировой системой координат и в центре (по осям x и y) холста, который (я делаю это предположение) поместил 1 z перед камерой.
Скэсти (отрывок)
NB: поскольку X11 не активирован на Scastie, окно, которое я хочу создать, не будет отображаться.
https://scastie.scala-lang.org/N95TE2nHTgSlqCxRHwYnxA
Записи
Возможно проблема связана с записями? Что ж, даю вам их.
Очки куба
Ref. : сам
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#homogen-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/matMathematics-computing-2d-координаты-3d-точек
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/matMathematics-computing-2d-координаты3D-точек
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
)
}
})
}
}
Вопрос
Что не так с моей программой? Я понял геометрические концепции, объясняемые в обоих руководствах, которые я внимательно прочитал. Я почти уверен, что мой продукт работает. Я думаю, что либо растеризация, либо записи (матрица) могут быть неправильными ...
Projector::drawPointOnCanvas
использует похожие треугольники для проецирования точек, выраженных в соответствии с системой координат камеры, на холсте. (Холст просто норм. И растр) - person JarsOfJam-Scheduler   schedule 04.03.2018