[D3D] 수학 - 도형, Point Test, Intersection, Raycasting, Triangle

vector·2025년 12월 9일

도형

3D 기본적인 도형에 대해서 알아보자
우선 이번에 확인해볼 기본도형은 Point, Line, Ray, Sphere, AABB(Axis Aligned Bounding Box, OBB(Oriented Bounding Box), Plane, Triangle이다.

Point

말 그대로 하나의 좌표

// **************
// Point3D
// **************

using Point3D = Vec3;

Line

시작과 끝이 있는 선분

// **************
// 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); }
};

Ray

시작점과 방향이 있으며, 방향으로 무한으로 뻗어나가는 광선

// **************
// Ray3D
// **************

struct Ray3D
{
	Point3D origin = Point3D(0.f);
	Vec3 direction = Vec3(0.f);

	void NormalizeDirection() { direction.Normalize(); }

	static Ray3D FromPoints(const Point3D& from, const Point3D to) { return Ray3D{ from, to - from }; }
};

Sphere

중점(Position)과 반지름(Radius)가 있고, 중점에서 반지름까지의 모든 점을 합쳐진 도형

// **************
// Sphere3D
// **************

struct Sphere3D
{
	Point3D position;
	float radius;
};

AABB

x축, y축, z축이 정렬되어 있는 박스모양

// **************
// 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 GetMin(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

x축, y축, z축이 정렬되지 않은 도형 (AABB가 회전된 상태의 도형)

// **************
// OBB
// **************

struct OBB3D
{
	Point3D position;
	Vec3 size;

	Matrix orientation;	// 회전
	// Vec4 quaternion;
	// Vec3 rotation;
};

Plane

  • 평면
  • 점 3개(삼각형)를 이용해서 만들거나 점 하나와 노멀 벡터 / 노멀벡터와 원점에서 평면까지의 거리 중 하나를 활용해서 평면을 만들 수 있다.
// **************
// Plane3D
// **************

// 삼각형 (정점 3개)
// 노멀 + 정점 1개
// 노멀 + 거리
struct Plane3D
{
	Vec3 normal;
	float distance;

	// 헬퍼함수는 나중에 추가
};

Triangle

3개의 점으로 이루어진 도형

// **************
// Triangle3D
// **************

struct Triangle3D
{
	// 아래 중 아무거나 골라도 된다
	union
	{
		struct
		{
			Point3D a;
			Point3D b;
			Point3D c;
		};

		Point3D points[3];
		float values[9];
	};
};

PointTest

  • 어떤 점이 도형에 포함되어 있는지 확인하는 방법에 대해서

Point contained in sphere

중점과의 거리를 통해서 판별 가능

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::ClosestPoint(const Sphere3D& sphere, const Point3D& point)
{
    Vec3 sphereToPointDir = (point - sphere.position);
    sphereToPointDir.Normalize();


    return sphere.position + sphereToPointDir * sphere.radius;
}

Point Contained in AABB

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::ClosestPoint(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, maxPt.x);
    result.y = min(result.y, maxPt.y);
    result.z = min(result.z, maxPt.z);

    return result;
}

Point Contained in OBB

OBB에서의 방법은 첫번째로 OBB를 AABB형식으로 변형해서 계산해주는 방식이고,
두번째는 매 축마다 Projection 연산하고, 비교를 통해서 속해있는지를 판별하는 방식

bool MathUtils::PointInOBB(const Point3D& point, const OBB3D& obb)
{
    Vec3 dir = point - obb.position;

    Point3D result;
    vector<Vec3> axis;
    axis.push_back(obb.orientation.Right());
    axis.push_back(obb.orientation.Up());
    axis.push_back(obb.orientation.Backward());

    vector<float> size;
    size.push_back(obb.size.x);
    size.push_back(obb.size.y);
    size.push_back(obb.size.z);

    for (int i = 0; i < 3; i++)
    {
        // 내적을 통해서 거리 구하기
        float distance = dir.Dot(axis[i]);

        if (distance > size[i])
            return false;
        if (distance < size[i])
            return false;
    }

    return true;
}

Point3D MathUtils::ClosestPoint(const OBB3D& obb, const Point3D& point)
{
    Point3D result = obb.position;
    Vec3 dir = point - obb.position;

    vector<Vec3> axis;
    axis.push_back(obb.orientation.Right());
    axis.push_back(obb.orientation.Up());
    axis.push_back(obb.orientation.Backward());

    vector<float> size;
    size.push_back(obb.size.x);
    size.push_back(obb.size.y);
    size.push_back(obb.size.z);

    for (int i = 0; i < 3; i++)
    {
        float distance = dir.Dot(axis[i]);

        if (distance > size[i])
            distance = size[i];

        if (distance < -size[i])
            distance = -size[i];

        result += axis[i] * distance;
    }

    return result;
}

Point on surface of plane

점과 노멀 벡터를 내적해주는 것으로 거리를 구해서 판별 가능

bool MathUtils::PointInPlane(const Point3D& point, const Plane3D& plane)
{
    float dot = point.Dot(plane.normal);

    return dot == plane.distance;
}

Point3D MathUtils::ClosestPoint(const Plane3D& plane, const Point3D& point)
{
    float dot = point.Dot(plane.normal);
    float distance = dot - plane.distance;

    return point - plane.normal * distance;
}

Point on line segment

  • 내적 사용하며, 내적했을 때 거리가 0인지 검사하는 것으로 판별 가능
bool MathUtils::PointInLine(const Point3D& point, const Line3D& line)
{
    Point3D closest = ClosestPoint(line, point);

    float distanceSq = (closest - point).LengthSquared();

    return distanceSq == 0.f; // 소수점이기에 원래는 불가능이지만 이해를 위해서
}
Point3D MathUtils::ClosestPoint(const Line3D& line, const Point3D& point)
{
    //  C
    //
    //  D   A------B 
    // line.start = A / line.end = B / point = c
    // lVec = A->B 벡터
    // (point - line.start) = A->C 벡터
    // (point - line.start).Dot(lVec) = |AB|*|AD|
    // lVec.Dot(lVec) = |AB|*|AB|
    // t = |AD| / |AB| -> 0 ~ 1 사이의 값이면 선 안에 있다

    Vec3 lVec = line.end - line.start; // Line Vector
    float t = (point - line.start).Dot(lVec) / lVec.Dot(lVec);
    t = fmaxf(t, 0.0f); // Clamp to 0
    t = fminf(t, 1.0f); // Clamp to 1
    return line.start + lVec * t;
}

Point on ray

  • 내적 사용하며, 방향벡터와 정점의 좌표 - 시작점을 내적해주고, 이를 방향벡터에 곱해주는 방식으로 판별 가능
bool MathUtils::PointInRay(const Point3D& point, const Ray3D& ray)
{
    if (point == ray.origin)
        return true;

    Vec3 norm = point - ray.origin;
    norm.Normalize();

    float diff = norm.Dot(ray.direction);
    return diff == 1.0f;
}
Point3D MathUtils::ClosestPoint(const Ray3D& ray, const Point3D& point)
{
    float t = (point - ray.origin).Dot(ray.direction);

    t = fmaxf(t, 0.0f);

    return Point3D(ray.origin + ray.direction * t);
}

Intersection

물체끼리의 충돌했을 때 상호작용을 해주기 위한 탐지

Sphere to Sphere


두 원점간의 거리와 반지름의 합을 비교해서 충돌 탐지

bool MathUtils::SphereSphere(const Sphere3D& s1, const Sphere3D& s2)
{
    float sum = s1.radius + s2.radius;

    // 제곱으로 비교
    float sqDistance = (s1.position - s2.position).LengthSquared();

    return sqDistance <= sum * sum;
}

Sphere to AABB


원과 AABB의 최근접점까지의 거리와, 반지름을 비교해서 충돌 판별

bool MathUtils::SphereAABB(const Sphere3D& sphere, const AABB3D& aabb)
{
    Point3D closestPoint = ClosestPoint(aabb, sphere.position);
    float distSq = (sphere.position - closestPoint).LengthSquared();
    float radiusSq = sphere.radius * sphere.radius;

    return distSq < radiusSq;
}

Sphere to OBB


AABB와 동일하게 최근접점(Closest)과의 거리와 반지름을 비교해서 충돌 판별

bool MathUtils::SphereOBB(const Sphere3D& sphere, const OBB3D& obb)
{
    Point3D closestPoint = ClosestPoint(obb, sphere.position);
    float distSq = (sphere.position - closestPoint).LengthSquared();
    float radiusSq = sphere.radius * sphere.radius;

    return distSq < radiusSq;
}

Sphere to Plane

bool MathUtils::SpherePlane(const Sphere3D& sphere, const Plane3D& plane)
{
    Point3D closestPoint = ClosestPoint(plane, sphere.position);
    float distSq = (sphere.position - closestPoint).LengthSquared();
    float radiusSq = sphere.radius * sphere.radius;

    return distSq < radiusSq;
}

AABB to AABB


두 Box의 min과 max를 통해 비교해서 판별 가능

bool MathUtils::AABBToAABB(const AABB3D& aabb1, const AABB3D& aabb2)
{
    Point3D aMin = AABB3D::GetMin(aabb1);   // aabb1의 최소 좌표
    Point3D aMax = AABB3D::GetMax(aabb1);   // aabb1의 최대 좌표
    Point3D bMin = AABB3D::GetMin(aabb2);   // aabb2의 최소 좌표
    Point3D bMax = AABB3D::GetMax(aabb1);   // aabb2의 최대 좌표


    // 모든 축에 대해서 겹치면 true
    return (aMin.x <= bMax.x && aMax.x >= bMin.x) &&
            (aMin.y <= bMax.y && aMax.y >= bMin.y) &&
            (aMin.z <= bMax.z && aMax.z >= bMin.z);
}

AABB to OBB

  • 기준이 되는 축을 정하고 SAT알고리즘(Separation Axis Theorem)을 사용해서 모든 선분에 했을 때, 모든 축에서 겹치는 부분이 있는지 없는지를 통해서 충돌 판별
  • 계산해야하는 축은 AABB축, OBB축, 축들의 외적들
Interval3D MathUtils::GetInterval(const AABB3D& aabb, const Vec3& axis)
{
    Vec3 i = AABB3D::GetMin(aabb); // 최소 좌표
    Vec3 a = AABB3D::GetMax(aabb); // 최대 좌표

    Vec3 vertex[8] =
    {
        Vec3(i.x, a.y, a.z),
        Vec3(i.x, a.y, i.z),
        Vec3(i.x, i.y, a.z),
        Vec3(i.x, i.y, i.z),
        Vec3(a.x, a.y, a.z),
        Vec3(a.x, a.y, i.z),
        Vec3(a.x, i.y, a.z),
        Vec3(a.x, i.y, i.z)
    };

    // 최소, 최대 구하기
    Interval3D result;
    result.min = result.max = axis.Dot(vertex[0]); // 첫꼭짓점으로 초기화

    for (int i = 1; i < 8; i++)
    {
        float projection = axis.Dot(vertex[i]);
        result.min = min(result.min, projection);
        result.max = max(result.max, projection);
    }

    return result;
}
Interval3D MathUtils::GetInterval(const OBB3D& obb, const Vec3& axis)
{
    Vec3 vertex[8]; // OBB 꼭짓점을 저장할 배열

    Vec3 C = obb.position;  // OBB Center
    Vec3 E = obb.size;      // OBB extents

    vector<Vec3> A;         // OBB Axis
    A.push_back(obb.orientation.Right());
    A.push_back(obb.orientation.Up());
    A.push_back(obb.orientation.Backward());

    // 각 좌표를 구해주기
    vertex[0] = C + A[0] * E.x + A[1] * E.y + A[2] * E.z;
    vertex[1] = C - A[0] * E.x + A[1] * E.y + A[2] * E.z;
    vertex[2] = C + A[0] * E.x - A[1] * E.y + A[2] * E.z;
    vertex[3] = C + A[0] * E.x + A[1] * E.y - A[2] * E.z;
    vertex[4] = C - A[0] * E.x - A[1] * E.y - A[2] * E.z;
    vertex[5] = C + A[0] * E.x - A[1] * E.y - A[2] * E.z;
    vertex[6] = C - A[0] * E.x + A[1] * E.y - A[2] * E.z;
    vertex[7] = C - A[0] * E.x - A[1] * E.y + A[2] * E.z;

    // 최소/ 최대 구하기
    Interval3D result;
    result.min = result.max = axis.Dot(vertex[0]);

    for (int i = 1; i < 8; i++)
    {
        float projection = axis.Dot(vertex[i]);
        result.min = min(result.min, projection);
        result.max = max(result.max, projection);
    }

    return result;
}
bool MathUtils::OverlapOnAxis(const AABB3D& aabb, const OBB3D& obb, const Vec3& axis)
{
    Interval3D a = GetInterval(aabb, axis);         // AABB 구간 계산
    Interval3D b = GetInterval(obb, axis);          // OBB 구간 계산
    return ((b.min <= a.max) && (a.min <= b.max));  // 구간이 겹칠 경우 true반환
}
bool MathUtils::AABBToOBB(const AABB3D& aabb, const OBB3D& obb)
{
    Vec3 test[15] =
    {
        Vec3(1, 0, 0),                  // AABB axis 1
        Vec3(0, 1, 0),                  // AABB axis 2
        Vec3(0, 0, 1),                  // AABB axis 3
        obb.orientation.Right(),        // OBB  axis 1
        obb.orientation.Up(),           // OBB  axis 2
        obb.orientation.Backward(),     // OBB  axis 3
        // 외적으로 생성된 추가 축
    };

    // 추가 축 계산 (AABB축과 OBB축의 외적)
    for (int i = 0; i < 3; ++i)
    {
        test[6 + i * 3 + 0] = test[i].Cross(test[3]);
        test[6 + i * 3 + 1] = test[i].Cross(test[4]);
        test[6 + i * 3 + 2] = test[i].Cross(test[5]);
    }

    // 모든 축에 대해서 겹치는지 검사
    for (int i = 0; i < 15; ++i)
    {
        if (OverlapOnAxis(aabb, obb, test[i]) == false)
            return false;   // 하나라도 겹치지 않으면 false 반환
    }

    return true; // 모두 겹쳐야 true 반환
}

AABB to Plane

  • AABB의 8개의 점이 평면 위인지 아래인지 같은 방향에 있는지를 판별해주는게 가장 편한 방법이지만 코드상으로는 거리 계산을 통해서 충돌을 판별한다.
bool MathUtils::AABBToPlane(const AABB3D& aabb, const Plane3D& plane)
{
    float pLen = aabb.size.x * fabsf(plane.normal.x) +
        aabb.size.y * fabsf(plane.normal.y) +
        aabb.size.z * fabsf(plane.normal.z);

    float dot = plane.normal.Dot(aabb.position);    // 평면의 법선과 AABB 중심의 내적
    float dist = dot - plane.distance;              // 평면 상수와의 차이 계산

    return fabsf(dist) <= pLen;
}

OBB to OBB

  • AABB to OBB와 비슷하게 축을 정하고 모든 선분을 검사해주는 방식으로 충돌 판별 가능
bool MathUtils::OverlapOnAxis(const OBB3D& obb1, const OBB3D& obb2, const Vec3& axis)
{
    Interval3D a = GetInterval(obb1, axis);         // obb2 구간 계산
    Interval3D b = GetInterval(obb2, axis);         // obb1 구간 계산
    return ((b.min <= a.max) && (a.min <= b.max));  // 구간이 겹칠 경우 true반환
}
bool MathUtils::OBBToOBB(const OBB3D& obb1, const OBB3D& obb2)
{
    Vec3 test[15] =
    {
        obb1.orientation.Right(),
        obb1.orientation.Up(),
        obb1.orientation.Backward(),
        obb2.orientation.Right(),
        obb2.orientation.Up(),
        obb2.orientation.Backward(),
        // 외적 축
    };

    // 추가 축 계산 (AABB축과 OBB축의 외적)
    for (int i = 0; i < 3; ++i)
    {
        test[6 + i * 3 + 0] = test[i].Cross(test[3]);
        test[6 + i * 3 + 1] = test[i].Cross(test[4]);
        test[6 + i * 3 + 2] = test[i].Cross(test[5]);
    }

    // 모든 축에 대해서 겹치는지 검사
    for (int i = 0; i < 15; ++i)
    {
        if (OverlapOnAxis(obb1, obb2, test[i]) == false)
            return false;   // 하나라도 겹치지 않으면 false 반환
    }

    return true; // 모두 겹쳐야 true 반환
}

OBB to Plane

  • AABB to Plane과 동일
bool OBBToPlane(const OBB3D& obb, const Plane3D& plane)
{
    float pLen = obb.size.x * fabsf(plane.normal.x) +
        obb.size.y * fabsf(plane.normal.y) +
        obb.size.z * fabsf(plane.normal.z);

    float dot = plane.normal.Dot(obb.position);    // 평면의 법선과 AABB 중심의 내적
    float dist = dot - plane.distance;              // 평면 상수와의 차이 계산

    return fabsf(dist) <= pLen;
}

Plane to Plane

각 평면의 법선 벡터를 외적해서 평행한지 검사해서 충돌 판별 가능

bool MathUtils::PlaneToPlane(const Plane3D& plane1, const Plane3D& plane2)
{
    Vec3 d = plane1.normal.Cross(plane2.normal); // 두 평면의 법선 벡터의 외적

    return d.Dot(d) != 0;       // 외적의 결과가 0이 아니라면 두 평변은 평행하지 않은 것으로 간주
}

Raycasting

Ray와 도형의 충돌

Raycast Sphere

  • 예시(레이가 원 밖에 시작점이 있고, 원과 충돌했을 때의 사진)
  • 예시(레이가 원 밖에 시작점이 있고, 원과 충돌하지 않았을 때의 사진)
    - r의 제곱 - b의 제곱 < 0
  • 예시(레이가 원 안에 있을 때의 사진)
    - e의 제곱 < r의 제곱
bool MathUtils::RayCast(const Sphere3D& sphere, const Ray3D& ray, OUT float& distance)
{
    Vec3 e = sphere.position - ray.origin;          // 레이의 시작점에서 구의 중심까지의 벡터

    float rSq = sphere.radius * sphere.radius;      // 구의 반지름 제곱값
    float eSq = e.LengthSquared();                  // e 벡터 길이의 제곱값

    float a = e.Dot(ray.direction);                 // 레이 방향과 e 벡터의 내적

    float bSq = eSq - (a * a);                      // b의 제곱 (삼각형의 한 변의 제곱)
    float f = sqrt(rSq - bSq);                      // f는 삼각형의 다른 변 (구의 반지금에서 b를 뺀 값)


    // 충돌이 발생하지 않는 경우 -> 내적값으로 구한 반지름이랑 차이가 난다면
    if ((rSq - bSq) < 0.0f)
        return false;

    // 레이의 시작점이 구 내부에 있을 경우
    if (eSq < rSq)
    {
        distance = a + f;
        return true;
    }

    // 구 외부에서 시작하여 구에 닿지 않는 경우
    distance = a - f;       // 구에서 가장 가까운 거리
    return false;
}

Raycast AABB

  • 레이가 AABB와 겹치지 않을때
  • 레이가 AABB와 겹칠 때

    Cyrus-Beck 클리핑 알고리즘을 변형한 형태로 수행되고, tmin과 tmax를 통해서 충돌이 발생하는지를 판별
bool MathUtils::RayCast(const AABB3D& aabb, const Ray3D& ray, OUT float& distance)
{
    // Cyrus-Beck Clipping
    // AABB를 구성하는 6개의 평면에 대해 클리핑 진행
    // Point3D point = ray.origin +ray.direction * t;

    Vec3 min = AABB3D::GetMin(aabb);
    Vec3 max = AABB3D::GetMax(aabb);

    // 각 축에 대해서 레이가 AABB의 두 평면(최소값, 최대값)과 만나는 t값 계산
    float t1 = (min.x - ray.origin.x) / ray.direction.x;
    float t2 = (max.x - ray.origin.x) / ray.direction.x;

    float t3 = (min.y - ray.origin.y) / ray.direction.y;
    float t4 = (max.y - ray.origin.y) / ray.direction.y;

    float t5 = (min.z - ray.origin.z) / ray.direction.z;
    float t6 = (max.z - ray.origin.z) / ray.direction.z;


    // 가장 큰 최소 t값(tmin)과 가장 작은 최대 t값(tmax)을 계산
    float tmin = fmaxf(fmaxf(fminf(t1, t2), fminf(t3, t4)), fminf(t5, t6));
    float tmax = fminf(fminf(fmaxf(t1, t2), fmaxf(t3, t4)), fmaxf(t5, t6));

    // tmax가 0보다 작으면 레이가 AABB의 뒤쪽을 향함
    if (tmax < 0)
        return false;

    // tmin이 tmax보다 크면 레이는 AABB를 교차하지 않음
    if (tmin > tmax)
        return false;

    // 실제 충돌 거리 계산
    distance = (tmin < 0.0f) ? tmax : tmin;
    return true;
}

Raycast Plane

  • 레이가 평행한지 검사하고, 아니라면 교점을 찾아내는 과정을 통해서 판별
bool MathUtils::RayCast(const Plane3D& plane, const Ray3D& ray, OUT float& distance)
{
    float nd = ray.direction.Dot(plane.normal); // 레이 방향과 평면의 법선의 내적
    float pn = ray.origin.Dot(plane.normal);    // 레이 시작점과 평면의 법선 내적

    // nd가 0보다 크거나 같으면 레이와 평면은 평행하거나 레이가 평면에서 멀어짐
    if (nd >= 0.0f)
        return false;

    // 실제 충돌 거리 계산
    float t = (plane.distance - pn) / nd;
    if (t >= 0.0f)
    {
        distance = t; // 충돌 지점까지의 거리
        return true;
    }

    return false;
}

Triangle

  • 삼각형과 Point Test, Intersection, Raycast에 대해
  • 네비게이션 매쉬와 같은 요소를 만들때도 주로 사용

삼각형을 클릭했을 때 Point Test를 통해서 판별
삼각형을 구성하는 벡터들을 사용해서 교차벡터를 계산하고 이들의 내적을 검사

bool MathUtils::PointInTriangle(const Point3D& p, const Triangle3D& t)
{
    Vec3 a = t.a - p;           // p에서 꼭짓점 a로의 벡터
    Vec3 b = t.b - p;           // p에서 꼭짓점 b로의 벡터
    Vec3 c = t.c - p;           // p에서 꼭짓점 c로의 벡터

    Vec3 normPBC = b.Cross(c);  // PBC의 법선 벡터
    Vec3 normPCA = c.Cross(a);  // PCA의 법선 벡터
    Vec3 normPAB = a.Cross(b);  // PAB의 법선 벡터

    // 번선 벡터들의 방향이 모두 같은지 확인해서 점이 삼각형 내부에 있는지를 판단.
    // PBC와 PCA 법선 벡터가 서로 반대 방향이라면
    if (normPBC.Dot(normPCA) < 0.0f)
        return false;
    // PBC와 PAB 법선 벡터가 서로 반대 방향이라면
    else if (normPBC.Dot(normPAB) < 0.0f)
        return false;

    return true;
}

Vec3 MathUtils::ProjectVecOnVec(Vec3 from, Vec3 to)
{
    to.Normalize();

    float dist = from.Dot(to);

    return to * dist;
}

Plane3D MathUtils::FromTriangle(const Triangle3D& t)
{//삼각형이 있을 때 삼각형을 이용해서 플레인을 만드는 유틸
    Plane3D result;

    result.normal = (t.b - t.a).Cross(t.c - t.a);
    result.normal.Normalize();

    result.distance = result.normal.Dot(t.a);

    return result;
}

Raycast

Vec3 MathUtils::Barycentric(const Point3D& p, const Triangle3D& t)
{
    return Vec3();
}

bool MathUtils::RayCast(const Triangle3D& triangle, const Ray3D& ray, OUT float& distance)
{
    
    Plane3D plane = FromTriangle(triangle);

    float t = 0;
    // 평면과 레이 충돌 검사
    if (RayCast(plane, ray, OUT t) == false)
        return false;

    Point3D result = ray.origin + ray.direction * t;

    Vec3 barycentric = Barycentric(result, triangle);

    if (barycentric.x >= 0.0f && barycentric.x <= 1.0f &&
        barycentric.y >= 0.0f && barycentric.y <= 1.0f &&
        barycentric.z >= 0.0f && barycentric.z <= 1.0f)
    {
        distance = t;
        return true;
    }

    return false;
}

참고 강의

기본도형
https://www.inflearn.com/courses/lecture?courseId=329791&tab=curriculum&type=LECTURE&unitId=161173&subtitleLanguage=ko
Point Test
https://www.inflearn.com/courses/lecture?courseId=329791&tab=curriculum&type=LECTURE&unitId=161174&subtitleLanguage=ko
Intersection
https://www.inflearn.com/courses/lecture?courseId=329791&tab=curriculum&type=LECTURE&unitId=161175&subtitleLanguage=ko
Raycasting
https://www.inflearn.com/courses/lecture?courseId=329791&tab=script&type=LECTURE&unitId=161176&subtitleLanguage=ko

profile
게임 클라이언트 프로그래머 준비중 (공부 및 기록용)

0개의 댓글