Graphics Programming

여러 재질을 사용하는 개체의 렌더링 본문

Season 1/OpenGL

여러 재질을 사용하는 개체의 렌더링

minseoklee 2016. 10. 9. 15:51

만들고 있는 렌더링 엔진에서 Wavefront OBJ 모델을 불러오기 위해 tinyobjloader를 사용한다. 오랫만에 확인해보니 tinyobjloader의 버전이 올라가서 교체했는데 컴파일이 안 된다. 소스 코드를 열어보니 명세가 바뀌어서 그런 거였다. 내 엔진의 코드도 고쳐야 해서 귀찮은 상황이 되었는데, 명세를 찬찬히 보니 이전에 못다 구현한 기능이 생각났다.


tinyobjloader는 하나의 3D 개체를 shape로 표현한다. shape는 position buffer, normal buffer, texcoord buffer, index buffer 등을 포함한다. 그리고 shape는 face의 모음인데 face마다 다른 재질을 가질 수도 있다. 예를 들어 축구공이라는 개체는 하나지만 이 공의 흰 부분과 검은 부분에 서로 다른 재질을 쓸 수도 있다는 것이다.


tinyobjloader는 각 face가 어떤 재질을 쓰는지 알려준다. 하지만 내 엔진에서는 기하 정보를 Geometry 객체에 담고, 재질을 Material 객체에 담아 물체 = Geometry + Material으로 처리한다. 즉 shape의 buffer들을 Geometry에 넣을 수는 있는데 이걸 여러 Material 객체와 대응시킬 수 없는 것이다.


이걸 어떻게 처리할지 몰라서 그냥 각 shape가 쓰는 재질들 중 첫 번째 재질만 쓰도록 구현했었다. 물론 이러면 복수 재질을 쓰는 물체를 제대로 그릴 수 없기 때문에 수정을 시작했다.


처음 생각한 것은 물체에서 재질 A, B, C를 사용하면 A와 관련된 버퍼, B와 관련된 버퍼, C와 관련된 버퍼를 모두 분리해 각각을 Geometry + Material 쌍으로 만드는 것이다. 그런데 원래 단일 버퍼인 것을 재질 수만큼 새로운 버퍼를 생성해 넣는 작업이 번거로웠다. 특히 정점의 인덱스가 뒤틀리는 걸 새로 계산하기가 너무 복잡했다.


생각해보면 position, normal, texcoord 등의 버퍼는 통째로 유지하고 인덱스 버퍼만 따로 분리해도 충분하다. 각 재질을 쓰는 부분을 그리는데 필요한 인덱스 버퍼들을 구축한. Geometry 객체는 재질 수만큼 생성하지만 모두 같은 버퍼를 공유하고, 오직 인덱스 버퍼만 다르게 하면 된다.


아니다. 한 물체 안에서 재질이 같은 face끼리 모아서 메시를 구성하는 것이 맞다. tinyobjloader가 제공하는 index buffer의 각 원소는 {position buffer의 원소 위치, normal buffer의 원소 위치, texcoord buffer의 원소 위치}로 구성되는 구조체다. 그런데 세 값이 모두 다를 수 있다. 그래서 단순히 position buffer의 원소 위치만 모아서 인덱스 버퍼를 구성하면 정점이나 uv 좌표가 맞지 않는다. 성능상 좋지 않지만 glDrawElements가 아니라 glDrawArrays를 쓸 수 밖에 없다. Wavefront OBJ 포맷 자체가 너무 구식이라 어쩔 수 없는 것 같다.



Comments