Получение фактических 3D-координат точки треугольника, сглаженного до двух измерений

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

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

  • Увеличение индекса Z на width перемещает точку на полпути к точке схода в центре.
  • В Z = 0 значения X и Y соответствуют непосредственно пикселю в X, Y.

(У этого метода может быть название, но если оно и есть, то я с ним не знаком.)

В Питоне:

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

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


person ashastral    schedule 24.07.2012    source источник


Ответы (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 – это обычный трехкомпонентный евклидов векторный тип.

person Coincoin    schedule 24.07.2012
comment
Это выглядит многообещающе. Какую библиотеку векторов вы используете во втором блоке кода? - person ashastral; 24.07.2012
comment
Никто. Я изобрел шрифт для демонстрации. - person Coincoin; 24.07.2012
comment
Кстати, прошло довольно много времени с тех пор, как я программировал низкоуровневые растровые вещи, и это может быть немного не так. В частности, о том, как обращаются с Z. Возможно, вам нужно только Z инвертировать X и Y, а не сам Z. Дайте мне новости, и я обновлю ответ, если он неправильный. - person Coincoin; 24.07.2012
comment
Я пытался заставить любой из этих блоков кода работать и не имел большого успеха. В первом: при чем тут барицентр? Если triangle содержит трехмерные точки треугольника, а u и v являются координатами проверяемого пикселя, я не понимаю, как это связано с барицентрическими координатами. Если мне не нужно делать что-то еще с возвращаемым значением. Что касается второго блока, я не понимаю, что представляет собой inter. Ему присваивается t0 + u ... в одной строке, а затем присваиваются атрибуты x, y и z в следующих строках? - person ashastral; 25.07.2012
comment
Оба блока кода также выдают ошибки деления на ноль для любых экземпляров Z=0, что на самом деле должно быть самым простым случаем, поскольку это прямое сопоставление X и Y с соответствующим пикселем на выходе. - person ashastral; 25.07.2012
comment
u и v — барицентрические координаты для tri[1] и tri[2] соответственно, 1-u-v — барицентрические координаты для tri[0]. triangle — это таблица из трех векторов. inter — интерполированный вектор. t0 + u *... — векторные операции. - person Coincoin; 25.07.2012
comment
давайте продолжим это обсуждение в чате - person Coincoin; 25.07.2012