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 中实现。所以我建议如下:
- 使用Observer相机
创建想要的场景
- 在屏幕某处添加Plane(程序!)和Main相机,主相机正在平面
- 将主相机设置为玩家的主相机
- 创建 material 作为 RenderTarget (tutorial) 并将 Observer 相机设置为 material.
的来源
现在您已经设置好在 平面 上渲染场景,玩家 将看到该平面。
如果需要模拟梯形校正,可以改变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 并排渲染以获得并排重新投影。
让我们来看一个代理问题 - 您正在查看一些 3D 场景。现在,我将场景替换为从您的眼睛位置完成的渲染,因此您看不出有任何区别。然后我用墙壁和投影仪替换渲染图像。我需要的是一张图像,当投影到墙上时看起来就像您正在看场景一样。
看起来像这样:
我的方法是从观察者位置渲染场景,然后在 post 过程中我对渲染图像进行采样以添加失真。
我有一些概念验证代码,可以处理一些我需要调试的偏移量,但大部分计算是在像素着色器中完成的,因此它不是最佳解决方案。
在完成初始版本后,我阅读了有关单应矩阵的信息,它似乎是满足我需求的正确工具。如果我理解正确,我应该能够计算单应矩阵,然后只将我的屏幕 space UV 与其相乘以获得重新投影的 UV。
不幸的是,我能找到的大部分关于单应性的信息都与我有某个物体的 2 张图片、手动挑选 4 个对应点对并从中计算矩阵的情况有关,但我没有这样的点.相反,我知道两个视图的确切变换,它们的垂直和水平 FOV 和平面,所以我认为这就是我所需要的。
完美的解决方案是进行一些变换,将我的图像-space UV 映射到 [0,1] 范围内,以校正纹理采样的 UV。你们有没有看到类似的解决方案?
编辑:
我已经截取了我的 POC 实现:
从我们的 discussion 来看,梯形校正未在 UE 中实现。所以我建议如下:
- 使用Observer相机 创建想要的场景
- 在屏幕某处添加Plane(程序!)和Main相机,主相机正在平面
- 将主相机设置为玩家的主相机
- 创建 material 作为 RenderTarget (tutorial) 并将 Observer 相机设置为 material. 的来源
现在您已经设置好在 平面 上渲染场景,玩家 将看到该平面。
如果需要模拟梯形校正,可以改变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
矩阵的符号。
这是屏幕:
为了计算,我使用了 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 并排渲染以获得并排重新投影。