화면의 좌표는 2D, 세계는 3D인 경우 오브젝트의 선택 방법을 만들어야 한다.
먼저 화면에서 지정한 좌표를 이용하여 3D 세계로 광선을 발사하여 교차되는 오브젝트를 찾으면 된다.
간단한 그래픽 파이프라인을 생각해 보자
월드 변환-> 뷰 변환-> 투영 변환-> 화면 변환
이 과정을 거꾸로 거쳐서 화면에서의 점을 월드 좌표계로 만든다.
첫번째로 화면변환(ScreenTransform)-> 투영변환(ProjectionTransform)을 고려해 보자
뷰 공간에서 투영 변환이 끝난 공간은 NDC(Normalized Device Coordinates)가 된다.
잠시 원래 과정인 NDC-> Screen Coordinates을 생각해 보면 아래와 같은 공식이 나오게 된다.
참고: 화면변환
: 화면 좌표계의 좌측 상단 x 좌표
: 화면 좌표계의 좌측 상단 y 좌표
: 화면 좌표계의 화면 Width
: 화면 좌표계의 화면 Height
: 화면 좌표계의 x 좌표
: 화면 좌표계의 y 좌표
참고: 화면변환
: 화면 좌표계의 좌측 상단 x 좌표
: 화면 좌표계의 좌측 상단 y 좌표
: 화면 좌표계의 화면 Height
: 화면 좌표계의 y 좌표
두번째로 필요한 것은 NDC에서 ->View frame으로 변환이 필요하다.
먼저 z값을 생각해 보자.
현재 화면 변환-> 투영 변환을 거쳐서 좌표값은 NDC에 있으므로
z값은 투영 평면(projection plane)위에 놓여진 상태이며
그리고 좌표 값이 투영 평면에 위에 있으므로 x, y 값은 투영변환이 끝난 상태 이다.
NDC 에서 x의 범위는 [-a, a] 이고 y의 범위는 [-1, 1]이 된다.
여기서 a는 aspect값을 나타낸다.
aspect=(screen_width / screen_height)
aspect=(screen_width / screen_height)
이제 투영변환 되기 전의 x, y 값을 구해야 한다.
원래 변환 과정를 생각해 보면
View Frame에서 NDC로 변환 될때 위에서 나타낸 x, y의 범위로 Scale이 되었다.
그러므로 변환되기전 좌표값을 찾아낼려면 반대로 x, y의 값을 Scale 하면 된다.
먼저 y의 범위는 [-1, 1]이므로 Scale이 필요 없이 그대로 사용하면 되고
x의 범위는 [-a, a]이므로 a의 값 만큼 Scale이 필요하다.
(DirectX 의 경우 Z 방향은 화면 안쪽 방향이 되어 +d값이 사용 된다.)
세번째로 월드 좌표에 있는 모델과 충돌 시킬 광선(Ray)을 만든다.
현재까지 구해진 좌표값을 이용하여 광선을 만든다.
먼저, 좌표값은 View Frame에 있으므로 World 좌표계로 변환이 필요하다.
그리고 카메라의 위치값(Eye Position)을 사용한다.
마지막으로 World 좌표계에서 위의 광선과 충돌 여부를 판단한다.
카메라 위치에서 광선을 만들어 충돌을 판단하게 되므로 여러 개의 모델이 광선과 충돌할 수 있다.
충돌하는 모델 중 광선의 t 값이 가장 작을 경우가 처음으로 광선과 모델이 충돌되는 경우가 되며
이것이 picking 되는 모델이 된다.
상황에 따라 광선을 각 모델 좌표계로 변환하여 해당 모델 좌표계에서 계산할 수도 있다.
참고자료
Essential Mathematics for Games & Interactive Applications Second Edition