首页 > 解决方案 > 使用 ARCore 捕捉地板纹理

问题描述

我正在尝试根据 ARCore 检测到的平面和环境(相机)纹理来捕捉“地板纹理”。然后在平面网格中重新应用此地板纹理,创建基于现实的数字地板。我上传了一张图片来说明这一点:

插图

这不是 ARCore 特定的问题,我认为它可以通过数学和图形编程来解决,可能类似于基于相机矩阵取消投影平面,但我不知道具体该怎么做。

有人能帮我吗?谢谢!!

标签: unity3dmathgraphicsarcore

解决方案


本质上,我们在这个问题中有三个重要的坐标系:

坐标系

我们有可以任意定义的常用 3D 世界坐标系。我们有一个平面的二维坐标系。该坐标系的原点位于平面的中心(请注意,为此我使用与矩形同义的术语平面),坐标范围从 -1 到 +1。最后,我们得到了图像的二维坐标。实际上,我们有两个图像:一个无符号坐标系(如图所示),原点在左下角,坐标范围为 0 到 1,一个有符号坐标系,坐标范围为 -1 到 1。

我们知道我们平面在世界空间中的四个角和 3x4 视图/投影矩阵P,它允许我们使用齐次坐标将世界空间中的任何点投影到图像空间:

p_image,signed = P * p_world

如果您的投影矩阵是 4x4,只需删除第三行(最后一行),因为我们对图像空间深度不感兴趣。

我们并不真正关心世界空间,因为这有点武断。给定平面空间中的二维点,我们可以使用以下方法将其转换为世界空间:

p_world = 1/4 (p0 + p1 + p2 + p3) + u * 1/2 * (p1 - p0) + v * 1/2 * (p3 - p0)

第一部分是平面的原点,第二项和第三项中的点差是坐标轴。我们可以将其表示为矩阵形式

/ x \   / p1_x - p0_x   p2_x - p0_x   1/4 (p0_x + p1_x + p2_x + p3_x) \   / u \
| y | = | p1_y - p0_y   p2_x - p0_x   1/4 (p0_y + p1_y + p2_y + p3_y) | * | v |
| z |   | p1_z - p0_z   p2_x - p0_x   1/4 (p0_z + p1_z + p2_z + p3_z) |   \ 1 /
\ 1 /   \      0             0                         1              /

我们称这个矩阵为M

现在,我们可以直接从平面空间到图像空间:

p_image,signed = P * M * p_plane

该矩阵P * M现在是一个 3x3 矩阵。它是地平面和图像平面之间的单应性。

那么,我们能用它做什么呢?我们可以使用它在平面空间中绘制图像。所以,这就是我们要做的:

我们将生成一个渲染目标,我们将使用一个绘制调用填充该目标。然后,此渲染目标将包含我们平面的纹理。为了绘制这个,我们:

  1. 将相机图像作为纹理上传到 GPU
  2. 绑定渲染目标
  3. 绘制带角的全屏四边形(-1, -1), (1, -1), (1, 1), (-1, 1)
  4. 在顶点着色器中,从平面空间计算图像空间中的纹理坐标
  5. 在像素着色器中,在插值纹理坐标处对相机图像进行采样

有趣的部分是数字 4。我们几乎知道我们需要做什么。我们已经知道如何去签名图像空间。现在,我们只需要转到未签名的图像空间。这是一个简单的转变和规模:

                   / 1/2  0  1/2 \
p_image,unsigned = |  0  1/2 1/2 | * p_image,signed
                   \  0   0   1  /

如果我们调用这个矩阵S,我们就可以计算S * P * M得到一个 3x3 矩阵T。此矩阵可用于顶点着色器,以根据您传入的平面空间点计算纹理坐标:

texCoords = p_image,unsigned = T * p_plane

将整个 3D 矢量传递给片段着色器并仅在像素着色器中进行透视分割以产生正确的透视图,这一点很重要。


推荐阅读