将 2D 三角形上的点投影回 3D?

Project point on 2D triangle back into 3D?

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

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

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

通过代码而不是数学符号描述数学的答案(或欺骗链接!)将不胜感激;我很难阅读您在 Math.SE.

上获得的答案类型

您可以使用 barycentric coordinates...

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

  1. q0,q1,q2

    内计算 q 的重心坐标 u,v

  2. 使用三角形 p0,p1,p2

    u,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

扩展 , this was how I implemented a working solution. I'm working with Unity, so I used Ivan Kutskir's awesome lightweight C# matrix class 以处理矩阵数学。可能有 faster/cleaner 种方法可以做到这一点,但这非常简单并且工作正常。

显然,当您丢弃 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, but the 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;
}