Scala 透视投影的非工作实现
我写了一个程序,它接收一些入口点,用三维坐标表示,必须在二维画布上绘制。我使用透视投影、齐次坐标和类似的三角形来实现这一点。然而,我的程序不起作用,我也不知道为什么 我学习了两个教程。我真的理解了我读过的几何定义和性质。然而,我的实现失败了。。。我将一点一点地写这两门课程的参考资料,使你们的阅读更容易理解:) 概述:几何提醒 透视投影是按照这个工作流程完成的(参见这两门课程-我在这篇文章中进一步写下了相关链接(HTML锚):Scala 透视投影的非工作实现,scala,geometry,projection,perspective,Scala,Geometry,Projection,Perspective,我写了一个程序,它接收一些入口点,用三维坐标表示,必须在二维画布上绘制。我使用透视投影、齐次坐标和类似的三角形来实现这一点。然而,我的程序不起作用,我也不知道为什么 我学习了两个教程。我真的理解了我读过的几何定义和性质。然而,我的实现失败了。。。我将一点一点地写这两门课程的参考资料,使你们的阅读更容易理解:) 概述:几何提醒 透视投影是按照这个工作流程完成的(参见这两门课程-我在这篇文章中进一步写下了相关链接(HTML锚): 根据世界坐标系定义要绘制的点;投影矩阵的定义,它是一个变换矩阵,将根据
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)
)
变换(投影)矩阵
参考号:
其次,我的程序执行的第一个操作是:一个点与一个矩阵的简单乘积。
参考号:
然后,使用相似的三角形
参考文献1/2:第1部分。“将点转换为摄影机空间的重要性
“的
参考文献2/2:
注意:在此步骤中,条目是根据相机表示的点(即:它们是具有预先定义的矩阵的预先定义的产品的结果)
最后,在画布上绘制投影点。
参考:第二部分。“从屏幕空间到光栅空间”
问题:
我的程序怎么了?我理解了这两个教程所解释的几何概念,我仔细阅读了这两个教程。我很肯定我的产品能用。我认为光栅化或条目(矩阵)可能是错误的…您在标准化设备坐标上调用了
toInt
(表示有效范围为[0,1]):
这将使其四舍五入为0或1,以便所有点都位于屏幕的边界上。仅在乘以屏幕分辨率后进行四舍五入:
(normalized_drawn_point.head * IMAGE_WIDTH).toInt
(从技术上讲,如果屏幕坐标从零开始,它应该是
*(图像宽度-1)
,这是非常常见的。垂直坐标也是如此。)啊,谢谢!我可以看到类似立方体的东西,即使它不完美。你知道投影矩阵应该用什么值吗?或者我随机选择它们?@JarsOfJam。投影矩阵的值有特定的含义,所以你不能只是随机选择它们。它是如何构造的,以及它所依赖的摄影机变量。
/**
* 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
)
}
}
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)
})
}
}
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
}
}
normalized_drawn_point.head.toInt * IMAGE_WIDTH
-----
(normalized_drawn_point.head * IMAGE_WIDTH).toInt