DirectX에서 3D공간을 표시하기위해서 행렬들을 이용 , 변환해서 화면으로 출력한 것처럼

화면에서 클릭했을때 어떤것을 클릭했는지 확인하기 위해서는 화면의 x,y좌표를 변환해야 한다.

이번엔 화면의 x, y좌표를 이용해서

1) 클릭한 곳과 구가 충돌하는지

2) 클릭한 곳이 충돌한 부분에서 어느 좌표인지

이 두가지를 확인해 보자.


먼저 "구"라는 물체를 구조체로 정의해보자.

vCenter : 구의 중점 (위치)

fRadius : 구의 반지름 (크기)

isPicked : 선택됬는지 안됬는지 검사

밑의 ST_SPHERE()는 생성했을때 바로 저 값으로 초기화해준다.

클래스 생성자에서 이니셜 라이즈하는거랑 같다고 생각하면 된다.

-> ST_SPHERE를 생성하고 아무값도 안넣어주면 fRadius에 0.0, vCenter에 0,0,0, isPicked에 false값이 들어가있음


다음은 광선(마우스로 클릭한 좌표에서 -> 클릭한 물체로 향하는)에 대한 정의이다.

m_vOriginal : 광선의 출발 지점 (마우스로 클릭한 좌표)

m_vDirection : 광선의 방향 (화면 안쪽으로 향하게 됨)

m_eRaySpace : 종류를 나타냄, 궂이 없어도 된다.


다음은 마우스 왼쪽클릭시 실행되는 코드이다. 어떤 흐름인지 보자

cRay r : 현재 클릭한 마우스의 x좌표 (LOWORD(lParam), y좌표 (HIWORD(lParam))을 이용해 계산에 사용할 광선을 구해온다.

for반복문 : 모든 구들을 확인한다.

r.IsPicked : 구와 현재 광선이 충돌하는지 확인하고 충돌하면 true 충돌하지 않으면 false를 반환한다.

1) : 광선을 구하고

2) : 모든 구에

3) : 광선이 충돌하는지 검사하고 값을 바꿈

이와같은 순서로 흘러가고 있다.


그러면 어떻게 구해지는지 살펴보자.

RayAtWorldSpace를 구하기 위해서 ViewSpace함수가 필요하기 때문에 처음인 ViewSpace부터 시작한다.


RayAtViewSpace함수

1) d3dDevice에서 뷰포트를 얻어온뒤 저장한다.

2) d3dDevice에서 투영행렬(Projection)을 얻어온뒤 저장한다.

3) 계산에 필요한 방향벡터 (m_vDirection)를 만든다.

4) X,Y좌표에 뷰포트 , 투영행렬을 이용해 계산한다.

4-1)    X = ((+2.0f * 마우스X좌표) / 뷰포트 넓이 - 1.0f) / 투영행렬중 1행 1열 (만약 배열, 벡터로 행렬이 되어있다면 MatProjection[0][0]에 해당한다)

         ->이는 투영행렬의 1행 1열이 X값이 변하는데 영향을 주기 때문이다.

4-2)    Y = ((-2.0f * 마우스Y좌표) / 뷰포트 높이 + 1.0f) / 투영행렬중 2행 2열 (배열 , 벡터라면 [1][1]에 해당)

         ->투영행렬의 2행 2열이 Y값 변화에 영향을 줌

4-3)    Z = 1.0f

         투영 윈도우 값이다. DirectX는 평면 z = 1 과 일치하도록 정의하고 있다.

RayAtWorldSpace함수

1) r에 뷰포트, 투영행렬을 이용해 변환한 방향벡터를 얻어온다.

2) 행렬을 담기위한 변수 2개를 생성한다 (matView, matInvView)

3) d3dDevice를 이용해 matView에 현재 View행렬을 담는다.

4) matInvView행렬에 matView의 역행렬을 담는다.

5) 뷰의 역행렬을 이용해서 광선의 출발점을 변환한다.

6) 뷰의 역행렬을 이용해서 방향벡터를 변환한다.

7) 방향벡터를 정규화(행렬안의 모든값이 1보다 작도록)한다.


여기서 왜 View의 역행렬을 구하는건가 ?

렌더링 파이프라인을 한번 살펴보자

여기에서 실제로 좌표에 영향을 주는 부분은

1 : 로컬 스페이스 -> 2 : 월드 스페이스 -> 3 : 뷰 스페이스 -> 4 : 투영 -> 5 : 뷰포트 이다.

뷰포트 , 투영과 관련된 부분은 이전함수에서 처리했지만 뷰 스페이스와 관련된 부분은 아직 처리되지 않았다.

우리는 현재 뷰스페이스가 적용되기 전의 좌표가 필요하다.

그래서 뷰스페이스의 역행렬을 이용해서 좌표를 변환하는 것이다.


지금까지의 과정을 모두 거치면 실제 3D좌표 내에서 클릭한 점 (벡터)가리키는 방향 (벡터)를 둘다 구한 것이다.

이제 구와 충돌을 어떻게 처리하는지 확인해보자.

r.IsPicked : 광선 R을 이용해서 구와 충돌하는지 확인하겠다는 뜻이다.

1) cRay r = (*this) r이라는 변수를 만들고 거기에 자신의 값을 집어넣는다.

여기에서는 자기와 같은 광선 r을 한게 더 만든거다.

2) matInvWorld : 월드의 역행렬

2-1)    역행렬의 4행 1열에 구의 -x좌표를 넣는다

2-2)    역행렬의 4행 2열에 구의 -y좌표를 넣는다

2-3)    역행렬의 4행 3열에 구의 -z좌표를 넣는다

3) 월드의 역행렬을 이용해 카메라의 좌표를 변환한다.

4) 월드의 역행렬을 이용해 방향벡터를 변환한다.

5) vv는 방향벡터와 방향벡터를 내적한 값이다.

6) qv는 위치좌표와 방향벡터를 내적한 값이다.

7) qq는 위치좌표와 위치좌표를 내적한 값이다.

8) rr은 구의 반지름 * 반지름한 값이다.

9) qv * qv - vv * (qq - rr) >= 0;을 반환한다.

9-1)    0이라면 1개의 점이 교차, 0보다 크다면 2개의 점이 교차, 0보다 작다면 교차하지 않는다.

사실 여기 너무 어려워서 나도잘 모르겠음 ;;; 좀더 찾아봐야 겠다


다음은 세개의 점 (면)과 선이 충돌하는지 확인하는 부분이다.

사용될 함수는 D3DXIntersectTri 이다.

D3DXintersectTri함수는 충돌했다면 True, 충돌하지 않았다면 false값을 반환한다.

여기서 충돌된 지점을 구하는 부분이 중요하다

1번째 식 : 광선의 출발점 + t(출발점에서 충돌된 지점까지의 거리) * 광선의 방향

2번째 식 : v0 + (u * (v1 - v0)) + (v * (v2 - v0));

-> 충돌에 사용된 3개의 점을 이용해서 구한다. v0의 점에서 v1방향으로 u만큼 , v2방향으로 v만큼 움직여서 구한다.

이렇게 하면 충돌된 여부 뿐만 아니라 충돌된 좌표가 어디인지 구해낼 수 있다.

+ Recent posts