3

这是一个有点复杂的问题,所以我会尽力把它分解成块。

为了学习/乐趣,我正在编写一个 3D Python 库(而不是我打算让其他人使用的库)。在我开发的系统中,三维点一般会被展平到图像上,如下所示:

  • width通过将点移动到中心消失点的一半来增加 Z 索引。
  • Z = 0处,X 和 Y 值直接对应于 X、Y 处的像素。

(这个方法可能有名字,但如果有,我不熟悉。)

在 Python 中:

# vx and vy are the vanishing point's coordinates
def flatten_point(width, vx, vy, x, y, z):
    distance = (x - vx, y - vy)
    flat_distance = [d / (1 + float(z) / width) for d in distance]
    return (vx + flat_distance[0], vx + flat_distance[1])

在这一点上,我能够通过展平它的顶点并使用重心坐标来查找和填充落在这三个点之间的像素来稍微有效地创建三角形。如果我不需要知道这些像素对应的三角形上的实际点,这已经足够好用了,但是如果我想对三角形进行着色以使更深的点绘制得更暗,我需要知道什么未展平点像素对应的三角形。

math.stackexchange 上的 joriki建议使用重心坐标作为权重来查找原点。这似乎确实有效了一段时间——如果我使用线性深度系统,它可能会有效——但是当三角形点的深度相差足够大时,它就会分崩离析。三角形似乎比实际更快地接近最大深度,就好像它向后弯曲一样。

所以,简而言之:如何反转点展平功能以获得展平三角形上任意 2D 像素的实际 3D 点?或者,如果有更好/更有效的方法来展平三角形而不丢失每个像素的深度,那也可以。

4

1 回答 1

2

你是对的,问题在于你的深度值不是线性的。幸运的是,解决方案很简单,但如果按像素计算,则有点贵。

使用您的重心坐标,而不是直接插值三个 Z 分量,您需要插值它们的并重新反转结果。这称为透视校正。

仅 Z 示例:

def GetInterpolatedZ(triangle, u, v):
    z0 = 1.0 / triangle[0].z
    z1 = 1.0 / triangle[1].z
    z2 = 1.0 / triangle[2].z
    z = z0 + u * (z1-z0) + v * (z2-z0)
    return 1.0/z

具有triangle三个向量的列表 和 以及和uv重心坐标。triangle[1]triangle[2]如果它们被偏移,您将需要在分区之前和之后重新映射您的 Z。

如果你想插入实际的 X 和 Y 坐标,你可以做类似的事情。您将需要对 x/z 和 y/z 进行插值,并通过乘以 z 来重新线性化结果。

def GetInterpolatedZ(tri, u, v):
    t0 = Vec3(tri[0].x/tri[0].z, tri[0].y/tri[0].z, 1.0/tri[0].z)
    t1 = Vec3(tri[1].x/tri[1].z, tri[1].y/tri[1].z, 1.0/tri[1].z)
    t2 = Vec3(tri[2].x/tri[2].z, tri[2].y/tri[2].z, 1.0/tri[2].z)

    inter = t0 + u * (t1-t0) + v * (t2-t0)
    inter.z = 1.0 / inter.z
    inter.x *= inter.z
    inter.y *= inter.z
    return inter

同样,tri是三个向量的列表,并且u, v是 的重心坐标tri[1], tri[2]Vec3是一个规则的 3 分量欧几里得向量类型。

于 2012-07-24T17:20:55.150 回答