픽킹

반응형


3d 게임에서 중요한 부분을 차지하고 있는 픽킹.
이번기회에 다시 한번 정리해보자.

1. 카메라 공간에서 픽킹광선의 계산.
// 뷰포트 정보를 얻는다.
 UINT numViewports = 0;
 device->RSGetViewports(&numViewports, NULL); // 뷰포트의 갯수를 얻는다.

 D3D10_VIEWPORT* vp = new D3D10_VIEWPORT[numViewports];
 device->RSGetViewports(&numViewports, vp);
 
 // 보통 뷰포트는 하나므로 처음것을 가져온다.
 float vpWidth  = (float)vp[0].Width;
 float vpHeight = (float)vp[0].Height;

/*
 카메라 공간에서 피킹광선 계산
 피킹광선은 카메라 공간에서 카메라 중심점 po(0,0,0)에서 시작하고
 투사창에서의 한점 p(px,py,1)을 지나는 광선이다.
 즉 광선의 방향(u) = 한점(p) - 카메라중심점(po) = p
 */
 float px = ((( 2.0f*pt.x) / vpWidth) - 1.0f) / matProj(0, 0);
 float py = -(((2.0f*pt.y) / vpHeight) - 1.0f) / matProj(1, 1);
 SAFE_DELETE(vp);

2. 월드공간에서의 픽킹광선 계산
/*
 뷰공간의 피킹관선을 월드공간으로 변환
 v피킹광선과 물체는 동일좌표계에서 정의되도록 해야 교차검사를 수행할 수 있다.
 카메라 공간에서 정의된 피킹광선을 월드공간으로 바꾼다.
 */
 D3DXMATRIX viewInverse;
 camera.GetViewMatrix(&matView);
 D3DXMatrixInverse(&viewInverse, 0, &matView); 

/*
 광선을 변환한다.
 뷰행렬의 역행렬을 피킹광선에 적용하면 피킹광선은 월드공간으로 변환된다.
 */
 D3DXVec3TransformCoord( &ray.origin, &ray.origin, &viewInverse);
 D3DXVec3TransformNormal( &ray.direction, &ray.direction, &viewInverse);
 D3DXVec3Normalize(&ray.direction, &ray.direction);

3. 광선과 메시의 교차테스트
이렇게 월드공간에서의 픽킹관선을 구하였다.
픽킹광선을 구했으니 이제 광선과 메시와의 교차 테스트를 한다.
그런데 메시의 좌표들은 로컬공간에 정의된다.
그래서 월드공간에서의 광선을 다시 로컬공간으로 변환시켜 줘야 한다.

 D3DXMATRIX matWorldInverse;
 D3DXMatrixInverse(&matWorldInverse, NULL, &matWorld);
 D3DXVec3TransformCoord(&ray.origin, &ray.origin, &matWorldInverse);
 D3DXVec3TransformNormal(&ray.direction, &ray.direction, &matWorldInverse);
 D3DXVec3Normalize(&ray.direction, &ray.direction);

// 교차테스트
 UINT hitCount = 0;
 UINT faceIndex;
 HRESULT hr = dxMesh->Intersect(&ray.origin, &ray.direction, &hitCount, &faceIndex, NULL, NULL, NULL, NULL);
 if(FAILED(hr))
 {
  ::MessageBox(0, L"D3DXIntersect() - Failed", L"ERROR", MB_OK);
 }

ID3DX10Mesh에서 메시와 광선과의 교차테스트를 위한 함수인 intersect를 제공해 준다.
반환값은 hitCount로 광선이 메시와 교차한 횟수이다.

4. 광선과 경계구와의 교차 테스트
사실 경계구, 경계상자(AABB, OBB)는 모두 어느정도 부정확 점을 가지고 있다.
실제 데이터가 아니라 감싸는 데이터로 테스트를 하기 때문이다.
하지만 계산량에 있어서 효율성을 지닌다.

Ray ray = GetRayInWorldSpace(pt);

 // 로컬공간의 경계구를 월드공간으로 변환
 BoundingSphere sphereWorld = sphere;
 sphereWorld.center_.x = matWorld._41;
 sphereWorld.center_.y = matWorld._42;
 sphereWorld.center_.z = matWorld._43;

 // 광선과 구의 교차검사
 D3DXVECTOR3 v = ray.origin - sphereWorld.center_;
 float b = 2.0f * D3DXVec3Dot(&ray.direction, &v);
 float c = D3DXVec3Dot(&v, &v) - (sphereWorld.radius_ * sphereWorld.radius_); // 구의 중심점

 float discriminant = (b*b) - (4.0f*c); // 판별식
 if(discriminant < 0.0f)     // 허수여부 판별
  return false;

 discriminant = sqrtf(discriminant);

 float s0 = (-b + discriminant) * 0.5f;
 float s1 = (-b - discriminant) * 0.5f;

 if(s0 < 0.0f && s1 < 0.0f) // 모든 해가 < 0 이면 교차하지 않는다.
  return false;


'Study > Directx 10' 카테고리의 다른 글

다수의 광원.  (0) 2010.05.15
쉐이딩 모드  (0) 2010.05.14
경계볼륨 - 경계구, 경계상자  (0) 2010.05.11
3ds mesh file load  (0) 2010.05.11
mesh 생성  (0) 2010.05.11
TAGS.

Comments