목차
- 원하는 결과물
- 발상의 동기
- 이론및 수식 설명
- 의사 코드
- 부록: 하고싶은 말
2D상의 좌표에서 특정 3D물체의 정보에 관여하는 "Picking" 이라는 기법을 사용해 얻은 반직선을 통해
이 반직선에 맞닫는 특정물체위에 다른 물체를 설치하는 방법에 관한것이며,
따라서 Picking에 관한 부분은 스킵하고 지나가겠다.
원하는 결과물
우선 이 글의 최종 결과물이다.
Frank D. Luna책의 Chapter.17의 Picking프로젝트에서 나의 수식을 새로 추가한 프로젝트영상이다.
발상의 동기
며칠전부터 문득 드는 생각이 있었다.
맵툴을 실제로 만들게 된다면 오브젝트를 그래도 쉽게 설치할 수 있도록 도와주는 기능이 필요할것같았고,
그에 따라 실제로 물체를 설치하는데
이때, 물체의 위치가 잘못 계산하면 너무나도 쉽게 다른 메쉬랑 겹치게 되는 현상이 일어나게 될것이다.
따라서 특정한 공식에 따라 물체를 제대로 설치할 필요성을 느끼게 되어 연구를 시작했다.
수식 설명
우선 해당 수식은 Picking을 진행한 후의 상태라고 가정한 상태위에서 시작한다.
따라서 우리가 알아야할 정보와 사전에 미리 알고있는 정보는 다음과 같다.
이제 정보에 관한 정리도 끝났으니, 본격적으로 이론에 관한 설명을 시작하겠다.
우선 우리가 원하는 상황은 다음과 같은 상황이다.
OW는 월드좌표계의 원점이다.
OL은 직육면체의 로컬좌표계의 원점이고, 우리는 이 원점을 기준으로 생각할 것이다.
P는 반직선에 충돌한 평균정점의 위치이다.
r은 원의 반지름이며,
QL은 우리가 구해야할 위에 설치될 물체의 로컬좌표이다.
그리고 해당 삼각형의 실제 면위에서 보면 다음과 같은 상황이 연출된다.
이제 우리는 여기서 갈색 벡터인 QL벡터를 구해주면 우리의 1차적인 목표는 완수된다.
이제 본격적으로 구해보자.
우선 P는 반직선을 통해 얻은 세 정점의 평균위치이다.
또한, 평균 법선벡터는 다음과 같이 구해줄수 있겠다. 미리 구해주자.
우리가 여기서 알아야 할 포인트는 반직선이 원의 중심을 지나갈 필요가 전혀 없다.
그냥 그 물체에 접선벡터와 수직인 방향으로 나아가면 제대로 작동할 것이다.
또한 P벡터와 QL벡터사이의 거리는 r이여야한다.
따라서 P벡터에서 QL벡터로 가는 벡터는 다음과 같이 구해줄 수 있겠다.
P에서 QL로 가는 벡터는 해당 물체 표면위에있는 접선벡터와 수직이어야 한다.
따라서 법선벡터가 되며 수식은 이런식으로 바뀌게 된다.
이제 P에서 QL로 가는 벡터를 구했으니 최종적인 OL에서 QL로 가는 벡터는 다음과 같다.
이제 우리가 얻어야할 최종적인 Q의 로컬좌표를 얻어냈다.
하지만, 이것을 성공적으로 월드좌표로 변환하기 위해서는 해당 세 정점을 가지는 물체A의 월드행렬을 알아야한다.
따라서 우리가 위에 설치할 원의 월드행렬은 다음과 같은 형식이 되겠다. (Am: A의 월드행렬)
의사 코드
이제 위에서 구한 수식을 본격적으로 나만의 의사코드로 표현해 보겠다.
아마 C/C++계열로 공부한 사람이라면 쉽게 이해가 가능할 것이라 믿는다.
Matrix A_m = ???; // 픽킹을 통해 얻어낸 물체의 월드 행렬
Scalar r = ???; // 원의 반지름
Vector3 v0 = ???; // 정점0
Vector3 v1 = ???; // 정점1
Vector3 v2 = ???; // 정점2
Vector3 P = (v0 + v1 + v2) / 3.0f; // 평균 정점
Vector3 n0 = ???; // 법선벡터0
Vector3 n1 = ???; // 법선벡터1
Vector3 n2 = ???; // 법선벡터2
Vector3 tempN = (n0 + n1 + n2); // 법선의 총합
Vector3 P_normal = tempN / Length(tempN); // 평균 법선벡터
// P_normal = Normalize(P_normal);
Vector3 Q_L = P + (P_normal * r); // 로컬좌표계에서의 Q벡터
// Translation(x, y, z): x, y, z좌표에 위치한 이동행렬을 구축한다.
Matrix Q_world = Translation(Q_L.x, Q_L.y, Q_L.z) * A_m; // QL벡터의 월드행렬
최종적으로 이러한 형식으로 구할 수 있었으며,
이제 Q_world를 위에 설치할 물체의 월드행렬에 복사해주면 [원하는 결과물] 처럼 물체위에 물체를 설치해줄 수 있게된다.
부록: 하고싶은 말
만약에 원이 아닌 직육면체를 설치하고싶다면, 반지름대신 한계벡터의 크기를 넣거나
아니면 위에 설치할 물체(지금은 직육면체)와 물체A의 맞닫는 두 법선벡터간의 길이를 비교해서 제일 길이가 긴 벡터의 크기를 r대신 넣으면 되겠다.
이번에 이 토이프로젝트를 진행하면서 개인적으로 엄청 재밌었다.
만약에 이 글을 보는 여러분도 맵툴이나 이러한 비슷한 기능을 구현할때 이 글이 여러분에게 큰 도움이 되었으면 한다.
'개인 연구노트 > 그래픽스' 카테고리의 다른 글
Rookiss님 강의) Deferred Shading 버그 해결법 (0) | 2023.05.20 |
---|---|
물체를 설치하는 방법 (v2) (0) | 2023.01.17 |