Computer Graphics/VulkanPT

pt - 1. (.pbrt)파일 파싱과 나이브한 구현

surkim 2025. 4. 28. 10:31

원래 작업하던 (reflection, gi)이 있었기 때문에 그리 많이 바꿀 필요는 없었다.

대신 .pbrt 파일을 파싱하는 과정이 필요했는데

모든 파일을 전부 대응하게 하는것은 예외도 많고 엄청나게 고생할 거 같아

원래 레퍼런스로 잡은 bathroom.pbrt 만 대응하도록 방향을 바꿨다.

 

.pbrt파일 자체가 게임 오브젝트처럼 모든 재질에 대해 동일한 파라미터(roughness, metalic) 로 표현되는게 아니라 

재질자체의 속성이 이미 정해져있어서 그거에 맞는 파라미터만 있거나 파라미터가 재질에 따라 다르다

예)

# small bottle by the tub
MakeNamedMaterial "etiquette" 
        "float uroughness" [ 0.0104080001 ] 
        "float vroughness" [ 0.0104080001 ] 
        "string type" [ "glass" ] 

# black seam around top of tub
MakeNamedMaterial "jointure_noire" 
        "float roughness" [ 0.1 ] 
        "string type" [ "plastic" ] 
        "rgb Kd" [ .04 .04 .04 ] 
        "rgb Ks" [ 0.5 0.5 0.5 ] 

# walls
MakeNamedMaterial "latte" 
      #"string type" "fourier" "string bsdffile" "bsdfs/ceramic.bsdf"
      "string type" "matte"
      "color Kd" [ .55 .5 .5 ]
      "float sigma" 20

bathroom.pbrt 파일안의 일부이고

각각 유리, 플라스틱, 매트 재질이다. 또 다른 재질들도 있다.

 

이걸 파싱해 gpu로 올리는 과정에 꽤나 애를 먹었는데

 

모든 재질을 통합적으로 관리하면 재질마다 쓸모없는 메모리가 생겨버리니깐

struct MaterialGPU {
	int type;
	int index;
	float pad0;
	float pad1;
};

struct MirrorGPU {
	vec3 Kr;
	float pad0;

	int KrIdx;
	float pad1;
	float pad2;
	float pad3;
};


struct PlasticGPU {
	vec3 Kd;
	float pad0;
	vec3 Ks;
	float pad1;

	
	int KdIdx;
	int KsIdx;
	float roughness;
	int roughnessIdx;

	
	int remaproughness;
	float pad2;
	float pad3;
	float pad4;
};


struct ShapeGPU {
	mat4 modelMatrix;
	
	uint64_t vertexAddress;
	uint64_t indexAddress;
	
	int materialIdx;
	int areaLightIdx;
	int alphaIdx;
	int shadowAlphaIdx;
	
	int reverseOrientation;
	float pad0;
	float pad1;
	float pad2;
};

 

layout(set = 1, binding = 0) readonly buffer instanceBuffer {
    ShapeGPU instances[];
};

layout(set = 2, binding = 0) readonly buffer AreaLightBuffer { 
    AreaLightGPU lights[]; 
};
layout(set = 2, binding = 1) readonly buffer MaterialBuffer { 
    MaterialGPU materials[]; 
};
layout(set = 2, binding = 2) readonly buffer UberBuffer { 
    UberGPU ubers[];
};
layout(set = 2, binding = 3) readonly buffer MatteBuffer { 
    MatteGPU mattes[];
};

 

이런식으로 각 재질에 대한 구조체 배열로 적재한 후

제일 윗단(Shape)에서 material 인덱스를 참조해 그 정보를 가져오는 형식으로 했다.

shape 구조체는 .pbrt파일에서 있는 정보인데 모양을 가지고 있는 모든 오브젝트에 대응되고

이게 재질이 있는 오브젝트 뿐만 아니라 areaLight도 있어서 결국 이 방식을 그대로 채용할 수 밖에 없었다.

 

현재는 완전 나이브하게 화면에서 레이를 쏘고 코싸인 헤미스피어 샘플링만 사용하여 렌더링을 해봤다.

결과적으로 모든 오브젝트가 매트하게 나오는 결과가 나왔고 예상했던 결과였다.

레퍼런스

 

 

앞으로 방향)

  • 샘플링 개선 예정
    현재는 미러를 제외한 모든 재질에서 코사인 헤미스피어 샘플링만 사용하고 있는데
    앞으로 각 재질의 반사 모델에 맞춰 BRDF에 맞는 샘플링으로 개선할 예정
  • 스펙트럼 데이터 보정 문제
    .pbrt 파일을 읽어오는 라이브러리를 직접 구현하지 않고 외부 라이브러리(minipbrt)를 사용했는데,
    이 파서에서 스펙트럼 → XYZ → RGB 변환이 잘못되어 받아오는 조명 데이터가 정확하지 않은 문제가 있는거 같다.
    현재는 임시 보정을 통해 사용 중
    • 정면 조명 (6500K, 100) = 차가운 밝은 조명
    • 천장 조명 (2700K, 10) = 따뜻하고 약한 조명
      으로 설정되었지만,
      내 결과에선 천장 조명이 과하게 강조되어 레퍼런스보다 붉은 느낌이다.
      정확한 스펙트럼 변환 및 보정을 통해 해결할 예정