post-process 中的平面重投影使用单应矩阵

Plane reprojection in post-process using homography matrix

让我们来看一个代理问题 - 您正在查看一些 3D 场景。现在,我将场景替换为从您的眼睛位置完成的渲染,因此您看不出有任何区别。然后我用墙壁和投影仪替换渲染图像。我需要的是一张图像,当投影到墙上时看起来就像您正在看场景一样。

看起来像这样: 左边的摄像头是观察者,右边的是投影仪。

我的方法是从观察者位置渲染场景,然后在 post 过程中我对渲染图像进行采样以添加失真。

我有一些概念验证代码,可以处理一些我需要调试的偏移量,但大部分计算是在像素着色器中完成的,因此它不是最佳解决方案。

在完成初始版本后,我阅读了有关单应矩阵的信息,它似乎是满足我需求的正确工具。如果我理解正确,我应该能够计算单应矩阵,然后只将我的屏幕 space UV 与其相乘以获得重新投影的 UV。

不幸的是,我能找到的大部分关于单应性的信息都与我有某个物体的 2 张图片、手动挑选 4 个对应点对并从中计算矩阵的情况有关,但我没有这样的点.相反,我知道两个视图的确切变换,它们的垂直和水平 FOV 和平面,所以我认为这就是我所需要的。

完美的解决方案是进行一些变换,将我的图像-space UV 映射到 [0,1] 范围内,以校正纹理采样的 UV。你们有没有看到类似的解决方案?

编辑:

我已经截取了我的 POC 实现: 我不确定它 1:1 是否正确,但它显示了我的需要。渲染失真,但当我将此图像投影到墙上时,这种失真应该会被抵消。请看一下 table 顶部 - 它看起来仍然像是从侧面渲染的,而不是从投影仪渲染的。当我将此图像投影到墙上时,它应该看起来像从您的眼睛位置渲染的那样。

从我们的 discussion 来看,梯形校正未在 UE 中实现。所以我建议如下:

  1. 使用Observer相机
  2. 创建想要的场景
  3. 在屏幕某处添加Plane程序!)和Main相机,相机正在平面
  4. 相机设置为玩家的主相机
  5. 创建 material 作为 RenderTarget (tutorial) 并将 Observer 相机设置为 material.
  6. 的来源

现在您已经设置好在 平面 上渲染场景,玩家 将看到该平面。

如果需要模拟梯形校正,可以改变Plane的位置或旋转。 (您可以将该操作绑定到您的设置 GUI 等)并且由于它是 程序化的 ,您可以以垂直或水平方式更改大小(和形状)。 (UE docs)

因为你有一个具有恒定 UV 的平面并且你正在改变它的形状,结果将是该平面上的梯形效果,这(在正确的平面设置的情况下)将导致硬件投影仪上的梯形效果相反从而生成正确的图像。

对于设置,您只需要重置平面上的所有设置(旋转、位置、比例等)并设置另一个 Material,最好使用 square( s) - 你可以很容易看到的东西,它可以帮助你正确地修改 Plane。所以它只是搞乱了投影平面。

我终于找到了解决办法。

在一些关于 Homography 的文章中,我看到了一个从两个视口的已知变换计算 H 矩阵的方程式,但无法让它工作,也许我误解了什么或者我的数学有误所以我决定尝试 4 点方法,但如何自动获得这些点数?

考虑到我将在 post 渲染的处理阶段使用它,因此主要在屏幕上操作 -space 我决定将目标视口的角映射到我的源视口。

Projector 变换得到 forward direction 并将其旋转一半的垂直和水平 FOV。结合正值和负值,我可以获得对应于我视口的 4 个角的 4 个向量,在 World space.

中计算

使用 Projector 位置作为起点,使用 4 个计算向量作为方向,我可以计算出与 Plane 的 4 个交点。这些是 World-space 点,位于我的 Plane 上,代表 what is visible from projector.

的角

有了这 4 个点,我用 Observer 相机将它们投射到它的屏幕上-space。

我在这里得到的点是 Observer-screen-space 点,鉴于我已经映射了 Projector 的整个视口,我可以使用典型的 [0,0],[1,0],[0,1],[1,1] 矩形作为 Projector-screen-space points .

这样我就可以 four corresponding pairs of points 与我的单应术一起使用了。

单应

这有点棘手,因为我仍然不完全理解这里的数学,但我用过这个:http://www.cse.psu.edu/~rtc12/CSE486/lecture16.pdf 文章的第 29 页有一个使用 8x8 方阵的矩阵方程。 link 可能会消失,但你可以在很多地方找到相同的方程式,重要的是我使用了 8x8 版本,但看到使用 8x9 矩阵的符号。

这是屏幕:

一般来说,它是一个 A*x=b 符号,我们知道 A 和 b 并想要计算 x。

为了计算,我使用了 Eigen 库中的 JacobiSVD class。

得到 8 个浮点数后,我可以构建 3 个我正在寻找的矩阵的行向量。前 3 个值形成第一个向量,另外 3 个值形成第二个向量,然后我们只剩下 2 个值,所以我们附加一个 1.0f 以获得最后一个向量。

我们称这个矩阵为H

像素着色器

每帧完成所有这些计算后,Pixel shader 非常简单 - 我们只需要将 screen-space UV 向量转换为 H 矩阵(但在齐次坐标系中)。

我们得到 u,v 值并附加 1.0 得到 [u,v,1.0] 然后我们将它与 H 矩阵相乘。我们已将此矩阵作为行向量传递,因此我们可以将 [u,v,1.0] 向量与 3 行进行点积并对结果求和。这样我们得到了类似 [x,y,z] 的结果,但实际上它意味着 [x/z,y/z] 2D 向量 - 这就是我们正在寻找的 UV。现在我只用这个 UV 采样纹理就完成了。

为什么它比 Pavel 方法更好?

我不需要单独的几何渲染,这在延迟渲染引擎(如 Unreal)中特别慢。

我一次可以在不同的平面上使用几个这样的纹理。我可以,例如通过几个 alpha 蒙版和纹理平面并决定我击中哪个。

我可以通过分别处理屏幕的每一半来重新投影 3D 并排渲染以获得并排重新投影。