1. 기본도형
오늘은 3d 기본 도형에 대해 알아보고 구현해보자
알아볼 기본도형은 점, 선, 구등이 있다.
점은 말그대로 하나의 좌표이다.
선은 시작과 끝이 있는 선분이다.
레이는 시작점이 있으면서 한쪽 방향으로 쭉 뻗어나가는 광선이다.
구는 중심점과 반지름을 가진 도형으로 중심에서 반지름까지의 모든 점을 합쳐주면 하나의 구가 되는 것이다.
AABB는 x,y,z축이 정렬되어(평행한) 만들어진 Box모양이다.
OBB는 x,y,z축이 정렬되어 있지않은 채 만들어 Box모양이다.
Plane은 평면으로 만들 때 점3개를 이용하여 만들거나 노멀벡터와 한점이나 노멀벡터와 평면과 원점까지의 거리를 활용 하여 만들어줄 수 있다.
Triangle은 3개의 점으로 이루어진 도형으로 이때 Union이라는 것을 사용해볼 것인데 이는 자료형이 3개의 각기 다른 자료형으로 표현가능하다는 것이다.
Primitive3D.h
#pragma once
// *************
// Point3D
// *************
using Point3D = Vec3;
// *************
// Line3D
// *************
struct Line3D
{
Point3D start = Point3D(0.f);
Point3D end = Point3D(0.f);
float Length() { return Vec3::Distance(start, end); }
//제곱급
float LengthSq() { return Vec3::DistanceSquared(start, end); }
};
// *************
// Ray3D
// *************
struct Ray3D
{
Point3D origin = Point3D(0.f);
Vec3 direction = Vec3(0.f);
void NormalizeDirection() { direction.Normalize(); }
//Ray3D생성
static Ray3D FromPoints(const Point3D& from, const Point3D& to) { return Ray3D{ from, to - from }; }
};
// *************
// Sphere3D
// *************
struct Sphere3D
{
Point3D position;
float radius;
};
// *************
// AABB
// *************
struct AABB3D
{
Point3D position = Vec3(0.f);
Vec3 size = Vec3(1.f, 1.f, 1.f);
//최소점
static Vec3 GetMin(const AABB3D& aabb)
{
Vec3 p1 = aabb.position + aabb.size;
Vec3 p2 = aabb.position - aabb.size;
return Vec3(fminf(p1.x, p2.x), fminf(p1.y, p2.y), fminf(p1.z, p2.z));
}
//최대점
static Vec3 GetMax(const AABB3D& aabb)
{
Vec3 p1 = aabb.position + aabb.size;
Vec3 p2 = aabb.position - aabb.size;
return Vec3(fmaxf(p1.x, p2.x), fmaxf(p1.y, p2.y), fmaxf(p1.z, p2.z));
}
//최소 최대주면 AABB 생성해주는
static AABB3D FromMinMax(const Vec3& min, const Vec3& max)
{
return AABB3D((min + max) / 2, (max - min) / 2);
}
};
// *****************
// OBB
// *****************
struct OBB3D
{
Point3D position;
Vec3 size;
Matrix orientation;
// Vec4 quaternion;
// Vec3 rotation;
};
// *****************
// Plane3D
// *****************
// 삼각형 (정점3개)
// 노멀 + 정점 1개
// 노멀 + 거리
struct Plane3D
{
Vec3 normal;
float distance;
};
// *****************
// Triangle3D
// *****************
struct Triangle3D
{
//3개모두 사용할 수 있다.
union
{
struct
{
Point3D a;
Point3D b;
Point3D c;
};
Point3D points[3];
float values[9];
};
};
2.Point Test
이제 만약 어떤 점이 있다면 그것이 도형에 포함이 되어있는지 확인하는 이론에 대해 배워보고 코드로 구현해보자.
먼저 구에 대해 Point Test를 해보자. 중심점과의 거리를 통해 판별할 수 있다.
bool MathUtils::PointInSphere(const Point3D& point, const Sphere3D& sphere) {
float magSq = (point - sphere.position).LengthSquared();
float radSq = sphere.radius * sphere.radius;
return magSq <= radSq;
}
Point3D MathUtils::ClossetPoint(const Sphere3D& sphere, const Point3D& point) {
Vec3 sphereToPointDir = (point - sphere.position);
sphereToPointDir.Normalize();
return sphere.position + sphereToPointDir * sphere.radius;
}
AABB도 안에 포함되어있는지 표면에 있는거에서 가장 가까운 점이 어느것인지 찾아보자. 이때 위에서 만들어준 min과 max값을 활용하여 검사해줄 수 있다.
bool MathUtils::PointInAABB(const Point3D& point, const AABB3D& aabb) {
Point3D min = AABB3D::GetMin(aabb);
Point3D max = AABB3D::GetMax(aabb);
if (point.x < min.x || point.y < min.y || point.z < min.z)
return false;
if (point.x > max.x || point.y > max.y || point.z > max.z)
return false;
return true;
}
Point3D MathUtils::ClossetPoint(const AABB3D& aabb, const Point3D& point) {
Point3D result = point;
Point3D minPt = AABB3D::GetMin(aabb);
Point3D maxPt = AABB3D::GetMax(aabb);
result.x = max(result.x, minPt.x);
result.y = max(result.y, minPt.y);
result.z = max(result.z, minPt.z);
result.x = min(result.x, minPt.x);
result.y = min(result.y, minPt.y);
result.z = min(result.z, minPt.z);
return result;
}
OBB도 동일하게 검사해주는데 이때 AABB로 변환해준 다음에 테스트를 해줄 수 도 있고 아니면 매축에 프로젝션 연산을 해주면 된다.
bool MathUtils::PointInOBB(const Point3D& point, const OBB3D& obb) {
Vec3 dir = point - obb.position;
vector<Vec3> axis = { obb.orientation.Right(), obb.orientation.Up(), obb.orientation.Backward() };
vector<float> size = { obb.size.x, obb.size.y, obb.size.z };
for (int i = 0; i < 3; i++) {
float distance = dir.Dot(axis[i]);
if (distance > size[i] || distance < -size[i])
return false;
}
return true;
}
Point3D MathUtils::ClossetPoint(const OBB3D& obb, const Point3D& point) {
Vec3 dir = point - obb.position;
Point3D result;
vector<Vec3> axis = { obb.orientation.Right(), obb.orientation.Up(), obb.orientation.Backward() };
vector<float> size = { obb.size.x, obb.size.y, obb.size.z };
for (int i = 0; i < 3; i++) {
float distance = dir.Dot(axis[i]);
distance = clamp(distance, -size[i], size[i]);
result = result + (axis[i] * distance);
}
return obb.position + result;
}
Plane은 위에서 구해준 방법대로 점과 노멀벡터를 내적해주는 것으로 Distance를 구할 수 있는데 이 값을 검사해주면 된다.
bool MathUtils::PointInPlane(const Point3D& point, const Plane3D& plane) {
float dot = point.Dot(plane.normal);
return fabs(dot - plane.distance) < FLT_EPSILON;
}
Point3D MathUtils::ClossetPoint(const Plane3D& plane, const Point3D& point) {
float dot = point.Dot(plane.normal);
float distance = dot - plane.distance;
return point - plane.normal * distance;
}
Line은 내적을 통해서 구해줄텐데 내적했을 때 거리가 0인지 검사하는 것으로 내부에 있는지 파악할 수 있다. 가장 가까운 점은 내적을 통한 크기와 기존 크기를 통해 비율을 구해서 활용해주면 된다.
bool MathUtils::PointInLine(const Point3D& point, const Line3D& line) {
Point3D closest = ClossetPoint(line, point);
return (closest - point).LengthSquared() == 0.f;
}
Point3D MathUtils::ClossetPoint(const Line3D& line, const Point3D& point) {
Vec3 lineVec = line.end - line.start;
float t = (point - line.start).Dot(lineVec) / lineVec.Dot(lineVec);
t = clamp(t, 0.0f, 1.0f);
return line.start + lineVec * t;
}
Ray는 내적을 통해 구해줄텐데 수직을 내렸을 때의 좌표를 구해주면 되는 것이기 때문에 방향벡터와 정점의 좌표 - 시작점을 내적해주고 이를 방향벡터에 곱해주면 위치를 구해줄 수 있다. 그리고 내부에 있는지 확인하려면 시작좌표와 비교하고 아니라면 시작좌표와의 방향벡터와 기존 레이의 방향벡터가 내적했을 때 1이면 같은것이기 때문에 내부에 있는것으로 판단해주면 된다.
bool MathUtils::PointInRay(const Point3D& point, const Ray3D& ray) {
if (point == ray.origin)
return true;
Vec3 norm = point - ray.origin;
norm.Normalize();
return fabs(norm.Dot(ray.direction) - 1.0f) < FLT_EPSILON;
}
Point3D MathUtils::ClossetPoint(const Ray3D& ray, const Point3D& point) {
float t = (point - ray.origin).Dot(ray.direction);
t = fmaxf(t, 0.0f);
return ray.origin + ray.direction * t;
}
'게임공부 > Directx11' 카테고리의 다른 글
[Directx11][C++][3D]32. 수학3(Triangle) (4) | 2024.10.09 |
---|---|
[Directx11][C++][3D]31. 수학2(Intersection & RayCasting) (1) | 2024.10.09 |
[Directx11][C++][3D]29. Terrain Picking (0) | 2024.10.07 |
[Directx11][C++][3D]28. Collision(Box) & Picking (0) | 2024.10.06 |
[Directx11][C++][3D]27. Collision(Sphere) (3) | 2024.10.06 |