개발 / / 2024. 4. 30. 16:04

webgpu obj load 오브젝트 로딩 깨짐 늘어짐 문제 해결 fix vertex broken + blender decimate vertex face count 버텍스 줄이기 면 줄이기 + uint16 to uint32

반응형

https://like-grapejuice.tistory.com/396

 

obj loading 오브젝트 로딩 깨짐 문제

이렇게 obj 깨져서 어떻게 해야할지.. 고민하던 중이었다.

 

문득 든 생각

토끼 모델의 vertex만 조금씩 줄여서 로딩해볼까?

 

blender decimate ratio 0.2 face count 22480

쪼금 깨진 상태이다. 오른쪽 그림을 자세히 보면 깨진 부분이 토끼의 수염 인근쪽에 몰려있는 것을 볼 수 있다.

이걸 보고 추측 가능한 것은

obj line 혹은 indices 크기가
오버플로우 나서 저렇게 밀린 현상이 생기나?

 

decimate 기능은 vertex 개수는 일정하고, face의 개수를 조정한다.

점점 face의 개수가 늘어날 때마다 많이 깨진다는 것은 일정 크기까지만 받아들이다가 넘친 것이라고 예상된다.

 

positoin uv normal indices 크기

    return {
      positions: new Float32Array(finalPosition),
      uvs: new Float32Array(finalUvs),
      normals: new Float32Array(finalNormals),
      indices: new Uint16Array(finalIndices),
    };

ObjLoader에서 파싱 후 반환하는 값들이다. 보면 float32, unsigned int16 등 타입이 있는데, 혹시나 이 크기에 걸린 것이 아닐까 추측해본다.

 

일단 indices의 크기인 unsigned int 16은 2^16 = 65536 즉 0~65535까지 담을 수 있다. 

다르게 말하면 obj의 indices 개수는 ObjLoader가 65535보다 많아지면 오버플로우 될 것이다.

엥? 근데 array에 들어가는 element의 타입인거지 array이 length가 아닌데?

즉 값이 65535보다 큰 경우에 해당한다. 근데 값이 그것보다 큰 경우는 못본 것 같은데 말이다. 

 

vertex 개수는 중복을 포함해서 잘 못 띄웠다.

face 개수는 indices의 개수인데, obj 파일에서 face 정보는 총 22,480개 있다.

indices는 face하나에 세 개의 vertex가 대응되니까, 그 세 배이므로 67,440으로 정확히 일치한다.

 

67,440?... uint16... 65,535? ...!

 

위에서 좀 헷갈렸는데, indices의 개수를 확인해보고 나니까 알았다.

결국 finalIndices 에 최종적으로 67,440이라는 수가 들어가게 되는데 TS에서는 기본적으로 number형태로 제공된다.

 

number 타입에 대해서 알아보기

 

TS에서 number형태로 제공되기 때문에 console.log로 찍으면 오버플로우 같은거 안생기고 67,440이 출력된다.

 

하지만 방심하면 안된다 우리는 webgpu를 하고 있다

 

JS에서 아무리 숫자를 다 지원한다고 한들 결국에는 이 값을 buffer에 담아 GPU로 전달하게 된다. 그렇다면 buffer의 타입은 엄연히 uint16으로 한계가 있는 것이다.

 

DefaultProgram.ts
  render(
    renderPassAPI: RenderPassAPI,
    vertexData: VertexBuffers
  ): DefaultProgram {
    super.render(renderPassAPI);
    renderPassAPI.setBindGroup(2, this.lightModelBindGroup);
    renderPassAPI.setVertexBuffer(0, vertexData.position.data);
    renderPassAPI.setVertexBuffer(1, vertexData.normals.data);
    renderPassAPI.setIndexBuffer(vertexData.indices.data, "uint16");
    renderPassAPI.drawIndexed(vertexData.indices.length);
    return this;
  }

engine의 역할을 하는 program class의 base class이다. 

보면 renderPassAPI.setIndexBuffer함수에 "uint16" 형식으로 전달되게 되어있다. buffer로 들어가면 65,535보다 큰 숫자는 정상적으로 들어갈 수 없다. 잘리든지 오버플로우가 나던지 할 것이다.

 

가장 유력한 용의자는 이러한 문제이다. 65,535에 가까운 67,440의 경우 많이 깨지지 않고 더 face가 많아질 수록 많이 깨지는 것이 그 근거이다.

 

indices uint16 문제의 해결책은?

그러게 그게 문제다.

 

setIndexBuffer에 대해서 알아보기

 

setIndexBuffer는 uint32 형식을 지원한다고 하니까. 프로젝트를 면밀히 뒤져서 한 번 변경해보자.

 

됐다! indices는 현재 70,812 개인데 완전히 깨지지않고 로딩되었다!

 

고해상도 버전의 stanford_bunny.obj로 바꿔도 잘 되려나

 

매우 잘된다. 매우 indices 33만 개가 넘는데도 전혀 깨지지 않고 잘 로딩된다!

 

드디어 이 온전하고 요망한 토끼녀석을 고해상도로 볼 수 있게 되었다. 

carmen씨는 전혀 코드를 잘못짜지 않았다. (vertex quad를 구현하지 않은 것은 잘못 짰지만...)

 

이대로 다음 단계로 나아가자

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유