[DirectX11] 070 OBB(Oriented Bounding Box) collision
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
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=55smile&logNo=221671747433
https://sanghoon23.tistory.com/m/82
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);
}
실행화면
'⭐ DirectX > DirectX11 3D' 카테고리의 다른 글
[DirectX11] 074 Instancing (0) | 2023.03.18 |
---|---|
[DirectX11] 071~73 Get MultiBones (0) | 2023.03.15 |
[DirectX11] 069 Collider (0) | 2023.03.13 |
[DirectX11] 068 Unprojection - 2D 좌표를 3D 좌표로 변환 (0) | 2023.03.13 |
[DirectX11] 067 Projection (0) | 2023.03.09 |
댓글
이 글 공유하기
다른 글
-
[DirectX11] 074 Instancing
[DirectX11] 074 Instancing
2023.03.18 -
[DirectX11] 071~73 Get MultiBones
[DirectX11] 071~73 Get MultiBones
2023.03.15 -
[DirectX11] 069 Collider
[DirectX11] 069 Collider
2023.03.13 -
[DirectX11] 068 Unprojection - 2D 좌표를 3D 좌표로 변환
[DirectX11] 068 Unprojection - 2D 좌표를 3D 좌표로 변환
2023.03.13