【问题标题】:Project point on 2D triangle back into 3D?将 2D 三角形上的点投影回 3D?
【发布时间】:2020-11-30 00:42:30
【问题描述】:

我不确定要搜索什么,所以我找不到我需要的。

假设我有一个带有点 [0, 1, 1], [1, 0.5, 0.5], [0, 0, 0] 的 3D 三角形。我丢弃 Z 组件以创建一个带有点 [0, 1], [1, 0.5], [0, 0] 的二维三角形。 (我认为这是一个正交投影?)通过一个不重要的过程,我发现了一些位于二维三角形内的二维点,比如[0.5, 0.5]

我如何获取那个 2D 点并找到它的 Z 值应该是多少才能让它位于由原始 3D 三角形形成的平面上?

通过代码而不是数学符号描述数学的答案(或重复链接!)将不胜感激;我很难阅读你在Math.SE 上得到的答案类型。

【问题讨论】:

  • 原始三角形的平面将有一个类似z = a*x + b*y + c 的方程。找到那个等式,然后插入xy
  • @JohnColeman 我不确定你的意思。 abc 是三角形的点吗?我不确定你是不是说这个方程以某种方式给了我 Z 值。
  • 非垂直线的二维方程可以写成y = a*x + b。类似地,3维非垂直平面的方程可以写成z = a*x + b*y + c。有问题的a,b,c 是标量,可以通过各种方式获得。一种标准方法是形成向量PQPR 的叉积(其中P,Q,R 是原始三角形中的点。)。 a,b,c 可以很容易地从该叉积中获得。搜索“通过 3 点的平面方程”。
  • @JohnColeman 老实说,我希望自己能少一点。如果我必须自己弄清楚数学,我会犯错误并浪费数小时试图在我并不真正理解的东西中找到错误。如果我必须在谷歌上搜索“[whatever] 的方程式”,我就完蛋了,因为我只在通过代码描述数学时才理解数学,除非它是简单的代数。这是我教育的失败,我知道。当你说“二维非垂直线的方程可以写成y = a*x + b”之类的话时,我只能茫然地盯着它。 y是什么? x、a 和 b 是什么?这如何转化为代码?
  • 也许this link 会有所帮助。

标签: math projection


【解决方案1】:

你可以使用barycentric coordinates...

所以你得到了 2D 三角形 q0,q1,q2 和对应的 3D 三角形 p0,p1,p2 并且想要将 2D 点 q 转换为 3D 点 p

  1. q0,q1,q2 内计算q 的重心坐标u,v

    how to compute barycentric coordinates

  2. 使用三角形p0,p1,p2u,v转换为笛卡尔

所以当放在一起时:

| u |           | (q1.x - q0.x) , (q2.x - q0.x) , q0.x |   | q.x |
| v | = inverse | (q1.y - q0.y) , (q2.y - q0.y) , q0.y | * | q.y |
| 1 |           |       0       ,       0       ,   1  |   |  1  |

p.x = p0.x + (p1.x - p0.x) * u + (p2.x - p0.x) * v
p.y = p0.y + (p1.y - p0.y) * u + (p2.y - p0.y) * v
p.z = p0.z + (p1.z - p0.z) * u + (p2.z - p0.z) * v

【讨论】:

  • 啊当然!取 2D 三角形中点的重心坐标,并找出具有相同重心坐标的 3D 三角形上的哪个点。我正在考虑以类似的方式使用平方距离然后对 Z 进行插值,但这可能更快。 Sebastian Lague 对重心坐标 (youtube.com/watch?v=HYAgJN3x4GA) 有很好的解释,但正如 cmets 中的人指出的那样,如果A.y == C.y 他的实现将失败,因为它除以 0。有修复,但我想我会用谷歌搜索测试实施。谢谢!
  • @Clonkex 逆 3x3 矩阵方法消除了方程组计算顺序错误的问题......否则,您需要有 2 个解决方案并使用最准确的一个(避免除以零或太小的值)
  • 为了将来参考,我使用了this implementation(尽管接受的答案也有效),然后做了float newX = tri.p0.x * u + tri.p1.x * v + tri.p2.x * w;(显然用yz替换所有x其他轴) .我会使用你的逆矩阵方法,但我对矩阵数学几乎一无所知(我意识到,这是游戏编程的一个重要弱点)并且无法轻易弄清楚如何在 Unity 中实现它(并不是我花了很多时间在上面) .
  • @Clonkex 见Understanding 4x4 homogenous transform matrices ... 在没有它们的情况下做 3D 图形不是一个好主意。对于矩阵数学,有像 GLM 这样的库......或者你可以在这里写你自己的 ND math
  • 谢谢,这很有用。 3Blue1Brown 也有一些优秀的视频,解释了线性代数的一些基础知识(视觉上,这对我很有帮助!),我以前看过但需要再看一遍。人们常常对我还不知道矩阵数学感到惊讶,但是虽然我当然不想在不学习它的情况下编写自己的图形引擎,但我一生中的大部分时间都是一个业余游戏开发者,除了一些着色器编程我几乎不需要它们。对于我使用的 99% 的东西,我只需要简单的向量数学。
【解决方案2】:

扩展@Spektre's excellent answer,这就是我实现工作解决方案的方式。我正在使用 Unity,所以我使用了 Ivan Kutskir 的 lightweight C# matrix class 来处理矩阵数学。可能有更快/更清洁的方法可以做到这一点,但这非常简单并且可以正常工作。

显然你必须确保当你放弃 Z 轴时,你不会得到一个退化的三角形。

// tri is a 3D triangle with points p0, p1 and p2
// point is a 2D point within that triangle, assuming the Z axis is discarded

/*
Equivalent to this part of @Spektre's answer:

| u |           | (q1.x - q0.x) , (q2.x - q0.x) , q0.x |   | q.x |
| v | = inverse | (q1.y - q0.y) , (q2.y - q0.y) , q0.y | * | q.y |
| 1 |           |       0       ,       0       ,   1  |   |  1  |
*/

Matrix m1 = new Matrix(3, 3);
Matrix m2 = new Matrix(3, 1);
m1[0, 0] = tri.p1.x - tri.p0.x;
m1[0, 1] = tri.p2.x - tri.p0.x;
m1[0, 2] = tri.p0.x;
m1[1, 0] = tri.p1.y - tri.p0.y;
m1[1, 1] = tri.p2.y - tri.p0.y;
m1[1, 2] = tri.p0.y;
m1[2, 0] = 0;
m1[2, 1] = 0;
m1[2, 2] = 1;
m2[0, 0] = point.x;
m2[1, 0] = point.y;
m2[2, 0] = 1;
Matrix mResult = m1.Invert() * m2;
float u = (float)mResult[0, 0];
float v = (float)mResult[1, 0];

/*
Equivalent to this part of @Spektre's answer:

p.x = p0.x + (p1.x - p0.x) * u + (p2.x - p0.x) * v
p.y = p0.y + (p1.y - p0.y) * u + (p2.y - p0.y) * v
p.z = p0.z + (p1.z - p0.z) * u + (p2.z - p0.z) * v
*/

float newX = tri.p0.x + (tri.p1.x - tri.p0.x) * u + (tri.p2.x - tri.p0.x) * v;
float newY = tri.p0.y + (tri.p1.y - tri.p0.y) * u + (tri.p2.y - tri.p0.y) * v;
float newZ = tri.p0.z + (tri.p1.z - tri.p0.z) * u + (tri.p2.z - tri.p0.z) * v;
Vector3 newPoint = new Vector3(newX, newY, newZ);

或者,您可以在没有矩阵的情况下获得相同的结果(尽管这可能是一种不太可靠的方法,我不确定)。为了计算重心坐标,我使用了this implementation,但accepted answer 也可以。

// tri is a 3D triangle with points p0, p1 and p2
// point is a 2D point within that triangle, assuming the Z axis is discarded

// Find the barycentric coords for the chosen 2D point...
float u, v, w = 0;
Barycentric2D(point, new Vector2(tri.p0.x, tri.p0.y), new Vector2(tri.p1.x, tri.p1.y), new Vector2(tri.p2.x, tri.p2.y), out u, out v, out w);

// ...and then find what the Z value would be for those barycentric coords in 3D
float newZ = tri.p0.z * u + tri.p1.z * v + tri.p2.z * w;
Vector3 newPoint = new Vector3(point.x, point.y, newZ);

// https://gamedev.stackexchange.com/a/63203/48697
void Barycentric2D(Vector2 p, Vector2 a, Vector2 b, Vector2 c, out float u, out float v, out float w)
{
    Vector2 v0 = b - a;
    Vector2 v1 = c - a;
    Vector2 v2 = p - a;
    float den = v0.x * v1.y - v1.x * v0.y;
    v = (v2.x * v1.y - v1.x * v2.y) / den;
    w = (v0.x * v2.y - v2.x * v0.y) / den;
    u = 1.0f - v - w;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-27
    • 1970-01-01
    • 2018-07-23
    • 2012-02-29
    • 1970-01-01
    • 2011-07-29
    • 2010-09-12
    • 1970-01-01
    相关资源
    最近更新 更多