Установка вращения совместной иерархии в новую позу привязки с помощью кватернионовPython

Программы на Python
Ответить Пред. темаСлед. тема
Anonymous
 Установка вращения совместной иерархии в новую позу привязки с помощью кватернионов

Сообщение Anonymous »

Редактирование: добавление более конкретного примера и нескольких изображений.
Обычно вы можете взять раздел ИЕРАРХИЯ файла BVH, построить график суставов с их положением относительно их родителей и отобразить позу привязки (см. первое изображение)< /p>
При использовании «перевернутого» скелета рендеринг того же самого результата приводит к изображению2. Однако, если я затем применю повороты из 0-го кадра к перевернутой версии, я снова получу изображение 1.
Я хочу перебазировать все повороты кадров в «перевернутой» версии. ', чтобы они соответствовали позициям суставов "нормального" скелета по умолчанию, а затем для рендеринга кадров используйте "нормальное" вращение вместо условного.
"Перевернутые" скелеты генерируются Миксамо/Рококо, но если вы импортируете файл BVH в блендер, а затем экспортируете его, он будет в «нормальном» скелетном формате.
Эти две строки представляют собой поворот BVH в формате Mixamo, за которым следует поворот сгенерировано Blender относительно развернутого скелета (оба представлены в градусах в порядке поворота YZX)

Код: Выделить всё

-0.000000 -0.152100 12.667808
-0.2075    2.8384   -0.0478
tl;dr: Я хочу сделать это с помощью сценария, а не с помощью Blender.
Изображение
Изображение
Разрабатывая набор инструментов анимации BVH (Biovision Heuristics) с открытым исходным кодом, я столкнулся с проблемой, которую не могу решить. .
Я хотел бы преобразовать «перевернутый» скелет в более распространенный формат.
Технический документ BVH не слишком конкретен и привел к двум различным вариантам. реализации.
Я могу визуализировать оба варианта, но мне бы очень хотелось преобразовать одно в другое и отказаться от рендеринга на основе регистра.
Ниже приведена небольшая рекурсивная функция, которая строит мировые позиции для суставов с помощью более распространенных и перевернутых реализаций. Единственная разница заключается в том, используем ли мы кумулятивное вращение или его сопряженное.
(Примечание: для ясности я пропустил несколько логических операций if Joint.parent is None:, где позиция | вращение по умолчанию равно 0,0,0)
self и parent имеют тип Joint .
w_position — мир позиция сустава (родительская относительная позиция + мировая позиция родительского элемента)
position — это позиция сустава относительно родителя.
joint.rotation - это вращение относительно родительского элемента
вращение (параметр) - это совокупное вращение предков сустава (относительное вращение мира)

Код: Выделить всё

    def compute_frame_wpos( self, frame_no : int, flipped=False, rotation : glm.quat = glm.quat(glm.vec3(0,0,0)) ):
'''This function sets the world position of the joints for a given
frame.  Recursively calls its children, passing them the cumulative
rotations of their ancestors'''

if flipped:
self.w_position = self.parent.w_position + \
self.position * rotation
else:
self.w_position = self.parent.w_position + \
self.position * glm.conjugate(rotation)

#Recurse
child_rotation = self.frames[frame_no].rotation * rotation
for child in self.children:
child.compute_frame_wpos( frame_no, flipped, child_rotation )
Исходные (родительские относительно) положения двух скелетов различны: перевернутый скелет отображается сложенным, как крендель, для позы привязки. Что я хотел бы сделать, так это использовать 0-й кадр (позу покоя) вместо позы привязки и исправить все вращения.
Затем я создал развернутую позу для перевернутого скелета с помощью вызов Compute_frame_wpos для 0-го (отдыхающего) кадра, а затем применение
следующей рекурсии для создания новых локальных позиций из мировых позиций.

Код: Выделить всё

    def _make_relative_from_world( self ):
'''A recursive call to set the joint's parent relative positions from the world positions.'''

self.position = self.w_position - self.parent.w_position

for child in joint.children:
child._make_relative_from_world(  )
Наконец, я застрял, пытаясь исправить повороты.
В моем вызывающем коде я перебираю кадры следующим образом:
Скелет — это инкапсулирующий класс, содержащий иерархию соединений и список псевдонимов.
скелет содержит исходный скелет с импортированными позициями соединений относительно родительского элемента.
ref_skeleton содержит новый развернутый скелет с позициями суставов и world_positions, описанными выше.

Код: Выделить всё

for frame_no in range(0, self.skeleton.num_frames):
self.skeleton.get_root().compute_frame_wpos( frame_no, True )
self._reset_resting_rotation( ref_skel, frame_no )
Моя наивная реализация использует дугу берега от мирового положения кадра для соединения с мировым положением для новой позы.

Код: Выделить всё

    def _reset_frame_rotation( self, reference_skeleton : Skeleton, frame_no : int ):
'''Recursively change frame's rotations to be relative the reference skeleton'''

#Get the joint matching this one in the reference_skeleton
ref_joint = reference_skeleton.joints[ self.name ]

frame_pos = self.w_position - self.parent.w_position

self.frames[frame_no].rotation = shortest_arc( frame_pos, ref_joint.position )

for child in self.children:
child._reset_frame_rotation( reference_skeleton, frame_no )
Совершенно уверен, что реализация shortest_arc правильна, но на всякий случай я включу ее:

Код: Выделить всё

def shortest_arc(point_a : glm.vec3, point_b : glm.vec3) -> glm.quat:
'''Given the vectors a and b return a quaternion representing the shortest arc between'''

result = glm.quat(1.0,0.0,0.0,0.0)

ab_dot = glm.dot(point_a,point_b)  #dot product
cross = glm.cross(point_a,point_b) #cross product
cross_sqr = glm.dot(cross,cross)  #Squared length of the crossproduct

#Test if args have sufficient magnitude
if ab_dot*ab_dot+cross_sqr != 0.0:
#Test if the arguments are (anti)parllel
if cross_sqr > 0.0:
sqr = math.sqrt(ab_dot * ab_dot + cross_sqr) + ab_dot

#Inverted magnitude of the quaternion.
mag = 1.0 / math.sqrt(cross_sqr + sqr * sqr)
result = glm.quat(sqr * mag, cross[0] * mag, cross[1] * mag, cross[2] * mag)
elif ab_dot < 0.0: #Test if the angle is > than PI/2 (anti parallel)
#The arguments are anti-parallel, we have to choose an axis
point_c = point_a - point_b

#The length is projected on the XY plane
mag = math.sqrt( point_c[0] * point_c[0] + point_c[1] * point_c[1] )

#F32 magnitude threshhold. Probably not needed.
f32_mag_thresh = 0.0000001
if mag > f32_mag_thresh:
result = glm.quat( 0.0, -point_c[1]/mag, point_c[0]/mag, 0.0 )
else:
result = glm.quat( 0.0, 1.0, 0.0, 0.0 )
return result
Написав это, я понимаю, что мне, вероятно, нужно получить мировое вращение иерархии как из эталонного, так и из окончательного скелета и вычесть их разницу из моего окончательного вращения кадра, и я попробую это и обновлю. если это сработает.

Подробнее здесь: https://stackoverflow.com/questions/792 ... quaterions
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «Python»