1.14 3D 모델 불러오기

이 튜토리얼에서는 파일에서 3D 모델을 불러오는 방법을 살펴보겠습니다. 지금까지 우리는 평면이나 큐브와 같이 정점 위치를 통해 수동으로 쉽게 정의할 수 있는 간단한 기하 도형을 렌더링해 왔습니다. 그러나 게임과 같은 실제 3D 애플리케이션에는 수작업으로 만들 수 없는 훨씬 더 복잡한 기하 도형이 포함됩니다. 따라서 파일에서 이러한 기하 도형을 불러오는 방법을 배워야 합니다.

플레이그라운드 실행 - 1_14_loading_models

이전 튜토리얼에서 인덱스 버퍼를 다루었던 이유는 인덱스와 정점 풀을 사용하는 것이 대부분의 3D 파일 형식에서 기하 도형을 저장하는 표준 방법이기 때문입니다. 따라서 인덱스 버퍼를 사용하는 것이 파일에서 불러온 기하 도형을 렌더링하는 가장 자연스러운 접근 방식입니다.

수많은 3D 파일 형식이 존재하며, 그 중 다수는 매우 복잡하고 저장 효율성을 위해 이진 인코딩을 사용하는 경우가 많지만, 우리는 가장 간단한 형식 중 하나인 .obj 파일 형식에 중점을 둘 것입니다. .obj는 단순함에도 불구하고 널리 사용되며, 사람이 읽을 수 있는 장점이 있어 모델 로딩 및 렌더링 디버깅에 유용합니다. 하지만 조명 및 애니메이션과 같은 고급 기능을 지원하지 않으므로 주로 기하 도형을 저장하는 데 적합합니다.

다음은 .obj 파일의 스니펫입니다:

v 0.000000 3.000000 1.800000
...
vn 0.2867 0.6914 -0.6631
...
f 2152//24 2158//24 2358//24

.obj 파일은 일반적으로 연속적인 데이터 섹션으로 구성됩니다:

  1. 정점 위치('v' 접두사)

  2. 선택적 텍스처 좌표('vt' 접두사)

  3. 선택적 정점 노멀('vn' 접두사)

  4. 면 정의('f' 접두사)

이 튜토리얼에서는 노멀은 포함하지만 텍스처 좌표는 없는 .obj 파일에서 찻주전자 모델을 불러올 것입니다. 노멀에 대해서는 조명을 다룰 때 자세히 논의할 것입니다.

면 섹션은 슬래시(/)로 구분된 정점, 텍스처 좌표, 노멀 섹션을 참조하는 인덱스를 사용하여 삼각형을 정의합니다. 각 줄은 'f'로 시작하며 그 뒤에 인덱스가 따릅니다. 이 구조는 이전에 소개했던 인덱스 버퍼 및 정점 버퍼와 잘 일치합니다.

우리는 자체 .obj 파일 파서를 작성하는 대신 OBJFile.js라는 오픈 소스 파서를 사용할 것입니다. 이 파서는 정점과 삼각형을 쿼리할 수 있는 JavaScript 객체를 생성합니다.

코드를 살펴보겠습니다. 셰이더 코드는 변경되지 않습니다. JavaScript 코드에서는 fetch API를 사용하여 찻주전자 .obj 파일을 불러옵니다:

const objResponse = await fetch('../data/teapot.obj');
const objBody = await objResponse.text();

let obj = await (async () => {
    return new Promise((resolve, reject) => {
        let obj = new OBJFile(objBody);
        obj.parse();
        resolve(obj);
    })
})();

큰 모델을 파싱하는 데 시간이 오래 걸릴 수 있으므로 비동기 함수를 사용합니다. .obj 파일이 불러와지면 위치 버퍼를 조립합니다:

let positions = [];

for (let v of obj.result.models[0].vertices) {
    positions.push(v.x);
    positions.push(v.y);
    positions.push(v.z);
}

positions = new Float32Array(positions);

let positionBuffer = createGPUBuffer(device, positions, GPUBufferUsage.VERTEX);

let indices = [];

for (let f of obj.result.models[0].faces) {
    for (let v of f.vertices) {
        indices.push(v.vertexIndex - 1)
    }
}

const indexSize = indices.length;

indices = new Uint16Array(indices);

const indexBuffer = createGPUBuffer(device, indices, GPUBufferUsage.INDEX);

.obj 파일의 첫 번째 모델(인덱스 0)에서 정점을 쿼리합니다. 정점 풀을 순회하며 모든 정점 위치를 위치 버퍼에 푸시합니다. 인덱스 버퍼의 경우, 모든 면과 해당 정점을 순회하며 정점 인덱스를 인덱스 배열에 푸시합니다.

.obj 파일 형식 인덱스는 0이 아닌 1부터 시작하므로 정점 인덱스를 1씩 감소시켜야 합니다. 렌더링해야 할 인덱스의 수를 나타내는 인덱스 크기를 저장하는 것을 잊지 마세요.

불러온 찻주전자 모델
불러온 찻주전자 모델

이러한 업데이트를 통해 이제 더 흥미로운 기하 도형을 다룰 수 있게 되었습니다. 컴퓨터 그래픽에 익숙하지 않은 분들은 보잘것없는 찻주전자가 그래픽스 실험에서 흔히 사용되는 요소로서 다양한 조명 기술과 기타 그래픽 혁신을 시연하는 데 자주 사용된다는 사실에 놀랄 수도 있습니다. 컴퓨터 그래픽 영역에서 찻주전자의 역사는 꽤 흥미롭습니다. 이에 대해 더 자세히 알아보려면 다음 링크를 참조하세요.

GitHub에 의견 남기기