Computer Graphics/Vulkan

Vulkan - 11. gltf 형식의 모델 로드, normal map 수정

surkim 2025. 1. 14. 15:02

gltf를 받도록 아예 설계를 다시했다.

 

가장 큰 문제점이었던

model이 sub mesh가 있으면 material을 각각의 mesh마다 적용시켜야 하는데

내 엔진은 하나의 model이 하나의 material을 갖는 다는 점을 고치기 위해

많은 것을 바꿔야 했다.

 

우선 그전의 내 object 구조는 이랬다.

 

object

 - transform

 - material

 - model

 

model

 - meshes

 

model이 assimp로 model file을 로드해 받아놓는 형식이고

이 모델을 object생성시에 가져와 object를 만드는 형식이었다.

 

 

여기서 object 생성시 material을 가져와 model 전체에 적용한다는 점이 문제였는데

이걸 고치기위해 첫번째로 고안한 방법이

mesh에서 material을 관리하고

이러면 mesh에서 descriptorset과 uniform buffer를 관리해주게끔 만들어 주는 것이었다.

 

다 하고 나서 문제가 생겼었는데

object가 여러개인데 몇 개 보이지 않는 문제가 발생했었다..

 

원인은

object가 model을 가져와 생성하는데

이 model은 사실상 공유자원이지 각각의 object마다 생성하는게 아니라서

object와 descriptorset, uniform buffer는 1대1이어야하는데

사실상 model과 descriptorset, uniform buffer가 매칭이 되어

model당 하나씩만 렌더링이 됐었던 것이다.

 

구조 설계할 때 당연히 고려했어야 하는 문제인데

바보같이 생각을 못했다..

 

다시 여러 방면으로 고민하고 실험해보다가

지금의 방법은 이렇다.

 

 object 생성시 renderingComponet를 같이 생성한다. (나중에 entity - component 구조로 만들기 위해 class를 하나 더 만들었다.)

renderingComponent가 model을 들고 있고 descriptorset, uniform buffer를 같이 갖고 있는다.

 

model 생성시 로드했을 때 같이 로드되는 텍스쳐가 없으면 default material을 가지고 있고

object가 model을 가져와서 생성시에

renderingComponet가 model을 보고 material을 descriptorset에 넣어

uniform buffer와 descriptorset을 만든다.

 

그래서 구조가

object

 - transform

 - renderingComponent (object 생성시에 model을 가져와 생성)

 

renderingComponent

 - model

 - descriptorset

 - uniform buffer

 

model

 - materials

 - meshes

 

이렇게 된다.

 

구조 이렇게 짜니깐 나머지는 쉬운데

외부 model 받아올 때는 파일 확장자 봐서

gltf 일 때를 제외하고 default material을 mesh마다 벡터로 들고 있고

gltf 일 때는 material texture를 받아서 material을 만들어 mesh마다 매칭시켜 놓는다.

 

그래서 Scene 구성은

m_floorDiffuseTexture = Texture::createTexture("textures/laminate_floor_02_diff_2k.jpg");
m_floorNormalTexture = Texture::createMaterialTexture("textures/laminate_floor_02_nor_gl_2k.jpg");
m_floorRoughnessTexture = Texture::createMaterialTexture("textures/laminate_floor_02_rough_2k.jpg");

m_floorMaterial = Material::createMaterial(
    {glm::vec3(1.0f, 1.0f, 1.0f), m_floorDiffuseTexture, true},
    {m_floorNormalTexture, true},
    {0.5f, m_floorRoughnessTexture, true},
    {0.0f, m_defaultTextures.metallic, false},
    {1.0f, m_defaultTextures.ao, false},
    {0.0f, m_defaultTextures.height, false}
);

m_floorObject = Object::createObject("floor", m_planeModel, 
Transform{glm::vec3(0.0f, -1.7f, 0.0f), glm::vec3(-90.0f, 0.0f, 0.0f), glm::vec3(5.0f, 5.0f, 5.0f)});
m_objects.push_back(m_floorObject);

m_floorObject->updateMaterial({m_floorMaterial});

이런식으로 외부 material을 받아 그릴 수도 있고

 

model file에 texture가 포함된경우

m_cameraModel = Model::createModel("Models/Camera_01_2k.gltf/Camera_01_2k.gltf", m_defaultMaterial);
m_cameraObject = Object::createObject("camera", m_cameraModel, 
Transform{glm::vec3(0.1f, -1.2f, 0.1f), glm::vec3(0.0f, -50.0f, 0.0f), glm::vec3(1.0f, 1.0f, 1.0f)});
m_objects.push_back(m_cameraObject);

이런식으로만 받아도 텍스쳐, material적용이 된다!

 

결과는

 

 

 

 

+

디버깅이 편한건 point light라

방향성광원에서 점광원으로

바꾼 과정에서 놓쳤던 에러를 하나 발견했었는데

normal map만 적용하면 빛이 이상하게 적용이 되던 문제가 있었다.

진짜진짜 2박3일 동안 디버깅하다가 발견한게

 

texture image format을 VK_FORMAT_R8G8B8A8_SRGB로 받아서

감마보정이 들어간 이미지로 자동 보정했었는데 이게 문제였다.

material texture도 감마보정을 해줘서

이상한 값이 되어버려 normal도 사실상 roughness도 metallic도 다 이상한 값으로 pbr계산을 했었던 것이다.

 

gltf 파일로 여러개 로딩해서 보니깐 두들어지게 보여서 발견했고

그전까지 pbr 한답시고 한거는 해골물 마셨던 것이었다 ㅠㅠ

 

이걸 찾으려고 shader부터 싹다 바꿔가며 찾아봤었는데..

쨋든 찾아서 고쳐 다행이다.

 

다음은 그림자다.

이번거에 너무 늘어진거 같아 후딱 하고 와야겠다.