Как рассчитать (правильную) матрицу преобразования между двумя кадрами (осевыми системами) в Unity3D - PullRequest
0 голосов
/ 25 января 2019

Для проекта в Unity3D я пытаюсь преобразовать все объекты в мире, меняя фреймы.Это означает, что источник нового кадра поворачивается, переводится и масштабируется для соответствия источнику старого кадра, затем эта операция применяется ко всем другим объектам (включая старый источник).Для этого мне нужен обобщенный, 3-мерный (таким образом, 4x4) Transformation-Matrix.

Я смотрел на использование встроенного в Unity метода Matrix4x4.TRS (), но это кажется бесполезным, так как это толькоприменяет перевод, вращение и масштабирование к определенной точке.То, что я ищу, - это смена кадров, в которых новый кадр имеет другое происхождение, вращение и масштаб по сравнению с исходным.

Чтобы визуализировать проблему, я сделалмаленький GIF (у меня есть рабочая версия в 3D, без использования матрицы и без вращения): https://gyazo.com/8a7ab04dfef2c96f53015084eefbdb01

Значения для каждой сферы:

Origin1 (Red Sphere)

Before:   
Position (-10, 0, 0)
Rotation (0,0,0)
Scale    (4,4,4)   

After:
Position (10, 0, 0)
Rotation (0,0,0)
Scale    (8,8,8)   

-
Origin2 (Blue Sphere)

Before:   
Position (-20, 0, 0)
Rotation (0,0,0)
Scale    (2,2,2)   

After:
Position (-10, 0, 0)
Rotation (0,0,0)
Scale    (4,4,4)   

-
World-Object (White Sphere)

Before:   
Position (0, 0, 10)
Rotation (0,0,0)
Scale    (2,2,2)   

After:
Position (30, 0, 20)
Rotation (0,0,0)
Scale    (4,4,4)  

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

// Position in original axes
Vector3 positionBefore = testPosition.TestPosition - origin.TestPosition;
// Position in new axes
Vector3 positionAfter = (positionBefore * scaleFactor) + origin.transform.position;

Мне нужна матрица, которая может сделать это (и включает вращение, такое, что Origin2 поворачивается вВращение Origin1 было до преобразования, и все другие объекты перемещаются в правильные положения).

Есть ли способ сделать это, не выполняя полный расчет для каждого вектора (т.е. преобразование положения перед вектором)?Его необходимо применять к (очень) большому количеству объектов в каждом кадре, поэтому его необходимо (достаточно) оптимизировать.

Редактировать: масштабирование ВСЕГДА будет одинаковым.

Ответы [ 2 ]

0 голосов
/ 28 января 2019

Nevermind .. Пришлось применить вращение и масштаб к переводу перед созданием TRS D'Oh

0 голосов
/ 26 января 2019

Могут быть и другие решения, но вот что я бы сделал

  1. Оберните ваши объекты в следующую иерархию

    WorldAnchorObject
    |- Red Sphere
    |- Blue Sphere
    |- White Sphere
    
  2. Makeубедитесь, что WorldAnchorObject имеет

    position: 0,0,0
    rotation: 0,0,0
    localScale: 1,1,1
    
  3. положение / вращение / масштабирование сфер (теперь это будет происходить относительно WorldAnchorObject)

  4. Теперь осталось только преобразовать WorldAnchorObject -> он будет перемещать, масштабировать и вращать все остальное и сохранять относительные преобразования без изменений.

    Как точно вы перемещаете якорь мира - ваша вещь.Я предполагаю, что вы хотите всегда центрировать и нормализовать определенный дочерний объект.Может быть, что-то вроде

    public void CenterOnObject(GameObject targetCenter)
    {
        var targetTransform = targetCenter.transform;
        // The localPosition and localRotation are relative to the parent
        var targetPos = transform.localPosition;
        var targetRot = targetTransform.localRotation;
        var targetScale = targetTransform.localScale;
    
        // First reset everything
        transform.position = Vector3.zero;
        transform.rotation = Quaternion.Idendity;
        transform.localScale = Vector3.one;
    
        // set yourself to inverted target position
        // After this action the target should be on 0,0,0
        transform.position = targetPos * -1;
        // Scale yourself relative to 0,0,0
        var newScale = Vector3.one * 1/targetScale.x;
        ScaleAround(gameObject, Vector3.zero, newScale);
        // Now everything should be scaled so that the target 
        // has scale 1,1,1 and still is on position 0,0,0
    
        // Now rotate around 0,0,0 so that the rotation of the target gets normalized
        transform.rotation = Quaternion.Inverse(targetRotation);
    }
    
    // This scales an object around a certain pivot and 
    // takes care of the correct position translation as well
    private void ScaleAround(GameObject target, Vector3 pivot, Vector3 newScale)
    {
        Vector3 A = target.transform.localPosition;
        Vector3 B = pivot;
    
        Vector3 C = A - B; // diff from object pivot to desired pivot/origin
    
        float RS = newScale.x / target.transform.localScale.x; // relative scale factor
    
        // calc final position post-scale
        Vector3 FP = B + C * RS;
    
        // finally, actually perform the scale/translation
        target.transform.localScale = newScale;
        target.transform.localPosition = FP;
    }
    

    Теперь вы называете это пропуском одного из детей, например,

    worldAnchorReference.CenterOnObject(RedSphere);
    

    должно привести к тому, чего вы хотели достичь.(Взломать это на моем смартфоне, так что никаких гарантий, но если возникнут проблемы, я могу проверить это, как только я снова с ПК.;))

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...