Graphics Programming
UBO 활용 본문
OpenGL 처음 배울 때 glUniform()으로 일일이 값 설정하게 짜놓은 코드를 아직도 쓰고 있다. UBO를 쓸까 말까 고민만 하다 이제야 도입하고 있다.
1. DX11에서 constant buffer를 써보니 넘 편했다.
2. model-view transform, camera position 등 한 프레임에서 일정한 유니폼들은 한 번 설정해놓고 잊어버리는 게 편하고 코드 잘못 짤 가능성도 적어진다. 원래는 셰이더 프로그램마다 이런 유니폼들을 각자 선언하고 셰이더 바뀔 때마다 업데이트했다. 이런 유니폼들은 한 프레임에 한 번만 설정하면 전부 맞거나 전부 틀리니 디버깅도 쉽다.
그런데 막상 써보니 넘 구렸다. packed, shared, std140 등 여러 레이아웃이 있지만 오프셋은 아무튼 GL 쪽이 정하는데 이게 되게 헷갈린다.
// in view space
layout (std140, binding = 0) uniform UBO_UnpackHDR {
vec3 eyeDirection;
vec3 eyePosition;
uint numDirLights;
vec3 dirLightDirs[MAX_DIRECTIONAL_LIGHTS];
vec3 dirLightColors[MAX_DIRECTIONAL_LIGHTS];
uint numPointLights;
vec3 pointLightPos[MAX_POINT_LIGHTS];
vec3 pointLightColors[MAX_POINT_LIGHTS];
} ubo;
vec3는 4N 워드 정렬이라 eyeDirection이 HLSL의 packoffset(c0), eyePosition이 packoffset(c1)에 해당하는 오프셋에 위치한다.
그러면 uint는 packoffset(c2.x)에 있나 싶었는데 packoffset(c1.w)에 있다.
그래서 numPointLights는 dirLightColors의 마지막 vec3의 w 성분에 들어가냐 하면, 그 다음 4N 워드 경계에서 시작한다.
이상한 레이아웃 규칙을 외우게 하지 말고 그냥 내가 packoffset을 지정할 수 있게 해달라고 -_-
struct UBO_UnpackHDR {
glm::vec3 eyeDirection; float __pad0;
glm::vec3 eyePosition; uint32_t numDirLights;
glm::vec4 dirLightDirs[MAX_DIRECTIONAL_LIGHTS]; // w components are not used
glm::vec4 dirLightColors[MAX_DIRECTIONAL_LIGHTS]; // w components are not used
uint32_t numPointLights; glm::vec3 __pad1;
glm::vec4 pointLightPos[MAX_POINT_LIGHTS]; // w components are not used
glm::vec4 pointLightColors[MAX_POINT_LIGHTS]; // w components are not used
};
UBO_UnpackHDR data;
data.eyeDirection = glm::vec3(camera->getViewMatrix() * glm::vec4(camera->getEyeVector(), 0.0f));
data.eyePosition = glm::vec3(camera->getViewMatrix() * glm::vec4(camera->getPosition(), 1.0f));
data.numDirLights = std::min(static_cast<uint32_t>(scene->directionalLights.size()), MAX_DIRECTIONAL_LIGHTS);
// ... 이하 생략
glBindBuffer(GL_UNIFORM_BUFFER, ubo_hdr);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UBO_UnpackHDR), &data);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo_hdr);
레이아웃 맞추기는 홧병나지만 애플리케이션 측 구조체와 GL측 유니폼 블록의 레이아웃을 맞추고 나면 한방에 유니폼들을 밀어넣을 수 있다.