레이캐스팅
보이지 않는 광선을 쏘고 광선과 collider의 충돌 여부를 통하여 판별
=>
유저의 입력을 받아서 계산하는 경우
픽셀 좌표(스크린 공간)를 이용하여 역변환
screen -> clip -> view -> world -> local
광선과 판별할 물체들을 view ~ local 중 하나의 공간으로 통일하여 계산
구 충돌 판별
구의 중심 c에서 구의 임의의 표면점 p까지 거리는 d
ray 벡터 r은 임의의 크기를 갖는 벡터 u와의 합으로 나타낼 수 있음
r 벡터를 1식의 p에 넣고 제곱후 정리하면 t에 대한 2차 방정식을 도출할 수 있음
이를 2차 방정식의 판별식에 넣어 근의 개수로 충돌을 판별
DX12에서 이러한 함수를 제공함
base collider 클래스 구현
다양한 콜라이더를 만들 때 공통적으로 상속할 베이스 클래스
class BaseCollider : public Component
{
public:
...
virtual bool Intersects(Vec4 rayOrigin, Vec4 rayDir, OUT float& distance) = 0;
...
};
충돌 판별을 할 함수를 상속받은 클래스들이 구현하도록 정의
sphere collider 클래스 구현
class SphereCollider : public BaseCollider
{
public:
...
virtual void FinalUpdate() override;
virtual bool Intersects(Vec4 rayOrigin, Vec4 rayDir, OUT float& distance) override;
...
private:
...
BoundingSphere _boundingSphere;
};
DX12에서 제공하는 BoundingSphere를 이용하여 계산 시 사용
void SphereCollider::FinalUpdate()
{
_boundingSphere.Center = GetGameObject()->GetTransform()->GetWorldPosition();
...
_boundingSphere.Radius = _radius * max(max(scale.x, scale.y), scale.z);
}
bool SphereCollider::Intersects(Vec4 rayOrigin, Vec4 rayDir, OUT float& distance)
{
return _boundingSphere.Intersects(rayOrigin, rayDir, OUT distance);
}
final update 때 구의 중심점과 반지름을 결정
충돌 시 BoundingSphere의 Intersects 사용
input 클래스 수정
class Input
{
...
const POINT& GetMousePos() { return _mousePos; }
...
private:
...
POINT _mousePos = {};
};
void Input::Update()
{
...
::GetCursorPos(&_mousePos);
::ScreenToClient(GEngine->GetWindow().hwnd, &_mousePos);
}
마우스 입력을 받고 엔진에서 사용할 수 있도록 수정
GetCursorPos와 ScreenToClient 함수들은 윈도우 api
충돌 판별
shared_ptr<class GameObject> SceneManager::Pick(int32 screenX, int32 screenY)
{
shared_ptr<Camera> camera = GetActiveScene()->GetMainCamera();
...
Matrix projectionMatrix = camera->GetProjectionMatrix();
float viewX = (+2.0f * screenX / width - 1.0f) / projectionMatrix(0, 0);
float viewY = (-2.0f * screenY / height + 1.0f) / projectionMatrix(1, 1);
Matrix viewMatrixInv = camera->GetViewMatrix().Invert();
...
Vec4 rayOrigin = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
Vec4 rayDir = Vec4(viewX, viewY, 1.0f, 0.0f);
rayOrigin = XMVector3TransformCoord(rayOrigin, viewMatrixInv);
rayDir = XMVector3TransformNormal(rayDir, viewMatrixInv);
rayDir.Normalize();
for (auto& gameObject : gameObjects)
{
...
if (gameObject->GetCollider()->Intersects(rayOrigin, rayDir, OUT distance) == false)
continue;
if (distance < minDistance)
{
minDistance = distance;
picked = gameObject;
}
}
return picked;
}
클릭된 스크린 좌표를 카메라 공간의 좌표로 변환 후
카메라에서 near 평면까지의 방향을 추출
그리고 쏴야 할 ray의 방향 벡터와 원점 벡터를 역변환 행렬을
곱하여 월드 공간으로 좌표계 변환
게임 오브젝트들과 충돌을 계산하여 가장 가까운 물체를 반환