본문 바로가기

DirectX11 3D/기본 문법

<DirectX11 3D> 69 - OBB Raycast(OBB와 반직선 교차)

 

필요한 개념


AABB와 OBB 차이

 

Slab을 활용한 OBB와 Ray간의 충돌판정

 

우리가 여태까지 해왔던 충돌방식은 
AABB(Axis Aligned Bounding Box) : 사각형이 회전되어있지 않은 상황에서의 충돌이다.
회전되어있지 않은 충돌이어서 두 사각형의 거리의 차만 보면 된다.
예를 들어 사각형이 있다면 한쪽 방향의 반직선이 있다면
반직선의 거리가 사각형에 들어오는지 확인하면 된다.

OBB(Oriendted Bounding Box) : 회전 적용된 상태의 충돌(어딘가를 향하는 사각형을 의미)

일반적인 충돌 체크
Ray로 봤을때 각 방향에 거리가 사각형 전체 안에 들어와있는지 체크하면 된다.

그러나 OBB끼리의 충돌은 얘기가 복잡해진다.
OBB 끼리의 충돌은 Slab이 활용된다.
Slab - 어떤 축 두개를 연장했을 때 두 축 사이의 무한한 공간
obb는 슬랩의 영역에 선분이 교차하는지 체크하는 방법
슬랩 이외에 거리로 판단하는 방법도 가능

 

Slab 설명

더보기

들어가기 전에...

면과 직선의 충돌에 대해 알아야 한다.
점 P, Q에 의해서 만들어지는 직선은, 직선 L = P + (Q-P)*t 로 나타낼 수 있다.
이때, 면과 직선이 충돌하는 지점을 t에 의해서 표현 할 수 있다.
즉, t = 0  에서 충돌했다고 하면 P 지점에서 충돌한 것이고
     t = 1  에서 충돌했다고 하면 Q 지점에서 충돌한 것이고
     t = 10 에서 충돌했다고 하면 P+(Q-P)*10 지점에서 충돌한 것이다.

아래 만나는 지점에(xmin, xmax 등등) 관한 값은 모두 이 t에 해당하는 값이다.

Slab method. (Kay-Kajiya test)

ray와 box 간의 가장 빠르면서도 신뢰할 수 있는 충돌 검사가 있는데,
이것이 바로 slab method이다.
검사 방법은 OBB 이거나 AABB이거나 마찬가지인데, 여기서는 AABB를 이용하여 설명하겠다




slab method는 AABB의 면을 기준으로 하는 slab을 이용한다.   (slab은 어느정도 두껍고 평평한 돌)
위의 그림에서 x slab은 노랗게 칠해 놓은 부분이다.
그에 수직한 부분의 직사각형 모양이 y slab 부분이다.

위 그림의 회색 굵은 박스(AABB)를 통과하는 붉은 선과 통과하지 못하는 파란 선에 대해 생각해 보자.

파란 선:
  - 충돌 안 하는 경우
  - 선이 출발해서 만나는 곳이 ymin, ymax, xmin, xmax. (출발 부터의 거리라 생각해도 됨)
  - 판별법:
    xmin, ymin중 최대값은 xmin. (이것이 max of min).
    xmax, ymax중 최소값은 ymax. (이것이 min of max)
    max of min > min of max 경우는 충돌 안 함.

붉은 선:
  - 충돌 하는 경우
  - 선이 출발해서 만나는 곳이 ymin, xmin, ymax, xmax.
  - 판별법:
    xmin, ymin중 최대값은 xmin. (이것이 max of min)
    xmax, ymax중 최소값은 ymax. (이것이 min of max)
    max of min <= min of max 경우는 충돌 함.

 

본문 출처 : https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=ybill&logNo=120103496030

 

Ray Box intersection. (Slab method)

들어기가 전에... 면과 직선의 충돌에 대해 알아야 한다. 점 P, Q에 의해서 만들어지는 직선은, 직선 L ...

blog.naver.com

 


Unprojection으로 마우스로 삼각형교차하는 반직선 Raycast를 만들었었다.
이것을 활용해 Box와 충돌되었다면 그 박스의 Rendering 색깔을 빨간색으로 바꿔줬다.
다른데 클릭하면 다시 녹색(기본색)으로 돌아온다.
(Raycast와 OBB 충돌 체크했음, OBB Raycast)


나중에 어느본에 다가 갑옷을 입히거나, 무기를 붙이고 이럴때 사용(선택할때 사용)
흔히 디자이너가 max 디자인 할때 해놓긴한다. 그런데 직접 만들경우
캐릭터는 계속 회전하니까 OBB로 만들고, 반직선을 쏴서 캐릭터를 선택할 수 있도록 할 수 있다.
FPS에서 간단히 게임을 만든다면 총알이 적의 본에다가 충돌하도록 할 수 있다.
어느 지점을 클릭해서 캐릭터 이동도 가능하다.

정리
Obb박스와 Ray의 방향별로 정규화시킨 거리 간의 교차

 

 

 

 

Collider.h


더보기
#pragma once

// 반직선
struct Ray
{
	Ray() : Position(0, 0, 0), Direction(0, 0, 0)
	{
	}

	Ray(Vector3& position, Vector3& direction) :
		Position(position), Direction(direction)
	{
	}

	Vector3 Position;
	Vector3 Direction;
};

// 사용 편하게 하려고 묶어놈
struct ColliderObject
{
	class Transform* Init = NULL;
	class Transform* Transform = NULL;
	class Collider* Collider = NULL;
};

class Collider
{
private:
	struct Bounding;

public:
	Collider(Transform* transform, Transform* init = NULL);
	~Collider();

	void Update();
	// 보통 충돌체는 녹색이어서, 녹색으로 기본값줌
	void Render(Color color = Color(0, 1, 0, 1));

	Transform* GetTransform() { return transform; }


	// outDistance : 충돌거리 리턴을 위해
	bool Intersection(Ray& ray, float* outDistance);

	bool Intersection(Collider* other);


private:
	// 분리축 생성
	// 분리축을 검사하는 애를 만듬
	bool SeperatingPlane(Vector3& position, Vector3& direction, Bounding& box1, Bounding& box2);
	
	bool Collision(Bounding& box1, Bounding& box2);

	// 외적 함수
	// DirectX의 Cross함수는 약간 지저분해지므로, 외적식을 별도로 작성
	Vector3 Cross(Vector3& vec1, Vector3& vec2);

private:
	// 검사를 하기 위한 구조체
	struct Bounding
	{
		Vector3 Position; // 검사를 할 충돌 박스의 위치

		Vector3 AxisX; // 축의 방향
		Vector3 AxisY;
		Vector3 AxisZ;

		Vector3 HalfSize; // 박스의 사이즈의 반(사이즈 길이 1로 검사하는 것이 아닌, -0.5 ~ 0.5 크기를 잡아서 0.0 ~ 0.5검사)
	} bounding;

private:
	// Init이 기준이 됨
	// 최종 위치 = 기준 초기 값(Init) * 이동한 트랜스폼
	// 처음에 회전이나 이런거 기준 잡아 놓고 외부에서 transform 받아오면
	// 그거 곱해서 위치를 구함
	Transform* init = NULL;
	Transform* transform = NULL;

	// 라인을 통해 그려줘야 눈으로 볼 수 있다.
	Vector3 lines[8];
};

 

 

 

 

 

 

 

Collider.cpp 추가된 내용


더보기
bool Collider::Intersection(Ray& ray, float* outDistance)
{
	*outDistance = 0.0f;

	Vector3 dest[8];

	Transform temp;
	temp.World(transform->World());

	if (init != NULL)
		temp.World(init->World() * transform->World());

	Matrix world = temp.World();

	// 라인그릴때 0번을 좌하단을 줬고(제일 작은 값), 7번을 우상단(가장 큰 값)으로 줬다.
	// min, max있으면 큐브 정의할 수 있듯이
	Vector3 minPosition, maxPosition;
	D3DXVec3TransformCoord(&minPosition, &lines[0], &world);
	D3DXVec3TransformCoord(&maxPosition, &lines[7], &world);

	// 나중에 Direction을 나눠서 정규화 시켜서 사용할 것임(0 ~ 1까지 나옴)
	// 이따가 나눌것이니까, 최소값을 넣어준것이다.(없어도 되긴 된다. 0이면 밑에 충돌이 안된것으로 체크해서)
	// 그래도 초기값을 넣어주고 체크들어가는게 훨씬 안정적
	if (fabsf(ray.Direction.x) == 0.0f) ray.Direction.x = 1e-6f;
	if (fabsf(ray.Direction.y) == 0.0f) ray.Direction.y = 1e-6f;
	if (fabsf(ray.Direction.z) == 0.0f) ray.Direction.z = 1e-6f;

	float minValue = 0.0f, maxValue = FLT_MAX;

	// 0이면 체크가 안됨
	//Check X
	if (fabsf(ray.Direction.x) >= 1e-6f)
	{
		float value = 1.0f / ray.Direction.x;
		// value를 비율로 곱함
		float minX = (minPosition.x - ray.Position.x) * value;
		float maxX = (maxPosition.x - ray.Position.x) * value;

		if (minX > maxX)
		{
			float temp = minX;
			minX = maxX;
			maxX = temp;
		}

		minValue = max(minX, minValue);
		maxValue = min(maxX, maxValue);

		// 거리안에 들어와있지 않다면 false
		if (minValue > maxValue)
			return false;
	}
	else if (ray.Position.x < minPosition.x || ray.Position.x > maxPosition.x)
		return false;

	// y,z 축 x와 체크방법 동일

	//Check Y
	if (fabsf(ray.Direction.y) >= 1e-6f)
	{
		float value = 1.0f / ray.Direction.y;
		float minY = (minPosition.y - ray.Position.y) * value;
		float maxY = (maxPosition.y - ray.Position.y) * value;

		if (minY > maxY)
		{
			float temp = minY;
			minY = maxY;
			maxY = temp;
		}

		minValue = max(minY, minValue);
		maxValue = min(maxY, maxValue);

		if (minValue > maxValue)
			return false;
	}
	else if (ray.Position.y < minPosition.y || ray.Position.y > maxPosition.y)
		return false;


	//Check Z
	if (fabsf(ray.Direction.z) >= 1e-6f)
	{
		float value = 1.0f / ray.Direction.z;
		float minZ = (minPosition.z - ray.Position.z) * value;
		float maxZ = (maxPosition.z - ray.Position.z) * value;

		if (minZ > maxZ)
		{
			float temp = minZ;
			minZ = maxZ;
			maxZ = temp;
		}

		minValue = max(minZ, minValue);
		maxValue = min(maxZ, maxValue);

		if (minValue > maxValue)
			return false;
	}
	else if (ray.Position.z < minPosition.z || ray.Position.z > maxPosition.z)
		return false;

	// 여기까지 왔다면 x,y,z이 거리안에 박스가 들어와있다는 것이다.(충돌상황)
	// minValue는 최소거리
	*outDistance = minValue;
	return true;
}

bool Collider::Intersection(Collider* other)
{
	// 자신의 바운딩 정보와 체크하려는 충돌체의 바운딩 정보로 검사를 수행
	return Collision(this->bounding, other->bounding);
}


bool Collider::SeperatingPlane(Vector3& position, Vector3& direction, Bounding& box1, Bounding& box2)
{
	// 내적하고
	float val = fabsf(D3DXVec3Dot(&position, &direction));

	float val2 = 0.0f;
	// 각 박스의 거리 방향 크기를 이용해서 검사함
	// 분리축 이론 찾아보면 어렵지 않게 이해가능
	val2 += fabsf(D3DXVec3Dot(&(box1.AxisX * box1.HalfSize.x), &direction));
	val2 += fabsf(D3DXVec3Dot(&(box1.AxisY * box1.HalfSize.y), &direction));
	val2 += fabsf(D3DXVec3Dot(&(box1.AxisZ * box1.HalfSize.z), &direction));
	val2 += fabsf(D3DXVec3Dot(&(box2.AxisX * box2.HalfSize.x), &direction));
	val2 += fabsf(D3DXVec3Dot(&(box2.AxisY * box2.HalfSize.y), &direction));
	val2 += fabsf(D3DXVec3Dot(&(box2.AxisZ * box2.HalfSize.z), &direction));
	// 평면방정식의 이론과 비슷하다고 생각하고 이해해도됨

	return val > val2;
}


bool Collider::Collision(Bounding& box1, Bounding& box2)
{
	Vector3 position = box2.Position - box1.Position;

	// 총 128개의 분리축 검사를 수행해야 하지만 중복을 제거하면 
	// 15개로 줄어든다.
	if (SeperatingPlane(position, box1.AxisX, box1, box2) == true) return false;
	if (SeperatingPlane(position, box1.AxisY, box1, box2) == true) return false;
	if (SeperatingPlane(position, box1.AxisZ, box1, box2) == true) return false;

	if (SeperatingPlane(position, box2.AxisX, box1, box2) == true) return false;
	if (SeperatingPlane(position, box2.AxisY, box1, box2) == true) return false;
	if (SeperatingPlane(position, box2.AxisZ, box1, box2) == true) return false;

	if (SeperatingPlane(position, Cross(box1.AxisX, box2.AxisX), box1, box2) == true) return false;
	if (SeperatingPlane(position, Cross(box1.AxisX, box2.AxisY), box1, box2) == true) return false;
	if (SeperatingPlane(position, Cross(box1.AxisX, box2.AxisZ), box1, box2) == true) return false;

	if (SeperatingPlane(position, Cross(box1.AxisY, box2.AxisX), box1, box2) == true) return false;
	if (SeperatingPlane(position, Cross(box1.AxisY, box2.AxisY), box1, box2) == true) return false;
	if (SeperatingPlane(position, Cross(box1.AxisY, box2.AxisZ), box1, box2) == true) return false;

	if (SeperatingPlane(position, Cross(box1.AxisZ, box2.AxisX), box1, box2) == true) return false;
	if (SeperatingPlane(position, Cross(box1.AxisZ, box2.AxisY), box1, box2) == true) return false;
	if (SeperatingPlane(position, Cross(box1.AxisZ, box2.AxisZ), box1, box2) == true) return false;

	return true;
}

Vector3 Collider::Cross(Vector3& vec1, Vector3& vec2)
{
	// 교차시켜서
	float x = vec1.y * vec2.z - vec1.z * vec2.y;
	float y = vec1.z * vec2.x - vec1.x * vec2.z;
	float z = vec1.x * vec2.y - vec1.y * vec2.x;

	return Vector3(x, y, z);
}

 

 

 ObbRaycast.h


더보기
#pragma once
#include "Systems/IExecute.h"

class ObbRaycastDemo : public IExecute
{
public:
	virtual void Initialize() override;
	virtual void Ready() override {};
	virtual void Destroy() override;
	virtual void Update() override;
	virtual void PreRender() override {};
	virtual void Render() override;
	virtual void PostRender() override {};
	virtual void ResizeScreen() override {};

private:
	void Mesh();
	void Airplane();
	void Kachujin();
	void KachujinCollider();

	// 충돌체크가 되는지 검사
	void CheckIntersection();

	void Pass(UINT mesh, UINT model, UINT anim);

private:
	Shader* shader;

	CubeSky* sky;

	Material* floor;
	Material* stone;
	Material* brick;
	Material* wall;

	MeshRender* cube;
	MeshRender* cylinder;
	MeshRender* sphere;
	MeshRender* grid;

	ModelRender* airplane = NULL;

	ModelAnimator* kachujin = NULL;

	// Collider의 인스턴싱은 몇개 인지 모른다.(그래서 *하나 더 붙음)
	ColliderObject** colliders;

	// 충돌된 번호
	int collisionIndex = -1;

	// 나중에 Path 관리를 편하게 하기 위해
	vector<MeshRender*> meshes;
	vector<ModelRender*> models;
	vector<ModelAnimator*> animators;
};

 

 

 

 

 

 

 

ObbRaycast.cpp


더보기
#include "stdafx.h"
#include "ObbRaycastDemo.h"

void ObbRaycastDemo::Initialize()
{
	Context::Get()->GetCamera()->RotationDegree(20, 0, 0);
	Context::Get()->GetCamera()->Position(1, 36, -85);
	

	shader = new Shader(L"55_Render.fx");

	sky = new CubeSky(L"Environment/GrassCube1024.dds");

	Mesh();
	Airplane();
	Kachujin();
	KachujinCollider();
}

void ObbRaycastDemo::Destroy()
{
	SafeDelete(shader);
	
	SafeDelete(sky);
	SafeDelete(cube);
	SafeDelete(cylinder);
	SafeDelete(sphere);
	SafeDelete(grid);

	SafeDelete(floor);
	SafeDelete(stone);
	SafeDelete(brick);
	SafeDelete(wall);

	SafeDelete(airplane); 
	SafeDelete(kachujin);
}

void ObbRaycastDemo::Update()
{
	sky->Update();

	cube->Update();
	grid->Update();
	cylinder->Update();
	sphere->Update();

	airplane->Update();
	kachujin->Update();

	for (UINT i = 0; i < kachujin->GetTransformCount(); i++)
	{
		// 카추진 위치 얻어옴(업데이트 시킬)
		Matrix attach;
		kachujin->GetAttachTransform(i, &attach);

		// 얼마만큼 이동할지
		colliders[i]->Collider->GetTransform()->World(attach);
		colliders[i]->Collider->Update();
	}

	CheckIntersection();
}

void ObbRaycastDemo::Render()
{
	sky->Render();
	
	Pass(0, 1, 2);

	wall->Render();
	sphere->Render();

	brick->Render();
	cylinder->Render();

	stone->Render();
	cube->Render();

	floor->Render();
	grid->Render();

	airplane->Render();
	kachujin->Render();

	for (UINT i = 0; i < kachujin->GetTransformCount(); i++)
	{
		// 충돌되었다면 빨간색
		if((int)i == collisionIndex)
			colliders[i]->Collider->Render(Color(1, 0, 0, 1));

		// 아니라면 기본색
		else
			colliders[i]->Collider->Render();
	}
}

void ObbRaycastDemo::Mesh()
{
	//Create Material
	{
		floor = new Material(shader);
		floor->DiffuseMap("Floor.png");
		//floor->SpecularMap("Floor_Specular.png");
		//floor->NormalMap("Floor_Normal.png");
		//floor->Specular(1, 1, 1, 20);

		stone = new Material(shader);
		stone->DiffuseMap("Stones.png");
		//stone->SpecularMap("Stones_Specular.png");
		//stone->NormalMap("Stones_Normal.png");
		//stone->Specular(1, 1, 1, 20);

		brick = new Material(shader);
		brick->DiffuseMap("Bricks.png");
		//brick->SpecularMap("Bricks_Specular.png");
		//brick->NormalMap("Bricks_Normal.png");
		//brick->Specular(1, 0.3f, 0.3f, 20);

		wall = new Material(shader);
		wall->DiffuseMap("Wall.png");
		//wall->SpecularMap("Wall_Specular.png");
		//wall->NormalMap("Wall_Normal.png");
		//wall->Specular(1, 1, 1, 20);
	}

	//Create Mesh
	{
		Transform* transform = NULL;

		cube = new MeshRender(shader, new MeshCube());
		transform = cube->AddTransform();
		transform->Position(0, 5, 0);
		transform->Scale(20, 10, 20);

		grid = new MeshRender(shader, new MeshGrid(5, 5));
		transform = grid->AddTransform();
		transform->Position(0, 0, 0);
		transform->Scale(12, 1, 12);

		cylinder = new MeshRender(shader, new MeshCylinder(0.5f, 3.0f, 20, 20));
		sphere = new MeshRender(shader, new MeshSphere(0.5f, 20, 20));
		for (UINT i = 0; i < 5; i++)
		{
			transform = cylinder->AddTransform();
			transform->Position(-30, 6, -15.0f + (float)i * 15.0f);
			transform->Scale(5, 5, 5);

			transform = cylinder->AddTransform();
			transform->Position(30, 6, -15.0f + (float)i * 15.0f);
			transform->Scale(5, 5, 5);


			transform = sphere->AddTransform();
			transform->Position(-30, 15.5f, -15.0f + (float)i * 15.0f);
			transform->Scale(5, 5, 5);

			transform = sphere->AddTransform();
			transform->Position(30, 15.5f, -15.0f + (float)i * 15.0f);
			transform->Scale(5, 5, 5);
		}
	}

	sphere->UpdateTransforms();
	cylinder->UpdateTransforms();
	cube->UpdateTransforms();
	grid->UpdateTransforms();

	// 렌더링 할 것들을 추가
	meshes.push_back(sphere);
	meshes.push_back(cylinder);
	meshes.push_back(cube);
	meshes.push_back(grid);
}

void ObbRaycastDemo::Airplane()
{
	airplane = new ModelRender(shader);
	airplane->ReadMesh(L"B787/Airplane");
	airplane->ReadMaterial(L"B787/Airplane");

	Transform* transform = airplane->AddTransform();
	transform->Position(2.0f, 9.91f, 2.0f);
	transform->Scale(0.004f, 0.004f, 0.004f);
	airplane->UpdateTransforms();

	models.push_back(airplane);
}

void ObbRaycastDemo::Kachujin()
{
	kachujin = new ModelAnimator(shader);
	kachujin->ReadMesh(L"Kachujin/Mesh");
	kachujin->ReadMaterial(L"Kachujin/Mesh");
	kachujin->ReadClip(L"Kachujin/Sword And Shield Idle");
	kachujin->ReadClip(L"Kachujin/Sword And Shield Walk");
	kachujin->ReadClip(L"Kachujin/Sword And Shield Run");
	kachujin->ReadClip(L"Kachujin/Sword And Shield Slash");
	kachujin->ReadClip(L"Kachujin/Salsa Dancing");


	Transform* transform = NULL;

	transform = kachujin->AddTransform();
	transform->Position(0, 0, -30);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayTweenMode(0, 0, 1.0f);

	transform = kachujin->AddTransform();
	transform->Position(-15, 0, -30);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayTweenMode(1, 1, 1.0f);

	transform = kachujin->AddTransform();
	transform->Position(-30, 0, -30);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayTweenMode(2, 2, 0.75f);

	transform = kachujin->AddTransform();
	transform->Position(15, 0, -30);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayBlendMode(3, 0, 1, 2);
	kachujin->SetBlendAlpha(3, 1.5f);

	transform = kachujin->AddTransform();
	transform->Position(30, 0, -32.5f);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayTweenMode(4, 4, 0.75f);

	kachujin->UpdateTransforms();
	// 오른손 쪽(40번 본)
	kachujin->SetAttachTransform(40);

	animators.push_back(kachujin);
}

void ObbRaycastDemo::KachujinCollider()
{
	// 인스턴싱 개수
	UINT count = kachujin->GetTransformCount();

	colliders = new ColliderObject*[count];

	for (UINT i = 0; i < count; i++)
	{
		colliders[i] = new ColliderObject();

		// 기준 위치
		colliders[i]->Init = new Transform();
		colliders[i]->Init->Position(0, 0, 0);
		colliders[i]->Init->Scale(10, 30, 10);

		colliders[i]->Transform = new Transform();
		colliders[i]->Collider = new Collider(colliders[i]->Transform, colliders[i]->Init);
	}

}

void ObbRaycastDemo::CheckIntersection()
{
	// 마우스가 눌렸는지 체크(눌렸을때만 검사)
	if (Mouse::Get()->Down(0) == false) return;

	// Unprojection 만드는 부분
	Matrix V = Context::Get()->View();
	Matrix P = Context::Get()->Projection();
	Viewport* Vp = Context::Get()->GetViewport();

	Vector3 mouse = Mouse::Get()->GetPosition();


	Matrix world;
	D3DXMatrixIdentity(&world);

	Vector3 n, f;
	mouse.z = 0.0f;
	Vp->Unproject(&n, mouse, world, V, P);

	mouse.z = 1.0f;
	Vp->Unproject(&f, mouse, world, V, P);

	Ray ray;
	ray.Position = n;
	ray.Direction = f - n;

	// 판단할것
	float distance = 0.0f;
	// 인스턴싱 개수만큼 돌림
	UINT count = kachujin->GetTransformCount();

	bool check = false;
	for (UINT i = 0; i < count; i++)
	{
		// 충돌이면 true 아니라면 충돌되지 않은 값 리턴
		if (colliders[i]->Collider->Intersection(ray, &distance))
		{
			// 충돌된 인덱스 저장
			collisionIndex = (int)i;
			check = true;

			break;
		}
	}

	// 체크가 안되었다면 없는 걸로 초기화 해줌
	if (check == false)
		collisionIndex = -1;
}

void ObbRaycastDemo::Pass(UINT mesh, UINT model, UINT anim)
{
	for (MeshRender* temp : meshes)
		temp->Pass(mesh);

	for (ModelRender* temp : models)
		temp->Pass(model);

	for (ModelAnimator* temp : animators)
		temp->Pass(anim);
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

결과


Unprojection으로 Ray를 만들고 Ray와 OBB를 교차판정을 적용했다. 충돌했다면 빨간색으로 바뀐다.