DirectX11에서 OBB (Oriented Bounding Box) 충돌 검사는 다른 물체와의 충돌을 감지하는데 사용되는 방법 중 하나이다. 이를 위해 OBB의 충돌 분리 축(the separating axis) 이론이 사용된다. OBB는 충돌 검사를 수행하기 위해 일반적으로 모델의 경계 상자로 사용된다. 이 상자는 일반적으로 물체의 모양에 맞게 회전할 수 있다. 이러한 OBB의 충돌 검사는 OBB의 축을 기준으로 수행된다.

 

목차

     

     


     

     

    OBB(Oriented Bounding Box) collision

     

    Shader
       
    Framework
      Objects
      Collider.h .cpp
    UnitTest
      Objects
      ObbCollisionDemo.h .cpp 생성
      Main.h .cpp

     

     


     

    OBB 충돌 - 분리축 이론

     

    OBB는 충돌 검사를 수행하기 위해 일반적으로 모델의 경계 상자로 사용된다. 이 상자는 일반적으로 물체의 모양에 맞게 회전할 수 있다. 이러한 OBB의 충돌 검사는 OBB의 축을 기준으로 수행된다.

    분리 축 이론은 두 OBB가 충돌하는지 여부를 결정하기 위해 두 OBB 사이의 축을 비교한다. 두 축이 서로 분리되어 있다면, 두 OBB는 충돌하지 않는다. 즉, 최소한의 분리 축을 찾는 것이 중요하다.

    이를 수행하기 위해, 두 OBB의 모든 축(총 15개)을 비교한다. 각각의 축에 대해 두 OBB의 투영(projection)을 계산하고, 이러한 투영이 서로 겹치는지를 확인한다. 만약 모든 축에서 투영이 겹치면, 두 OBB는 충돌한다. 하지만 하나의 축에서라도 겹치지 않는다면, 두 OBB는 충돌하지 않는다.

    분리 축 이론은 물체의 복잡한 형태를 갖는 경우에도 적용할 수 있으며, 속도와 정확도 모두가 뛰어나다는 장점이 있다.

     

     

    https://m.blog.naver.com/kys9096/221200798891

     

    OBB 충돌 체크 - part 1 (분리축 이론과 벡터의 내적)

    본격적인 알고리즘 구현 이전에, 앞서 언급했던 분리축 이론과 벡터의 내적을 한번 짚고 넘어가보자. 분리...

    blog.naver.com

    https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=55smile&logNo=221671747433 

     

    분리축이론(SAT)과 OBB 충돌

    우리는 게임을 만들때 일반적으로 충돌박스라는것을 만들어서 체크를 한다. 이는 구가될수도있고, 면이될수...

    blog.naver.com

    https://sanghoon23.tistory.com/m/82

     

    [DX] ##6. AABB, OBB 충돌 SAT 분리축 이론( Separating Axis Theorem )

    Frustum Culling ( 절두체 선별 ) 에 앞서서 해당 범위에 오브젝트들이 포함되어있는지 어떻게 판별할까 ?? 그 방법에 대해서 몇 가지 알아보고 넘어가자. *AABB ( Axis - Aligned Bounding Box ) => 축 정렬 경계

    sanghoon23.tistory.com

     


     

    Collider

     

    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; }
    
    	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);
    
    	//외적
    	Vector3 Cross(Vector3& vec1, Vector3& vec2);
    
    private:
    	//충돌검사를 할 바운딩박스
    	struct Bounding
    	{
    		Vector3 Position;
    
    		Vector3 AxisX;
    		Vector3 AxisY;
    		Vector3 AxisZ;
    
    		Vector3 HalfSize; //반사이즈
    	} bounding;
    
    private:
    	Transform* init = NULL;
    	Transform* transform;
    
    	Vector3 lines[8];
    };

     

     

    Collider.cpp

    더보기
    #include "Framework.h"
    #include "Collider.h"
    
    Collider::Collider(Transform * transform, Transform * init)
    	: transform(transform), init(init)
    {
    	lines[0] = Vector3(-0.5f, -0.5f, -0.5f); //Min
    	lines[1] = Vector3(-0.5f, +0.5f, -0.5f);
    	lines[2] = Vector3(+0.5f, -0.5f, -0.5f);
    	lines[3] = Vector3(+0.5f, +0.5f, -0.5f);
    	lines[4] = Vector3(-0.5f, -0.5f, +0.5f);
    	lines[5] = Vector3(-0.5f, +0.5f, +0.5f);
    	lines[6] = Vector3(+0.5f, -0.5f, +0.5f);
    	lines[7] = Vector3(+0.5f, +0.5f, +0.5f); //Max
    }
    
    Collider::~Collider()
    {
    
    }
    
    void Collider::Update()
    {
    	//매 프레임마다 바운딩 생성
    	Transform temp;
    	temp.World(transform->World());
    
    	if (init != NULL)
    		temp.World(init->World() * transform->World()); //초기값에서부터 얼마만큼 이동했는지
    
    	
    	temp.Position(&bounding.Position); //이동된 위치
    
    	D3DXVec3Normalize(&bounding.AxisX, &temp.Right());//바운딩박스의 X축 오른쪽 방향
    	D3DXVec3Normalize(&bounding.AxisY, &temp.Up()); //바운딩박스의 Y축 윗방향
    	D3DXVec3Normalize(&bounding.AxisZ, &temp.Forward());//바운딩박스의 Z축은 전방 방향
    
    	Vector3 scale;
    	temp.Scale(&scale);
    	bounding.HalfSize = scale * 0.5f; 
    }
    
    void Collider::Render(Color color)
    {
    	Transform temp;
    	temp.World(transform->World());
    
    	if (init != NULL)
    		temp.World(init->World() * transform->World());
    
    	Matrix world = temp.World();
    
    
    	Vector3 dest[8];
    	for (UINT i = 0; i < 8; i++)
    		D3DXVec3TransformCoord(&dest[i], &lines[i], &world);
    
    	//Front
    	DebugLine::Get()->RenderLine(dest[0], dest[1], color);
    	DebugLine::Get()->RenderLine(dest[1], dest[3], color);
    	DebugLine::Get()->RenderLine(dest[3], dest[2], color);
    	DebugLine::Get()->RenderLine(dest[2], dest[0], color);
    
    	//Backward
    	DebugLine::Get()->RenderLine(dest[4], dest[5], color);
    	DebugLine::Get()->RenderLine(dest[5], dest[7], color);
    	DebugLine::Get()->RenderLine(dest[7], dest[6], color);
    	DebugLine::Get()->RenderLine(dest[6], dest[4], color);
    
    	//Side
    	DebugLine::Get()->RenderLine(dest[0], dest[4], color);
    	DebugLine::Get()->RenderLine(dest[1], dest[5], color);
    	DebugLine::Get()->RenderLine(dest[2], dest[6], color);
    	DebugLine::Get()->RenderLine(dest[3], dest[7], color);
    }
    
    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();
    
    	Vector3 minPosition, maxPosition;
    	D3DXVec3TransformCoord(&minPosition, &lines[0], &world);
    	D3DXVec3TransformCoord(&maxPosition, &lines[7], &world);
    
    	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;
    
    	//Check X
    	if (fabsf(ray.Direction.x) >= 1e-6f)
    	{
    		float value = 1.0f / ray.Direction.x;
    		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);
    
    		if (minValue > maxValue)
    			return false;
    	}
    	else if (ray.Position.x < minPosition.x || ray.Position.x > maxPosition.x)
    		return false;
    
    	//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;
    
    	*outDistance = minValue;
    	return true;
    }
    
    bool Collider::Intersection(Collider * other)
    {
    	//현재 가지고 있는 bounding 정보와 다른객체(other)이 가지고 있는 bounding 정보가 같은지 검사.
    	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);
    }

     

     


     

    Obb Collision Demo

     

    OBB Collsion을 화면에 그려주기 위해 collider 2개를 생성한다. 

     

     

    ObbCollisionDemo.h

    더보기
    #pragma once
    #include "Systems/IExecute.h"
    
    class ObbCollisionDemo : 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:
    	Shader* shader;
    
    	CubeSky* sky;
    
    	Material* floor;
    	MeshRender* grid;
    
    	Transform* transform[2]; //충돌체의 위치를 정의할 transform 2개
    	Collider* collider[2];  //충돌체 2개
    };

    Transform* transform[2]; 

    • 충돌체의 위치를 정의할 transform 2개

    Collider* collider[2];

    • 충돌체 2개

     

     

    ObbCollisionDemo.cpp

    더보기
    #include "stdafx.h"
    #include "ObbCollisionDemo.h"
    
    void ObbCollisionDemo::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");
    
    	floor = new Material(shader);
    	floor->DiffuseMap("White.png");
    	
    	grid = new MeshRender(shader, new MeshGrid(5, 5));
    	grid->AddTransform();
    	
    	//collider 2개 생성 후 위치 및 스케일 정의.
    	transform[0] = new Transform();
    	transform[0]->Position(0.0f, 0.5f, 0.0f);
    	transform[0]->Scale(2, 1, 1);
    	collider[0] = new Collider(transform[0]);
    
    	transform[1] = new Transform();
    	transform[1]->Position(2.0f, 0.5f, 0.0f);	
    	collider[1] = new Collider(transform[1]);
    }
    
    void ObbCollisionDemo::Update()
    {
    	sky->Update();
    	grid->Update();
    
    	Vector3 position2;
    	transform[1]->Position(&position2);
    
    	if (Keyboard::Get()->Press(VK_RIGHT))
    		position2.x += 3.0f * Time::Delta();
    	else if (Keyboard::Get()->Press(VK_LEFT))
    		position2.x -= 3.0f * Time::Delta();
    
    	if (Keyboard::Get()->Press(VK_UP))
    		position2.z += 3.0f * Time::Delta();
    	else if (Keyboard::Get()->Press(VK_DOWN))
    		position2.z -= 3.0f * Time::Delta();
    
    	transform[1]->Position(position2);
    
    
    	Vector3 rotation = Vector3(0, 0, 0);
    	Vector3 rotation2 = Vector3(0, 0, 0);
    
    	transform[0]->RotationDegree(&rotation);
    	transform[1]->RotationDegree(&rotation2);
    
    	ImGui::SliderFloat3("Rotation", (float *)rotation, -179, 179);
    	ImGui::SliderFloat3("Rotation2", (float *)rotation2, -179, 179);
    
    	transform[0]->RotationDegree(rotation);
    	transform[1]->RotationDegree(rotation2);
    
    
    	collider[0]->Update();
    	collider[1]->Update();
    }
    
    void ObbCollisionDemo::Render()
    {
    	sky->Render();
    
    	floor->Render();
    	grid->Render();
    
    
    	bool bCheck = collider[0]->Intersection(collider[1]);
    
    	Color color;
    	//충돌하면 Red, 충돌하지 않으면 Green
    	color = bCheck ? Color(1, 0, 0, 1) : Color(0, 1, 0, 1);
    
    	collider[0]->Render(color);
    	collider[1]->Render(color);
    }

     

     


     

    실행화면