У меня есть карта окружения сияния HDR в виде текстурного изображения LatLong 2D, которое я хочу преобразовать в кубическую карту. Я делаю это, загружая карту HDR как 2D-текстуру с плавающей точкой, проецируя ее на куб, а затем визуализирую сцену внутри этого куба с 6 различных направлений, непосредственно заполняя кубическую карту glFramebufferTexture2D
с соответствующими гранями куба в качестве цели текстуры функции.
Сгенерированная кубическая карта является кубической картой с плавающей запятой, сгенерированной следующим образом:
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
for (unsigned int i = 0; i < 6; ++i)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, NULL);
}
if (mipmap)
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
Обратите внимание, что параметр типа GL_FLOAT
поэтому он должен правильно принимать значения HDR. HDR-изображение загружается с помощью stb_image.h
следующее:
if (stbi_is_hdr(path.c_str()))
{
stbi_set_flip_vertically_on_load(true);
int width, height, nrComponents;
float *data = stbi_loadf(path.c_str(), &width, &height, &nrComponents, 0);
if (data)
{
GLenum format;
if (nrComponents == 3)
format = GL_RGB;
else if (nrComponents == 4)
format = GL_RGBA;
Bind();
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_FLOAT, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (Mipmapping)
glGenerateMipmap(GL_TEXTURE_2D);
Unbind();
stbi_image_free(data);
}
}
Я также попытался перебрать этот массив и извлечь максимальное значение с плавающей запятой, чтобы увидеть, правильно ли загружен HDR и самое высокое значение с плавающей запятой моей текущей карты HDR: 288
который намного выше 1.0
что я ожидал.
Вот где все становится сложнее, основываясь на входной текстуре (карта с плавающей запятой HDR) и выходной кубической карте (как поплавок). Я ожидаю, что грани кубической карты будут правильно обрабатываться как текстуры с плавающей запятой и напрямую копировать значения HDR. Тем не менее, кубическая карта выглядит как LDR, когда я добавляю тональное отображение (с переменной экспозицией), я получаю довольно много полос, и мне явно не хватает точности HDR, как показано на следующем изображении (с экспозицией ~ 7,5)
Я не уверен, что мне чего-то не хватает, и я не смог найти много документов в OpenGL, касающихся рендеринга непосредственно в кадровые буферы с плавающей запятой; Я предполагаю, что это возможно, поскольку это не имело бы смысла, если бы не было.
Для полноты, вот соответствующий код, который генерирует кубическую карту из изображения LatLong (с renderCustomCommand
рендеринг куба с правильными настройками сэмплеров):
glGenFramebuffers(1, &m_FramebufferCubemap);
glGenRenderbuffers(1, &m_CubemapDepthRBO);
Camera faceCameras[6] = {
Camera(position, vec3( 1.0f, 0.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f)),
Camera(position, vec3(-1.0f, 0.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f)),
Camera(position, vec3( 0.0f, 1.0f, 0.0f), vec3(0.0f, 0.0f, 1.0f)),
Camera(position, vec3( 0.0f, -1.0f, 0.0f), vec3(0.0f, 0.0f,- 1.0f)),
Camera(position, vec3( 0.0f, 0.0f, 1.0f), vec3(0.0f, -1.0f, 0.0f)),
Camera(position, vec3( 0.0f, 0.0f, -1.0f), vec3(0.0f, -1.0f, 0.0f))
};
glBindFramebuffer(GL_FRAMEBUFFER, m_FramebufferCubemap);
glBindRenderbuffer(GL_RENDERBUFFER, m_CubemapDepthRBO);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_CubemapDepthRBO);
glViewport(0, 0, width, height);
glBindFramebuffer(GL_FRAMEBUFFER, m_FramebufferCubemap);
for (unsigned int i = 0; i < 6; ++i)
{
Camera *camera = &faceCameras[i];
camera->SetPerspective(90.0f, width/height, 0.1f, 100.0f);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubeTarget->ID, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for (unsigned int i = 0; i < renderCommands.size(); ++i)
{
renderCustomCommand(&renderCommands[i], camera);
}
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, m_RenderSize.x, m_RenderSize.y);
А вот код для выборки 2D-изображения LatLong -> куб:
#version 330 core
out vec4 FragColor;
in vec3 wPos;
#include sample.glsl
uniform sampler2D environment;
void main()
{
vec2 uv = SampleLatLong(normalize(wPos));
vec3 color = texture(environment, uv).rgb;
FragColor = vec4(color, 1.0);
}
Обратите внимание, что преобразование LatLong в Cubemap проходит хорошо, так как 2D-среда правильно отображается на кубической карте, но просто фиксируется в диапазоне [0,1] в тот момент, когда она отображается как скайбокс, как если бы где-то в процессе она потеряла свое плавающее точечные данные.
Я застрял в этой проблеме какое-то время и надеялся, что кто-нибудь из вас сможет пролить некоторую проницательность (возможно ли даже сделать рендеринг напрямую для создания таких кубических карт?). Спасибо.
РЕДАКТИРОВАТЬ: Вот та же картинка с набором высокой экспозиции из Photoshop, поскольку вы можете увидеть много деталей, которые я потерял в рендере.
Третий параметр вашего вызова glTexImage2D должен быть GL_RGB16F или GL_RGB32F.
Указывает внутренний формат.
Два параметра GL_RGB и GL_FLOAT в конце используются только для указания расположения в памяти дополнительного указателя данных. Они не влияют на внутренний формат.
Других решений пока нет …