Picking

이승덱·2021년 8월 20일
0

Picking

  • 마우스 클릭을 통해 Object를 고를 때 사실 화면에서 보여지는 것은 2D로 변환된 Screen Space이다.

  • 따라서 우린 Ray Casting을 사용해 레이저를 쏘듯 카메라에서 Object까지 가상의 선을 그어 Object와의 HIT여부를 검사한다.

  • Collider Component를 만들어 Ray cast 충돌 검사에 사용한다.


shared_ptr<GameObject> SceneManager::Pick(int32 screenX, int32 screenY)
{
	shared_ptr<Camera> camera = GetActiveScene()->GetMainCamera();

	float width = static_cast<float>(GEngine->GetWindow().width);
	float height = static_cast<float>(GEngine->GetWindow().height);

	Matrix projectionMatrix = camera->GetProjectionMatrix();

	// ViewSpace에서 Picking 진행
	float viewX = (+2.0f * screenX / width - 1.0f) / projectionMatrix(0, 0);
	float viewY = (-2.0f * screenY / height + 1.0f) / projectionMatrix(1, 1);

	Matrix viewMatrix = camera->GetViewMatrix();
	Matrix viewMatrixInv = viewMatrix.Invert();

	auto& gameObjects = GET_SINGLE(SceneManager)->GetActiveScene()->GetGameObjects();

	float minDistance = FLT_MAX;
	shared_ptr<GameObject> picked;

	for (auto& gameObject : gameObjects)
	{
		if (gameObject->GetCollider() == nullptr)
			continue;

		// ViewSpace에서의 Ray 정의
		Vec4 rayOrigin = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
		Vec4 rayDir = Vec4(viewX, viewY, 1.0f, 0.0f);

		// WorldSpace에서의 Ray 정의
		rayOrigin = XMVector3TransformCoord(rayOrigin, viewMatrixInv);
		rayDir = XMVector3TransformNormal(rayDir, viewMatrixInv);
		rayDir.Normalize();

		// WorldSpace에서 연산
		float distance = 0.f;
		if (gameObject->GetCollider()->Intersects(rayOrigin, rayDir, OUT distance) == false)
			continue;

		if (distance < minDistance)
		{
			minDistance = distance;
			picked = gameObject;
		}
	}

	return picked;
}
  • 스크린의 높이와 너비와 마우스의 클릭 위치를 가져와 ViewSpace에서 Picking을 한다.

  • World Space에서 충돌검사를 하기 위해 view Matrix의 역행렬을 이용해 View Space에서의 Ray를 WorldSpace로 변환.

  • WorldSpace에서 Ray와 Collider사이의 충돌 여부 계산.

결과

  • 디버깅을 통해 구체가 클릭되면 HIT ! 이 출력되게 설정하였다.
profile
공부 기록용 블로그입니다

0개의 댓글