Post Lists

2018년 11월 20일 화요일

Bullet Physics 마우스 클릭 getRayTo 함수 분석

Bullet Physics 라이브러리의 CommonRigidBodyBase.h에 있는

btVector3 getRayTo(int x, int y)의 함수를 분석하겠다. 이것은 사용자의 클릭을 통해 Object picking을 구현하기 위해서 이다.



btVector3 getRayTo(int x, int y)
{
 CommonRenderInterface* renderer = m_guiHelper->getRenderInterface();

 if (!renderer)
 {
  btAssert(0);
  return btVector3(0, 0, 0);
 }

 float top = 1.f;
 float bottom = -1.f;
 float nearPlane = 1.f;
 float tanFov = (top - bottom) * 0.5f / nearPlane;
 float fov = btScalar(2.0) * btAtan(tanFov); // 구하기 Vertical Field of View
// float fov = camera.Zoom 그냥 이렇게 해도된다.
// 즉 perspective에 보내는 Vertical Field of View값을 말한다.

 btVector3 camPos, camTarget;

 renderer->getActiveCamera()->getCameraPosition(camPos);
 renderer->getActiveCamera()->getCameraTargetPosition(camTarget);

 // Camaera Pos에서 ray를 날림
 btVector3 rayFrom = camPos;
 // Ray Forward는 카메라가 바라보는 방향으로 던지기 위해서 함.
 btVector3 rayForward = (camTarget - camPos);
 rayForward.normalize();
 float farPlane = 10000.f;
 rayForward *= farPlane; // farplane을 10000으로 설정하여 멀리 날림

 btVector3 rightOffset;
 btVector3 cameraUp = btVector3(0, 0, 0); // World-Up 방향을 받음
 cameraUp[m_guiHelper->getAppInterface()->getUpAxis()] = 1;

 btVector3 vertical = cameraUp; // 이것을 위로가는 방향으로 설정해놓고

 btVector3 hor;
 hor = rayForward.cross(vertical); // rayForward를 기준으로 기저를 다시 구함.
 hor.safeNormalize();              // hor을 통해 local x축 구하고,
 vertical = hor.cross(rayForward);  // 다시 local y축 다시 구하기
 vertical.safeNormalize(); // safeNormalize는 cross값이 epsilon보다 작으면 x축 가리키는 것을 반환함.
 // 그리고 이를 통해 far plane의 width와 height를 구하려함.
 float tanfov = tanf(0.5f * fov); 

 hor *= 2.f * farPlane * tanfov;   // 여기다가 screen ratio곱하면 far plane width됌
 vertical *= 2.f * farPlane * tanfov; // vertical은 far plane height 길이 구해짐

 btScalar aspect;
 float width = float(renderer->getScreenWidth());
 float height = float(renderer->getScreenHeight());

 aspect = width / height;

 hor *= aspect; // far plane width 완성

 btVector3 rayToCenter = rayFrom + rayForward; // 1. camera에서 farPlane의 중앙으로 보냄
 btVector3 dHor = hor * 1.f / width;
 btVector3 dVert = vertical * 1.f / height;

 // 2. farPlane 중앙에서 윈도우 창은 왼쪽 상단에서부터 좌표가 시작하여 
 // 오른쪽으로 x값 증가, 아래쪽으로 y값 증가임.
 // hor은 far plane의 x축, 그러니까 오른쪽 가는 방향 길이를 가지고 있고 (far plane width)
 // verticla은 far plane의 y축, 그러니까 위로 가는 방향 길이를 가지고 있음 (far plane height)
 // 그래서 rayTo은 far plane의 왼쪽 상단을 가리키게 됌.
 btVector3 rayTo = rayToCenter - 0.5f * hor + 0.5f * vertical; 

 // 위에서 dHor = hor * 1.f / width 했는데
 // width는 near plane의 길이임.
 // 클릭은 near plane에서 이루어지므로, 그 near plane의 길이만큼 scale한 거임
 // dVert도 마찬가지임
 // 그래서 far plane의 어디지점에 향하는지 가리키기 위해
 // rayTo에 더해지는 것을 자세히 보면
 // rayTo += (x / width) * dHor임.
 // 그말은 dHor이 1.f / width가 없다하면, far plane의 오른쪽 방향으로 가는 길이인데
 // near plane에서 얼마만큼 가서 찍었냐는 거임
 // 그래서 아래의 식을 통해 far plane의 x축 어디에 있는지 알 수 있고
 // dVert는 위를 향하고 있고, 윈도우 좌표계는 아래를 향하고 있기 때문에 -를 통해 해결함.
 rayTo += btScalar(x) * dHor; 
 rayTo -= btScalar(y) * dVert;
 return rayTo;
}

댓글 2개:

  1. 힘든 길로 가시는 군요. 동병상련(?)을 느낍니다. 화이팅~!^^

    답글삭제
    답글
    1. 댓글을 이제 봤네요. 응원 감사합니다~
      블로그랑 깃헙을 둘러보게 됐는데.. 제 미래의 목표이신 분이네요!
      오픈소스 게임엔진 릴리즈 하셨는데 종종 참고하겠습니다!

      삭제