필요한 개념
충돌체 끼리 충돌 검사(분리축을 이용함)
분리축(SAT) 설명
분리축(SAT)이용한 OBB간의 충돌
우리는 게임을 만들때 일반적으로 충돌박스라는것을 만들어서 체크를 한다. 이는 구가될수도있고, 면이될수도, 직선이될수도, 삼각형, 각각의 다각형이 존재할수있다. 가장 보편적으로 나온
cpplab.tistory.com
분리축 : 128개가 나오는데, 중복제거시 15개가 나온다.
15개의 분리축 공간을 검사하고 false가 하나도 없다면 충돌로 간주
무기에 충돌체 붙여서 충돌하기도 가능하다.(칼 같은 것도 회전이 일어나서 OBB Collision로 해야한다.)
요약
분리 축을 이용하여 충돌체크를 수행
분리 축의 15개 케이스를 모두 통과시 충돌상태임
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 추가된 내용
더보기
void Collider::Update()
{
// 매 프레임마다 바운딩 정보 생성
Transform temp;
temp.World(transform->World());
if (init != NULL)
temp.World(init->World() * transform->World());
// 바운딩 위치가 이동된 world의 위치
temp.Position(&bounding.Position);
// 공간의 방향을 축에다 준다.
D3DXVec3Normalize(&bounding.AxisX, &temp.Right());
D3DXVec3Normalize(&bounding.AxisY, &temp.Up());
D3DXVec3Normalize(&bounding.AxisZ, &temp.Forward());
Vector3 scale;
temp.Scale(&scale);
// 충돌은 전체 크기가 아니라 크기의 반을 사용
bounding.HalfSize = scale * 0.5;
}
bool Collider::Intersection(Collider* other)
{
// 자신의 바운딩 정보와 체크하려는 충돌체의 바운딩 정보로 검사를 수행
return Collision(this->bounding, other->bounding);
}
bool Collider::SeperatingPlane(Vector3& position, Vector3& direction, Bounding& box1, Bounding& box2)
{
// OBB의 중심Position, 밑에 Separation axis(분리축)을 내적하여, OBB A와 OBB B의 사이 거리인 Projection을 구한다.
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));
// 평면방정식의 이론과 비슷하다고 생각하고 이해해도됨
// val은 사이 거리이기 때문에 val2가 이 보다 거리가 길다면 충돌이 안되었다는 것이다.
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);
}
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]; // 충돌체의 기준 위치
Collider* collider[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();
// 충돌체 생성
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::Destroy()
{
SafeDelete(shader);
SafeDelete(sky);
SafeDelete(grid);
SafeDelete(floor);
}
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 = bCheck ? Color(1, 0, 0, 1) : Color(0, 1, 0, 1);
collider[0]->Render(color);
collider[1]->Render(color);
}
결과
'DirectX11 3D > 기본 문법' 카테고리의 다른 글
<DirectX11 3D> 74 - Instancing (0) | 2022.03.09 |
---|---|
<DirectX11 3D> 71 - GetMultiBones (0) | 2022.03.09 |
<DirectX11 3D> 69 - OBB Raycast(OBB와 반직선 교차) (0) | 2022.03.02 |
<DirectX11 3D> 68 - Unprojection (0) | 2022.03.02 |
<DirectX11 3D> 67 - Projection (0) | 2022.03.01 |