Computer Graphics/SCOP

SCOP - 3. assimp 대체 라이브러리 만들기 (0)

surkim 2024. 9. 29. 09:02

이번 포스트에서는 기존에 사용하던 assimp 라이브러리를 대체하기 위해 새로운 라이브러리를 만드는 과정을 설명하려 한다. 이름은 내 닉네임을 딴 sAssimp로 정했다.

1. assimp 라이브러리란?

assimp는 OpenGL 등 다양한 그래픽 API에서 사용하는 3D 모델 데이터를 쉽게 로드하고 처리할 수 있도록 도와주는 라이브러리이다. .obj, .fbx 등의 파일 포맷을 파싱하고, 모델의 버텍스(vertex), 인덱스(indices), 노멀(normal)텍스처 좌표 등을 가져와 렌더링에 사용할 수 있게 한다. 이 라이브러리를 사용하면 다양한 3D 모델 포맷을 다루는 작업을 간단하게 처리할 수 있다.

 

2. 과제에 맞는 sAssimp 라이브러리

bool sAssimp::LoadBysAssimp(const std::string& filename) {
    auto result = LoadTextFile(filename);

    if (!result.has_value())
        return false;

    std::stringstream text(result.value());
    std::string line;

    while (std::getline(text, line)) {
        std::stringstream ss(line);
        std::string type;
        ss >> type;
        if (type == "v") {  // 버텍스 파싱
            if (!ParseVertex(ss))
                return false;
        }
        else if (type == "vt") {  // 텍스처 좌표 파싱
            if (!ParseTexCoord(ss))
                return false;
        }
        else if (type == "vn") {  // 노멀 벡터 파싱
            if (!ParseNormal(ss))
                return false;
        }
        else if (type == "f") {  // 페이스 정보 파싱
            if (!ParseFace(ss))
                return false;
        }
        else if (type == "mtllib") { // mtl 정보 파일 파싱
            if (!ParseMtl(ss, filename))
                return false;
        }
    }
    return true;
}

 

bool sAssimp::ParseVertex(std::stringstream& ss) {
    glm::vec3 vertex;

    std::string token;

    if (!(ss >> token))
        return false;
    auto x = ParseFloat(token);
    if (!x.has_value())
        return false;
    vertex.x = x.value();

    if (!(ss >> token))
        return false;
    auto y = ParseFloat(token);
    if (!y.has_value())
        return false;
    vertex.y = y.value();

    if (!(ss >> token))
        return false;
    auto z = ParseFloat(token);
    if (!z.has_value())
        return false;
    vertex.z = z.value();

    if (ss >> token)
        return false;

    m_vertices.push_back(vertex);
    return true;
}

 

과제에서는 이러한 외부 라이브러리를 사용할 수 없기 때문에, assimp의 기능을 직접 구현해 대체해야 한다.

내가 만드는 sAssimp 라이브러리는 3D 모델 데이터를 파싱하고, 필요한 정보를 추출OpenGL로 렌더링할 수 있도록 하는 기능을 제공하게 된다.

 

.obj 파일을 파싱할 때 o, g와 같은 키워드들은 객체나 그룹을 정의하는데 사용되지만, 실제 렌더링에는 큰 영향을 주지 않는다고 판단했다. 물론 이 부분이 텍스처 적용이나 다른 세부적인 렌더링 요소에 영향을 줄 수 있다는 점은 알고 있지만, 파싱 과정에서 복잡성을 줄이기 위해 이러한 키워드들은 무시하기로 했다. 이를 통해 파싱 로직을 간소화하고 중요한 데이터인 버텍스, 텍스처, 노멀, 페이스에 집중하는 데 주력했다.

 

기본적으로 sAssimp는 .obj 파일의 구조를 파싱하며, 각 버텍스의 위치, 텍스처 좌표, 그리고 인덱스 정보를 가져와 버텍스 버퍼에 넣어준다. 하지만 기존에 사용하는 assimp와 다르게, 현재 내 라이브러리에는 몇 가지 문제가 있다.

 

3. 인덱싱 문제와 해결 방안

왼쪽이 기존 라이브러리(assimp) 오른쪽이 내꺼 (sAssimp)

 

 

현재 sAssimp 라이브러리로 모델을 렌더링하면 기존 assimp 라이브러리로 렌더링할 때보다 이미지가 흐릿하거나 뭉뜬 느낌이 드는 문제가 있다.

 

같이 과제를 진행하는 동료와 함께 이 문제를 해결하기 위해 여러 가지 실험을 진행했다. 특히 .obj 파일의 f (face) 요소를 인덱싱하여 버텍스를 재구성하는 과정이 차이를 만들 수 있다는 점에 집중했다.

 

기존 assimp 라이브러리는 f 요소를 적절히 인덱싱하여 버텍스 버퍼에 정렬된 형태로 넣는데, 예를 들어 f 16 2 3이라는 데이터가 있다면 이를 재구성하여 인덱스 0부터 시작하는 정렬된 데이터로 변환한 후 버퍼에 넣는다.

 

반면, sAssimp는 .obj 파일의 f 요소를 있는 그대로 버텍스 버퍼에 넣고 있어 무질서한 인덱스가 저장되고, 이로 인해 렌더링 시 올바른 결과를 내지 못하는 것 같다. 하지만 이는 여전히 가정에 불과하며, 실제로 인덱싱 정렬과 재구성 로직을 추가함으로써 문제가 해결되는지 확인해봐야 한다.

 

4. 성능 최적화와 코드 리팩토링

현재 sAssimp의 구조에서는 클래스 내부에 모든 데이터를 보관하고 있다. 이 때문에 스코프가 닫히면 데이터가 손실될 위험이 있어, 깊은 복사를 통해 데이터를 보내고 있다. 하지만 이러한 깊은 복사는 불필요한 메모리 사용과 성능 저하를 유발한다. 이를 개선하기 위해 std::unique_ptr와 같은 스마트 포인터를 적절하게 활용해 데이터 소유권을 효율적으로 관리하고자 한다.

 

향후 코드 리팩토링을 통해 성능 최적화를 진행할 예정이며, 중간 결산으로 이 부분에 대한 계획을 공유하려 한다.

다음 포스트에서는 인덱싱 문제를 해결하고 성능을 향상시키는 과정을 다룰 예정이다.