hyp*_*one 5 3d graphics perspectivecamera perspective camera-calibration
我正在尝试从图片的消失点和地平线开始计算 3D 对象的精确预期变换。
我想要的是,固定图片的消失点和水平线,我想根据我从图片开始设置的消失点和水平线旋转和倾斜3D对象
最终结果低于我的预期。
我怎样才能得到这个结果?
我可以使用什么样的转换?
在这个视频中可以看到我想要的结果。
这远不是我喜欢的,所以要带着极端的偏见和遥远的解决方案来处理,只是一些起点提示......
首先,我们需要定义一些约束/假设才能使其发挥作用。
QUAD
平面具有相同的高度和高度。此外,物体高度轴几乎与相机 y 轴相同(不是旋转图像)。所以你想要获得的是一个 4x4 的齐次矩阵,它将一些全局 3D 坐标转换为 2D 图像坐标 + 透视除法。
|x'| | Xx Yx Zx Ox | |x|
|y'| = | Xy Yy Zy Oy | .|y|
|z'| | Xz Yz Zz Oz | |z|
|w'| | a b c 1 | |1|
Run Code Online (Sandbox Code Playgroud)
其中(x,y,z)
代表某个 3D 位置,(x'/z',y'/z')
代表屏幕(图像)上的 2D 位置。为了简单起见,我们假设图像中心是(0,0)
。
要构建透视矩阵,您需要知道相机的FOV角度及其焦距znear
。没有它,您可以根据图像上的已知内容来估计它......
另一种选择是拟合该矩阵直到点匹配。但由于有 15 个未知数,速度会非常慢(即使许多参数是相关的并且可以根据其他参数计算)。
[完整重新编辑] 简单的 C++ 方法示例
两个四人组
我将从计算每个平面的四边形开始:
为了简化以后的代码,这些点应该有特定的顺序。我以编程方式对它们进行排序,使它们逆时针旋转,并且每个四边形的第一个点位于右上角。第一个 QUAD 位于右侧(代表建筑物的 Z 轴或 YZ 平面),第二个位于左侧(代表建筑物的 X 轴或 XY 平面)。
我还计算每个四边形的中点(平均点),并按屏幕 x 轴该点与排序点之间的角度对点进行排序。之后,需要进行位置校正(如果屏幕 x 轴与四边形水平轴碰撞,则将所有点移动 1),因此四边形的第一个点是右上角。
现在我们需要将线变成 QUAD。为此,我们需要建筑物 y 轴方向...首先,我从 4 条线中的每条线投射 2D 法线,并将它们平均在一起。请注意,它们应该都在同一方向......因此,如果添加法线具有负点积,则在添加之前将平均值取反。该平均法线是 XY 平面上的 UP 矢量投影。
但后来我改变了这一点,我计算了相应的左右 QUAD 水平线之间的 2 个交点(获得 QUAD 之间的建筑物边缘的 UP 向量/方向)。事实证明,这更准确,也更容易计算。
现在,要将线转换为四边形,只需找到线与从每个平面之一线的端点投射的法线之间的交点即可。之后,交叉点将与四角完全对齐,因此我们可以从现在开始使用它......
看法
由于我们的建筑物很可能是一个平面之间具有直角的盒子,因此我们的 2 个四边形在 3D 中也应该彼此垂直。我们可以使用这个...就好像我们将它们的消失点与中点连接起来一样,3D 中的线也应该是 90 度直角。所以我们可以直接从中获得FOVx角度......
FOVx
因此和之间的比率90.0deg
与屏幕 x 分辨率和 2 个消失点距离(以像素为单位)之间的比率相同......因此:
FOVx = 90.0*deg * image_x_resolution / intersections_x_distance
Run Code Online (Sandbox Code Playgroud)
我们也知道屏幕分辨率也znear
可以直接计算。例如我使用<-1,+1>
OpenGL 中的屏幕坐标:
znear = 1.0/tan(0.5*FOVx)
Run Code Online (Sandbox Code Playgroud)
粗略地说,这会影响结果的整体规模,所以不要指望米单位......
应该zfar
明智地选择,以便建筑物实际上位于视锥体中。例如:
zfar = 1000.0*znear
Run Code Online (Sandbox Code Playgroud)
它只影响相对于znear
...的视图深度,但不会影响透视本身。
构建 3D 坐标
QUAD 垂直线尺寸为我们提供了取决于深度的比例。这可以用来计算我们拥有的每个点的 Z 坐标......但为此我们需要知道 QUAD 的原始高度。对我们来说幸运的是,QUAD 到 3D 的未投影 2D 屏幕坐标应该形成直角。因此,如果我们使用 3 个点(四边形中点和它们之间的边的中点)并对未投影线方向进行点积,结果应该为零。所以我们得到了 4 个方程和 4 个可代数求解的未知数......
深度关系如下:
scale(z) = znear/z
Run Code Online (Sandbox Code Playgroud)
因此,如果我们计算我们所讨论的点所在位置的 QUAD 高度,我们就可以获得相对于原始 QUAD 高度的比例l
...因为我们有 3 个点:
z0 = znear*l0/l
z1 = znear*l1/l
z2 = znear*l2/l
dot(pnt1-pnt0,pnt2-pnt0)=0
Run Code Online (Sandbox Code Playgroud)
其中未投影点:pnt0(x0,y0,z0)
是 QUAD 之间的边的中点, 和pnt1(x1,y1,z1)
是pnt2(x2,y2,z2)
QUAD 的中点。这l0,l1,l2
是相应的高度尺寸。所以这里唯一的未知数是z0,z1,z2,l
......
顺便说一句,这些未投影的点直接为我们提供了 2 个基向量和建筑物坐标系的位置。所以我们也可以组成它的矩阵...第三个也可以不投影或使用叉积代替...
这里是一个带有反向透视叠加的调试渲染立方体:
正如您所看到的,拟合并不完美,这是由于我的 3D 视图中存在一些与查看窗口纵横比相关的错误。如果窗口是方形的(不是图像,只是 GL 窗口),那么拟合是完美的。如果我将纵横比添加到 3D 视图(比例),则拟合是完美的,但坐标系的基本向量在视觉上的大小不同...需要更多地考虑它来修复...它很可能是一些愚蠢的简单的事情根本与反转视角无关......这里是方形视图屏幕截图:
这是我的实际 C++/GL 代码...但要注意我正在使用渲染引擎中的一些东西(如向量数学等...)
|x'| | Xx Yx Zx Ox | |x|
|y'| = | Xy Yy Zy Oy | .|y|
|z'| | Xz Yz Zz Oz | |z|
|w'| | a b c 1 | |1|
Run Code Online (Sandbox Code Playgroud)
这里重要的东西是计算函数,它将 QUAD 点反转为透视参数/矩阵和坐标系...其余的只是用于渲染/调整大小和鼠标处理...