Picking

ㅋㅋ·2022년 7월 31일
0

DirectX12강의

목록 보기
37/39

레이캐스팅

보이지 않는 광선을 쏘고 광선과 collider의 충돌 여부를 통하여 판별

=>

유저의 입력을 받아서 계산하는 경우

픽셀 좌표(스크린 공간)를 이용하여 역변환

screen -> clip -> view -> world -> local

광선과 판별할 물체들을 view ~ local 중 하나의 공간으로 통일하여 계산


구 충돌 판별

  1. pc=d\left| \vec{p} - \vec{c} \right| = d

구의 중심 c에서 구의 임의의 표면점 p까지 거리는 d

  1. r=q+tu\vec{r} = \vec{q} + t\vec{u}

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의 방향 벡터와 원점 벡터를 역변환 행렬을

곱하여 월드 공간으로 좌표계 변환

게임 오브젝트들과 충돌을 계산하여 가장 가까운 물체를 반환

0개의 댓글