FX/Houdini_Joy of VEX

Joy of Vex - Day 17-1: copy sop, orient, quaternions

Gamestonk118 2022. 7. 27. 16:32

중요했던 부분:

- quaternion()과 @orient 맛보기

지난번에 @up을 사용하여 특정 geometry의 머릿방향을 결정하는 방법을 배웠었다. 

/* 1번 input geometry에서부터 0번 포인트의 포지션값을 불러와 카피되는 geometry의 머릿방향을 결정하여라*/
@up = point (1, "P", 0);

function quaternion()은 첫번째 인풋으로 얼마나 회전할지의 대한 크기값과 두번째 인풋으로 축의 값이 들어옴으로써 그 축은 @N (즉 어떤 면이 향하는 방향)과 동일하게 값을 써 넣어줄 수 있다. 그러므로 첫번째 인풋에서는 얼마나 회전할지에 대해 angle값을 float으로 써넣어주면 되고 두번째 인풋에서는 면이 어딜향해 볼지 즉 어느 축을 중심으로 회전할지에 대해 @N값을 vector으로 써넣어주면 된다.

v@axis = @N; // 축 방향을 geometry의 노멀값으로 했을때
f@howMuch = chf ("HOWMUCH"); // chf()을 사용하여 얼마나 회전해줄지에 대한 크기값을 설정
// @orient에 quaternion()을 사용하여 얼마나 어느 축을 중심으로부터 얼마나 회전할지 결과값을 불러와라
@orient = quaternion (@howMuch, @axis);
이는 이와 같이 quaternion()을 통해 회전축과 각도를 잡아 geometry가 회전하는 것을 알 수 있다

- @orient

Copy To Points에 들어오고 있는 점의 @orient값이 (0, 0, 0, 1)이라면 즉 (a, b, c, d)순이라 가정했을시 D가 1이라면 A는 x, B는 y, C는 z축 방향으로 각각 1이 되었을때 90도 방향으로 아니면 -1이 되었을때 -90도 되는 방향으로 회전하는 방식으로 작동한다.

D의 값이 조절되면 A, B, 그리고, C는 D의 값만큼 변화를 주어야 완전한 90도 회전을 하는 것처럼 보여질 것이다. 그리고 D의 임계점을 A, B, C가 넘는다면 값은 다시 별로 영향을 받지 않을 것이다. 즉 마치 After Effect에서 Easy Ease효과처럼 처음에 확돌아가다 D값을 지나면 천천히 돌아가게끔 설정되있는 것 같다.

// 각기 @orient의 특정을 알아보기 위해 각각의 정보들을 파라미터로 조절할 수 있게끔 세팅해주었다.
float a = chf ("A");
float b = chf ("B");
float c = chf ("C");
float d = chf ("D");
// @orient에 각각에 알맞는 float값들을 집어넣어 어떠한 방식으로 회전하는 지 알아보았다.
@orient = set (a, b, c, d);

이렇게 설정되어있을때 A,B 그리고 C의 파라미터값을 조절한다면 각각의 축을 중심으로 90도만큼 회전하는 것을 알 수 있다

그렇다면 D의값을 0으로 중심으로 두고 회전시키려고 하면 어떻게 될까? 아까 A, B, C순서대로 x, y, z축방향으로 나타나는 것과는 대조되게 이번에는 각각의 variable들이 담고있는 축의 방향들이 바뀌어 또 뒤죽박죽되는 형상을 나타내기 시작한다. 그리고 geometry spreadsheet에 나타나는 결과값들은 우리가 마치 이해하기 힘든 radian관련 값들을 나타낸다. 이는 우리에게 교훈 하나를 주기 시작하는데...

 

- @orient의 결과가 되는 값은 우리가 예측을 하기 무척 어렵다. 

twa님께서는 우리가 @orient를 설정하였을때 정보들을 조절하였을때 생기는 수치들은 우리 인간들이 어떻게 하지 못하는 것이라고 설명해주셨다. quaternion()을 배울때는 우리가 원하는 축 그리고 얼마나 회전을 할지에 대해서 입력한 값이 3D 공간상에 결과로 잘 나오는지 그런 부분을 봐야 한다. quaternion()으로 나온 그 값 자체에 집착하지 말고 우리가 입력한 값들이 화면에 결과로 잘 나오는지 보는게 핵심으로 간주해야 한다. 

float angle = chf ("ANGLE"); // 얼마나 돌지에 대한 값을 chf()을 사용하여 파라미터로 조절
vector axis = chv ("AXIS"); // 어느 축으로 돌지에 대한값을 chv()을 사용하여 파라미터로 조절
/* 축을 기준으로 삼아 geometry를 얼마나 돌릴지 @orient로 나타네어 직접 로테이션하여라*/
@orient = quaternion (angle, axis);

 

이렇게 수치들을 조정해줌에 따라서 quaternion()으로 나온 값들에 따라 @orient가 정해저 geometry가 생성되는 것을 알 수 있다.

(quaternion() function 관점으로 이해하기)

- quaternion() 두개의 인풋

지금까지 배웠던 것중 중요한 점은 quaternion()은 float값인 angle그리고 vector값인 axis값 두개를 이용하여 copy to points 노드를 활용하여 물체를 회전시키려할때 vector값 axis의 하나의 정보의 크기가 1이어야만 일정한 속도로 회전된다. 이때 normalize()을 활용하여 axis vector값의 크기를 1로 만드는 것이 중요한데 그래야 angle값을 업데이트했을때 안정된 모션을 줄 수 있다.

float angle = chf ("ANGLE"); // 얼마나 돌지에 대한 값을 chf()을 사용하여 파라미터로 조절
vector axis = chv ("AXIS"); // 어느 축으로 돌지에 대한값을 chv()을 사용하여 파라미터로 조절
/* axis축을 normalize하여 크기값을 1로 정해줌. 그렇지 않으면 축의 크기가 1이 되지 않을때 움직이는 회전될때의
속도가 일정해지지 않아 뭔가 이상한 모션을주게됨*/
axis = normalize (axis); 
/* 축을 기준으로 삼아 geometry를 얼마나 돌릴지 @orient로 나타네어 직접 로테이션하여라*/
@orient = quaternion (angle, axis);

normalize()를 적용해주지 않으면 이렇게 뭔가 꿀렁꿀렁 거리게 되는 모션을 완성하게 된다.

 

이렇게 normalize()가 적용되지 않으면 꿀렁꿀렁한 움직임을 가진 회전을 하는 것을 알 수 있다.

normalize()를 사용했을때 무언가 이렇게 안정된 모션을 나타내는 것을 알 수 있다.

이렇게 normalize()가 적용되고 나면은 자연스러운 움직임을 가진 회전을 하는 것을 알 수 있다.

 

 

- quaternion() 한개의 인풋

quaternion()은 또한 정보를 하나만 넣어도 작동되게 만들 수 있는데 이는 angle값과 axis값을 곱하면 된다. 그러면 두개의 정보에 있었던 것과 동일하게 작동이 되는데 이를 활용하여 이렇게도 식을 만들어 줄 수 있다.

float angle = chf ("ANGLE"); // 얼마나 돌지에 대한 값을 chf()을 사용하여 파라미터로 조절
vector axis = chv ("AXIS");// 어느 축으로 돌지에 대한값을 chv()을 사용하여 파라미터로 조절
/* axis축을 normalize하여 크기값을 1로 정해줌. 그렇지 않으면 축의 크기가 1이 되지 않을때 움직이는 회전될때의
속도가 일정해지지 않아 뭔가 이상한 모션을주게됨*/
axis = normalize (axis);
vector A = axis*angle; // axis와 angle을 곱하여 vector a로써 저장해줌으로써 정보를 하나만 넣을수 있게 해줌
/* 축을 기준으로 삼아 geometry를 얼마나 돌릴지 @orient로 나타네어 직접 로테이션하여라*/
@orient = quaternion (angle*axis);

 

이러한 axis*angle을 활용하여 quaternion()에 적용하여 @orient로 나타나게 하여 이렇게 회전을 가능하게 할 수 있다.

- @N과 @up을 @orient로 변환시키는법 (matrix: 행렬) & maketransform(): Maketransform Function

@orient값을 구할때 quaternion()안에 들어가는 두개의 정보와 한개의 정보 이런것들은 어떻게 작동이 되느냐? 우선 아래 그림에 아랫쪽에 있는 모형을 quaternion()이라 가정했을때 첫번째 quaternion()에서 정보는 float 과 vector 두 정보로 이루어짐으로써 작동을 한다. 이는 주로 각기 angle과 axis로 여겨지며 이는 두번째 칸에서도 알 수 있듯이 angle과 axis를 곱한 값인 vector타입의 값을 집어넣어도 작동하는 것을 알 수있다. 이게 끝일까? 모형에서는 matrix라는 방법을 통해 새로운 정보를 통해 @orient값을 얻어낼 수 있다고 한다. 만약 아래 사진의 윗 화살표 그림에서 첫번째 화살표는 axis값과 angle값인 기본적인 quaternion()이 요구하는 값들을 통해 @orient를 얻게끔 유도한다. 그런데 만약 두 값이 아닌 가지고 있는 정보가 @N과 @up인 두 vector값 밖에 없다면 어떻게 할까? 이는 방금전 언급한 Matrix라는 정보의 형태로 집어넣어 quaternion()을 통해 @orient를 얻어낼수 있다고 말한다. 

 

이때 어떠한 function을 추가로 요구로 하는데 이는

maketransform()이라 불리며 @up과 @N같은 두 vector 값들을 matrix의 형태로 만들어 주는 것을 가능케 하는 function이다.

이를 만들어 줌과 동시에 quaternion()을 통해 @orient를 구할 수 있게 만들어줘 더욱이 유연하게 다양한 정보들을 집어넣을 수 있게 해주는 고마운 Function이다.

이와 같이 quaternion()안에 들어가는 정보들은 여러가지의 형태로 존재할 수 있으며 이는 위와 같은 방식을 통해 결국 @orient값을 얻어낸다는 사실을 알 수 있다.

아래에서 볼 수 있듯이 후에 언급할 수 도 있겠지만 matrix3의 형태를 바로 quaternion()에 집어넣으려고 할때는 ident()라는 function을 요구하며 이는 동일한 과정으로 @orient값을 얻을 수 있다는 사실을 알 수있다.

위에 있던 그림을 대충 요약한 diagram인데 위에 언급하지 않았던 부분은 matrix3형태의 수치들은 ident()를 활용하여 quaternion()을 통해 @orient값을 얻어낼수 있다는 사실을 내포하고 있다.

TWA선생님께서는 matrix안에 어떠한 형태의 수치들이 내포되고 있을지에 대해 너무 직접적으로 집착할 필요는 없으시다고 말씀하셨다. 우선적으로 matrix는 3*3으로 약간 array가 또 쌓여버린 형태라 보면 될것같다. (수열) 그래서 @N과 @up은 maketransform()을 통해 matrix라는 형태의 수치로 변하게 되고 이를 quaternion()에 집어넣어 @orient값을 구할 수 있다고 twa님께서 설명하고 있으셨다.

 

이와 같이 vex를 작성해 줄 수 있다.

@N = {1, 1, 0}; // @N값을 (1, 1, 0)이라는 vector값으로 만들어 준다.
@N = normalize (@N); // @N값을 normalize()을 적용하여 크기값을 1로 만들어준다.
// 1번 포인트에 연결된 geometry에서 0번 포인트의 포인트 위치값을 불러와 @up에 저장해준다.
// 이때 나는 add노드로 생성된 점을 원운동을 시켜서 @up이 계속 원운동을 하면서 모션이 생기게끔 해주었다
@up = point (1, "P", 0);

/* matrix3라는 (3*3) 수열형태의 variable type을 m이라 선언해주고 이를 maketransform()을 활용하여 @N과
@up을 집어넣어 저장시킨다.*/
matrix3 m = maketransform (@N, @up);
// matrix3 variable m을 attribute m으로써도 저장시켜준다 그래서 geometry spreadsheet에 디스플레이된다
3@m = m; 
// 방금전 얻은 matrix3 variable m을 quaternion()에 집어넣어 @orient값으로 만들어준다
@orient = quaternion (m);

이렇게 작성한 코드에는 geometry spreadsheet에 attribute로 만들어준 m값에 matrix3의 형태로써 이와 같이 수치들을 나타내는 것을 알 수 있는데 이는 다음에 언급할 방식으로 수치들이 결정난 다는 사실을 알 수 있다.

box들이 이러한 방식으로도 matrix를 이용하여 동일하게 회전하는 것을 알 수 있다

이 수치들은 단위 행렬에 관련된 신비로운 비밀을 가지고 있다.

이렇게 matrix3으로 지정해주어서 동일하게 box를 회전시켜줄 수 있다

이때 꿀렁꿀렁 거리는 모션이 진행됨을 알 수 있는데 이는 point()로 불러온 @up의 방향이 @N과 수직이 아니어서 그렇다. 수직으로 바꿔주면 정상적으로 작동되는 것을 알 수 있다.

이때 위에서 꿀렁꿀렁거리던 모션을 @N값이 @up과 수직이 되게 만들어 자연스럽게 회전되게끔 만들어 보았다.

이렇게 grid에 rotation을 적용하여 바꾸어주어도 이는 우리가 구한 @orient값에 회전이 되기 때문에 각각의 box는 동일한 방향으로 회전을 하고 있는 것을 알 수 있다.

이렇게 grid의 rotation을 바꿔주어도 이렇게 동일한 방향으로 box의 면이 향해있는 것을 알 수 있다.

- matrix값을 직접 quaternion()에 집어넣어 @orient를 얻는 방법: ident(): Ident Function

matrix값을 직접 quaternion()에 집어넣어 @orient를 얻으려면 앞서 설명했듯이 ident()라는 function을 요구로 하는데 이는 matrix값을 단위행렬로 변환시켜 matrix의 기본값으로 바꾸는 것을 알 수 있다. 

 

단위행렬은 matrix의 기본값으로 예를들어 3*3 matrix는 (1, 0, 0/0, 1, 0/0, 0, 1)이와 같이 약간 1의 순서가 행이 바뀔 수록 옮겨져 가는 형태의 값을 가진 것이라 보면 된다. 이로 만들어진 수치를 box에 대입해서 본다면 이러한 결과를 얻을 수 있다.

초록색면은 @N값이 (0, 1, 0)으로 간주되며 box에서의 @up은 이 면이기 때문에 matrix에서 두번째 행은 @up으로 간주될수 있다는 사실을 알 수 있다.

또한 파란색면은 @N값이 (0, 0, 1)으로 간주되며 이는 box에서의 @N으로 간주되기 때문에 세번째 행은 @N으로 간주될 수 있다는 사실을 알 수 있다.

box에 단위행렬을 대입하여 단위행렬안에 각각의 행들이 어떠한 수치를 의미하는지 알 수 있었다.

이때 이러한 코드를 사용하여 copy to points에 연결해 모션을 완성해 주면 이렇게 된다.

@N = {0, 1, 0}; // y값이 1일때 @N값을 지정해주어라
@up = point (1, "P", 0); // 1번 인풋에 연결된 geommetry에서 0번 포인트의 포인트 위치값을 찾아 @up으로 지정

/* 이때 maketransform()을 활용하여 @up값이 두번째 행 @N값이 세번째 행의 matrix값을 matrix3 
variable m에 저장*/
matrix3 M = maketransform (@N, @up);
// 방금전 생성한 matrix3 variable m을 attribute로 보여줌
3@M = M;

이때 @up이 원운동을 하여 계속 프레임이 올라감에 따라 값이 바뀌게 되는데 이때 matrix3 variable M의 3, 4, 5번 즉 두번째 행의 값들은 add노드로 생성된 점의 현재 위치와 똑같은 값을 가진다는 사실을 알 수 있다.

@M으로 생성되어 보이는 matrix variable m의 3,4,5번의 수치값들
이와 같이 1번 인풋에 연결된 add노드로 생성된 point의 위치와 동일한 값을 가지는 것을 알 수 있다.

또한 @N값은 지정된 (0, 1, 0)으로 matrix3 variable M의 6, 7, 8번 즉 세번째 행의 값으로 지정되기 때문에 동일한 값으로 matrix안에서 보여지는 것을 알 수 있다.

@M으로 생성되어 보이는 matrix variable m의 6, 7, 8번의 수치값들

- @orient값을 matrix로 & qconvert(): Quaternion Convert Function

이는 지금까지 matrix값을 @orient로 바꿔줄때 쓰던 quaternion()의 반대로써 qconvert()는 @orient값을 반대로 matrix의 형태의 수치로 바꿔줄 수 있다.

@N = {0, 1, 0}; // y값이 1일때 @N값을 지정해주어라
@up = point (1, "P", 0);// 1번 인풋에 연결된 geommetry에서 0번 포인트의 포인트 위치값을 찾아 @up으로 지정
/* 이때 maketransform()을 활용하여 @up값이 두번째 행 @N값이 세번째 행의 matrix값을 quaternion()을 
활용하여 @orient값으로 변환*/
@orient = quaternion (maketransform (@N, @up));
// 방금전 만든 @orient값을 qconvert()을 통해 3matrix attribute k의 값으로 보여주어라
3@K = qconvert (@orient);

이때 @K의 값으로 qconver()를 활용하여 이렇게 @orient에서 matrix수치들을 뽑아낼 수 있다.

이렇게 @orient를 qconvert()를 이용해 matrix로 바꾸어도 동일한 모션이 진행되는 것을 알 수 있다

- 실제 각도값을 @orient의 값으로 변환시키는, eulertoquaternion(): Euler to Quaternion Function, & radians(): Radians Function

지금까지의 quaternion()안에 들어갔던 angle값은 radian의 수치로 들어갔던 것을 확인 할 수 있다. 만약 angle값을 degree 즉 실제 각도값으로 quaternion()안에 적용시키려면 어떻게 하면 될까? 이는 radians()이라는 function으로 후디니 상에서 degree값을 직접 radian수치로 변환시킬 수 있게 할 수 있으며 이는 우리가 더욱 Geometry의 회전을 직관적으로 이해할 수 있게 돕는다. eulertoquaternion()에서는 회전되는 방향을 x/y/z축으로 각기 설정할 수 있게끔 vector 단위를 정보로써 지정해주기 때문이다. 이때 radians()로 degree인 값을 radians수치로 바꿔줄 수 있고 이를 종합하여 새롭게 방향과 회전을 설정해줄 수 있다.

 

eulertoquaternion()에는 첫번째 정보로 얼마나 돌아갈지에 대한  값을 x/y/z을 나눈 값으로 설정되고 두번째 값은 order로써 0이 들어가는 것을 알 수 있다. 

eulertoquaternion()이 부분은 블렌더를 배울때 geometry nodes에서 노드방식으로 썼던 방식이라는것이 기억이 나기 시작했다. 그래서 오일러라는 이름이 친숙하기도 했고 3D 툴을 배울때 결국 배우면 죄다 비슷하다는 것이 이렇게 되구나라는것을 이제야 이해하기 시작했다.

/* chv()으로 만들어진 vector 파라미터에서 degree값을 입력하면 radian()을 통해 radians값으로 후디니가
직접 변환해서 vector variable twaRot에 넣는다*/
vector twaRot = radians (chv ("TWAROT"));
/*방금전 생성한 x/y/z값이 담긴 vector 수치를 eulertoquaternion()을 활용해서 직접 유저가 각도를 조절하여
알맞은 방향으로 geometry를 회전시킬 수 있게 만든다. 이는 @orient에 저장하여 변화를 나타내고 수치를 보여준다*/
@orient = eulertoquaternion (twaRot, 0);

 

이렇게 geometry의 각도값을 degree값으로 x/y/z 방향으로 조절 하여 회전시킬 수 있다.

*이쯤되면 geometry spreadsheet에서 나타나는 @orient값들은 이해 못하겠다. 이는 괜찮다. 애초에 내가 오일러가 아닌 이상 이해 못한다*

이해가 안되었던 부분:

- eulertoquaternion()안에 두번째 정보로 들어가는 0이 무엇을 의미하는지 정확히 의미를 모르겠다. Houdini 웹사이트에서는 order이라고 표현하는데 정확히 무엇을 의미하는지 햇갈리는 부분이 있어서 여기에 적기로 하였다. integer value로써 작성되는 것은 알것 같다.

- 이번에 기초를 배워서 그나마 이해는 되게 넘어가는 부분이 있는것같는데 다음 예제시간이 두렵기 시작하였다...

 

공부하면서 들었던 생각:

와... 이번강의 역대급 역대급 역대급 이었던 것 같다..... Day 7와 Day 12 비교했을때 비교도 안될만큼 역대급으로 배우는 양이 어마어마하게 많았고 어려운 부분도 많았던 것 같다... 다음 예제시간 어떻게 버틸지 솔직히 감이 안잡히기도 하다. 어려웠던 만큼 공부일기를 쓰는데도 애먹기도 하였고 공부일기를 쓸때도 햇갈리는 부분이 많아서 그런지 강의를 다시 들으면서 쓰기도 하였다. 그러다 보니 강의를 거의 두번 본것이나 마찬가지여서 그런지 시간도 많이 걸렸고 이해하는데도 시간이 오래걸렸던 것 같다. 그래도 다행히 두 번 본 보람있게 처음에 강의를 듣고나서 이게 뭔소리여 하던거와는 다르게 두번째 들으면서 공부일기도 작성해보니까 이제야 조금 이해가 되어 생각보다 막 이해가 안되서 햇갈렸던 부분은 없었던 것 같다. 또한 이번에는 function들의 본질들에 집착하기다기 보단 결과와 간단한 원리만을 이해하면 좋을 것 같다고 선생님들께서 조언해주셔서 이를 바탕으로 공부를 하니 그나마 조금 수월해진 것 같다. 근데 다음부턴 joy of vex를 보면서 예제에 적용하는 시간을 가진다... 망한 것 같다... 이제 얼마 안남았는데... 포기는 안할거다 그래도....