부모자식 계층구조의 변환
구현 목표
- Obj1(object), Obj2(target)가 있을 때
- Obj1(object)이 Obj2를 바라보면서(target)
- obj2의 로컬 공간에 obj2.up 방향을 축으로
- 직선거리로 5만큼 차이를 두며
- Obj2를 바라보면서
- 공전하는 기능을 넣고 싶다.
우선 유니티 관계에서 obj1을 obj2의 자식 관계로 집어 넣는다.
혹은 수동적으로 코드에서 obj1을 obj2의 로컬 트랜스폼에 종속되도록 변경한다.
Unity Hierarchy
유니티에서 hierarchy에 넣는다는 것은 부모 자식 관계의 형성을 의미하며, 부모자식 관계의 형성은 자식에서 SRT연산을 부모의 local 좌표계로 처리한다는 것을 의미함
게임엔진에서 오브젝트를 배치할 때는 n개의 오브젝트에 대해서 n개의 localspace를 world space로 변환하는 순서로 처리가 된다
즉,
Local space -> World space처리가 되어야 하는 것
transform에서는 메소드 중 월드 좌표의 값을 반환하는rotation, position, lossyscale
그리고
이에 대응하는 로컬 좌표의 localrotation, localposition, localscale이 있다.
Rotation, position, lossyscale은 월드 좌표에 대한 값을 나타내며
Localrotation, localposition, localscale은 로컬 좌표에 대한 계산을 나타냄
여기서 scale이 아닌 ‘lossy’scale이라고 하는 것은 부모 자식 관계에 있는 오브젝트에 대해
부모공간에서 SRT를 할 시,
자식 공간을 월드 공간으로 비추어 보았을 때 scale에 대한 부분이 SRT 연산에 의해 최종적으로 뒤틀릴 가능성(skewed)이 있기 때문에
lossyscale이라고 부른다.(lossy -> 손실치)
다른 값과는 다르게 lossyscale은 직접적으로 변경할 수 없는 값이기에 원하는 크기만큼
스케일을 변경하기 위해서는
부모 자식 관계를 끊고 스케일 변경 처리를 해 주어야 한다.
Transform parent = transform.parent;
Transform.parent = null;
Transform.localsacle = targetscale
Transform.parent = parent;
Obj2
Obj1
로 되어있는 계층 관계일 때
Obj2에 대해서 rotation, position 처리할 시 world space에 대하여 처리되며
Obj2에 대해서 localrotation, localposition 처리할 시 obj1에 대한 local space에 대하여 처리됨
유니티 계층 구조에서의 SRT 변환에 대한 부모 자식 관계 처리 코드를 재현할 때
즉, 여기서 확실하게 알아야 할 것은
자식 오브젝트에 대해서 localrotation, localposition 처리 연산은 선형변환의 순서대로 처리되는 과정이지만
자식 오브젝트에 대해서 roation, position 처리 연산은 역변환을 해 주어야 하는 처리 과정임.
(월드 좌표의 결과를 자식 obj에서 바로 결과가 나오도록 처리해 주어야 하므로)
Localrotation, localposition의 자식에서 월드로의 변환 과정
Localrotation, localpositon시, 혹은 hierarchy 창에서 부모 자식으로 오브젝트를 묶었을 때 내부적으로 어떻게 처리가 되는지에 대해서 알아본다.
부모를(root) localtransform하는 경우 결과론적으로는 world좌표의 transform과 동일함.
따라서 가정은 부모자식으로 지정된 오브젝트가 2개 있다는 가정하에 설명
부모 자식 관계에서는 부모의 스케일이 1이라 가정할 때,
Child.rotation = parent.rotation*child.rotation
Child.position = parent.position + parent.rotation * child.position ( pos에 R연산후 T처리의 의미)
가 되며
부모의 스케일이 달라지는 경우에는 월드 공간에서
Child.rot = parent.rotation*child.rotation
Child.position = parent.position + parent.rotation * (parent.worldscale * child.position)
Child.scale = parent.scale * child.scale
이 됨
여기서 rotation 정보는 quaternion이며 회전 합성으로 q= q1*q2 처리
q는 회전축과, 회전각에 대해서 오일러 표기법으로
e^n세타, 혹은 (wcos세타, nsin세타) 로 표기됨을 알고 있고
q1 q2 연산에서 총 16개의 항이 나오며 이를 간략화 할 시에
q1q2=(w1v2+w2v1+v1×v2,w1w2−v1⋅v2)
로 표기됨을 알고 있다.
1. Child.rot = parent.rotation*child.localrotation
parent.rotation*child.rotation 과정은 기본적으로 쿼터니언 연산으로서 이는 오브젝트에 대한 회전 방향에 대해서만 고려하는 연산이므로 scale에 대한 항이 추가되지 않는다.
만일 오일러 각의 회전으로 합성을 고려하는 경우에는 문제가 발생할 수 있는데,
오일러 각의 회전은 각 축에 대해서 정규 직교 기저라는 전제를 깔며
축을 한 축으로의 회전으로 제한하는 회전이기 때문임
Scale시 (1,1,1)이 아닌 (1,2,3)등과 같이 되는 경우 각 축에 대한 기저가 직교 기저를 보장하지 못하며 이는 회전 연산에서 역행렬 계산, 선형 변환 등의 처리시 결과론적으로는 결과값이 마찬가지로 직교 기저를 형성하지 못하게 되므로
scale을 고려한 오일러 회전 연산의 경우 계산 결과가 잘못 나올 수 있음
따라서
쿼터니언 연산을 통해 단순히 회전 방향 성분에 대해서만 고려하여 연산을 간결하게 처리한다.
2. Child.position = parent.position + parent.rotation * (parent.worldscale * child.localposition)
a) parent.worldscale * child.localposition
자식의 local 공간은 기본적으로 0,0,0이지만 여러 offset 등도 고려
Ex) 부모와의 거리가 (0,0,-5)정도 떨어져 있을 때
여기서 부모 자식 관계에서 자식으로 들어가면 부모의 현재의 scale 에 영향을 받게 됨
Ex) 부모 scale이 2,2,2일 때
자식의 local 거리가 떨어지는 offset이 (0,0,-5)라면
자식이 부모에 붙었을 때는 (0,0,-10)으로 *2 되어 계산되어야 한다
여기까지가 자식을 부모 공간의 local 공간에 대한 position을 결정하는 단계
b) parent.rotation * (parent.worldscale * child.localposition)
부모공간으로 자식을 postion 이동하고 부모의 회전 방향에 따라 자식 또한 position 이동을 해야 함
(헷갈리지 말아야 할 것은 여기서는 단순 position 연산만을 보이고 있으므로 child의 방향은 계속 forward를 향하고 있는 상태이다)
C) parent.position + parent.rotation * (parent.worldscale * child.localposition)
최종적으로 부모의 world 공간으로 translation 하는 부분을 고려하여 parent.pos를 더해 줌
rotation, position의 자식에서 월드로의 변환 과정
엔진의 흐름은
Local space -> world(global) space
의 순서임
Rotation, position 연산은 이를 무시하고 world공간에서를 기준으로 위치변환, 회전을 한번에 처리하겠다는 메서드임
기본적인 아이디어는 원하는 동작인 world에 대한 pos, rot를 inverse하여
이러한 동작을 수행하도록 보이는 localpos, localrot 연산을 구한 후
이를 다시 local space -> world space 변환으로 하는 식에 넣는 과정임
1. 월드 -> 로컬 변환(역변환)
(역변환으로 월드에서 그렇게 보이도록 하는 localpos, localrot를 구함)
2. 로컬 -> 월드 변환
(이를 다시 로컬 공간에 대입하여 결과적으로 부모 자식 구조를 있는 상태에서도 월드 공간에 대해서 내가 의도한 결과처럼 보이게끔 처리됨 )
1. Child.rotation = parent.rotation*child.localrotation
에서
q * q^-1 = identity quaternion 이므로(회전이 0인)
Child.localrotation = invsereparent.rotation * childrotation
2. Child.position = parent.position + parent.rotation * (parent.worldscale * child.localposition)
에서
child.localpositoin=
*inverseparent.worldscale * Invsereparent.rotation*(Child.position – parent.positoin)
'Unity Study' 카테고리의 다른 글
| 2025-12-05_Script Cycle + GC (0) | 2025.12.05 |
|---|---|
| 2025-12-02_Quaternion(3) (0) | 2025.12.02 |
| 2025-11-28 Quaternion(2) (0) | 2025.11.28 |
| 2025-11-27 Quaternion (0) | 2025.11.27 |
| 2025-11-26 3rd person camera(3) - LookAt, Quaternion (0) | 2025.11.26 |