옥트리

jelly·2025년 2월 26일

📌 옥트리(Octree) 개념과 게임에서의 활용
✅ 옥트리(Octree)란?
옥트리(Octree)는 3차원 공간을 8개의 균등한 영역(Octants)으로 재귀적으로 분할하는 자료구조입니다.
이진 트리(Binary Tree)가 1차원, 쿼드트리(Quadtree)가 2차원이라면, 옥트리는 3차원 공간을 효율적으로 관리하는데 사용됩니다.

🛠 옥트리가 게임에서 사용되는 경우
공간 분할 (Spatial Partitioning)

광대한 맵에서 충돌 검사, 렌더링 최적화
예: 오픈월드 게임에서 먼 개체는 비활성화, 가까운 개체만 활성화
충돌 감지 (Collision Detection)

물리 엔진에서 충돌을 빠르게 탐색하기 위해 사용
예: 총알이 날아갈 때, 전체 맵이 아니라 특정 영역만 충돌 검사
레이트레이싱 (Ray Tracing)

광선이 특정 물체에 충돌하는지 검사할 때 옥트리 구조를 사용하면 빠르게 탐색 가능
LOD(Level of Detail) 최적화

카메라 거리에 따라 세밀한 모델을 로드하거나 해상도를 조정할 때 사용
🖥 C++로 옥트리 구현 예제
✅ 1. 옥트리 노드 구조 정의

struct OctreeNode
{
    FVector Center;   // 노드의 중심 좌표
    float Size;       // 노드의 크기
    std::vector<AActor*> Objects;  // 이 노드에 포함된 객체들
    OctreeNode* Children[8];       // 8개의 하위 노드 (옥탄트)
    
    OctreeNode(FVector InCenter, float InSize)
        : Center(InCenter), Size(InSize)
    {
        for (int i = 0; i < 8; i++)
            Children[i] = nullptr;
    }
};

✅ 2. 옥트리 클래스 생성

class Octree
{
public:
    OctreeNode* Root;

    Octree(FVector WorldCenter, float WorldSize)
    {
        Root = new OctreeNode(WorldCenter, WorldSize);
    }

    ~Octree()
    {
        DestroyNode(Root);
    }

    void DestroyNode(OctreeNode* Node)
    {
        if (!Node) return;
        for (int i = 0; i < 8; i++)
        {
            DestroyNode(Node->Children[i]);
        }
        delete Node;
    }

    // 객체 추가 (재귀적으로 적절한 옥트리 노드에 삽입)
    void Insert(AActor* Object, FVector Position)
    {
        InsertRecursive(Root, Object, Position);
    }

    void InsertRecursive(OctreeNode* Node, AActor* Object, FVector Position)
    {
        if (!Node) return;

        // 만약 노드가 충분히 작다면, 현재 노드에 추가
        if (Node->Size <= 100.0f) 
        {
            Node->Objects.push_back(Object);
            return;
        }

        // 적절한 옥탄트를 찾아서 삽입
        int Index = GetOctant(Node->Center, Position);
        if (!Node->Children[Index])
        {
            FVector NewCenter = GetChildCenter(Node->Center, Node->Size, Index);
            Node->Children[Index] = new OctreeNode(NewCenter, Node->Size / 2);
        }

        InsertRecursive(Node->Children[Index], Object, Position);
    }

    // 특정 위치가 어느 옥탄트에 속하는지 계산
    int GetOctant(FVector NodeCenter, FVector Position)
    {
        int Index = 0;
        if (Position.X >= NodeCenter.X) Index |= 1;
        if (Position.Y >= NodeCenter.Y) Index |= 2;
        if (Position.Z >= NodeCenter.Z) Index |= 4;
        return Index;
    }

    // 자식 노드의 새로운 중심점 계산
    FVector GetChildCenter(FVector ParentCenter, float ParentSize, int Index)
    {
        float Offset = ParentSize / 4;
        return ParentCenter + FVector(
            (Index & 1) ? Offset : -Offset,
            (Index & 2) ? Offset : -Offset,
            (Index & 4) ? Offset : -Offset
        );
    }
};

✅ 3. 언리얼 엔진에서 옥트리 활용

void AMyGameMode::SpawnObjects()
{
    // 월드 크기 10000x10000x10000 기준으로 옥트리 생성
    Octree MyOctree(FVector(0, 0, 0), 10000.0f);

    for (int i = 0; i < 100; i++)
    {
        FVector SpawnLocation = FVector(FMath::RandRange(-5000, 5000), 
                                        FMath::RandRange(-5000, 5000), 
                                        FMath::RandRange(-5000, 5000));

        AActor* NewActor = GetWorld()->SpawnActor<AActor>(ActorClass, SpawnLocation, FRotator::ZeroRotator);
        MyOctree.Insert(NewActor, SpawnLocation);
    }
}

🎯 정리
✅ 옥트리는 3D 공간을 8개로 나누어 관리하는 자료구조
✅ 게임에서 충돌 감지, LOD 최적화, 레이트레이싱 등에 활용
✅ 객체 삽입, 탐색, 충돌 감지 성능을 향상시키는 데 유용
✅ C++로 옥트리를 구현하면 게임 성능을 최적화할 수 있음

🎮 특히 오픈월드 게임이나 대규모 오브젝트가 있는 환경에서 필수적으로 활용되는 개념! 🚀

profile
jelly

0개의 댓글