Получение фактических трехмерных координат точки на треугольнике, который был сглажен в 2 измерения

Это немного сложная проблема, поэтому я сделаю все возможное, чтобы разбить ее на куски.

Я пишу библиотеку 3D Python для обучения / развлечения (в отличие от той, которую я собирался использовать для других). В разработанной мною системе трехмерные точки обычно сглаживаются на изображении следующим образом:

  • Увеличение индекса Z на width перемещает точку на полпути к точке схода в центре.
  • В 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])

На данный момент я могу довольно эффективно создавать треугольники, сглаживая их вершины и используя барицентрические координаты, чтобы найти и заполнить пиксели, которые попадают между этими тремя точками. Это работает достаточно хорошо, если мне не нужно ничего знать о фактических точках на треугольнике, которым соответствуют эти пиксели, но если я хочу заштриховать треугольник так, чтобы более глубокие точки были нарисованы темнее, мне нужно знать, на какой незафиксированной точке треугольник, которому соответствует пиксель.

joriki на math.stackexchange рекомендует использовать барицентрические координаты в качестве весов, чтобы найти исходную точку. Это действительно работало некоторое время - и, вероятно, сработало бы, если бы я использовал линейную систему глубины - но оно разваливается, когда глубины точек треугольника достаточно различаются. Треугольник, кажется, приближается к наибольшей глубине быстрее, чем на самом деле, как если бы он был изогнут назад.

Итак, вкратце: как я могу повернуть функцию сглаживания точек, чтобы получить фактическую трехмерную точку произвольного 2D-пикселя на плоском треугольнике? В качестве альтернативы, если есть лучший / более эффективный способ сглаживания треугольников без потери глубины каждого пикселя, это также сработает.

1 ответ

Решение

Вы правы в том, что проблема заключается в том, что ваши значения глубины не являются линейными. К счастью, решение простое, но немного дороже, если рассчитывать на пиксели.

Используя ваши барицентрические координаты, а не интерполируя три компонента 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 список из трех векторов и u а также v барицентрические координаты для 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 является регулярным трехкомпонентным евклидовым векторным типом.

Другие вопросы по тегам