Отложенный рендеринг с OpenGL, с интенсивной пикселизацией вблизи освещенных границ на поверхностях
Объяснение проблемы
В настоящее время я использую точечные источники света для отложенного рендеринга, и у меня возникают проблемы с определением, откуда исходит сильная пикселизация / триангуляция, которая заметна только вблизи границ источников света.
Кажется, что проблема вызвана потерей точности где-то, но я не смог отследить точный источник. Нормальные значения - очевидная возможность, но у меня есть одноклассник, который использует directx и обрабатывает свои нормальные значения аналогичным образом, без проблем.
Примерно с 2 метров в наших игровых единицах (64 единицы / метр):
В нескольких сантиметрах. Обратите внимание, что "пикселизация" не меняет размер в мире, как я к нему подхожу. Тем не менее, он будет плавать, если я изменю ориентацию камеры:
Сравнение с крупным планом моего прямого рендера, который демонстрирует сферические полосы, которые можно ожидать с целью рендеринга RGBA8 (только 0-255 возможных значений для каждого цвета). Обратите внимание, что на моей отложенной картине задние стены имеют нормальную сферическую полосу:
Объем света показан здесь как зеленый каркас:
Как видно, эффект не виден, если вы не приблизитесь к поверхности (около одного метра в единицах нашей игры).
Реконструкция положения
Во-первых, я должен упомянуть, что я использую сферическую сетку, которую я использую, чтобы визуализировать только ту часть экрана, которую перекрывает свет. Я рендеринг только задних граней, если глубина больше или равна буферу глубины, как предложено здесь.
Чтобы восстановить положение фрагмента в пространстве камеры, я беру вектор из фрагмента пространства камеры по объему света, нормализуя его и масштабируя его на линейную глубину из моего буфера. Это своего рода гибрид методов, обсуждаемых здесь (с использованием линейной глубины) и здесь (сферические световые объемы).
Геометрический буфер
Моя настройка gBuffer:
enum render_targets { e_dist_32f = 0, e_diffuse_rgb8, e_norm_xyz8_specpow_a8, e_light_rgb8_specintes_a8, num_rt };
//...
GLint internal_formats[num_rt] = { GL_R32F, GL_RGBA8, GL_RGBA8, GL_RGBA8 };
GLint formats[num_rt] = { GL_RED, GL_RGBA, GL_RGBA, GL_RGBA };
GLint types[num_rt] = { GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_FLOAT };
for(uint i = 0; i < num_rt; ++i)
{
glBindTexture(GL_TEXTURE_2D, _render_targets[i]);
glTexImage2D(GL_TEXTURE_2D, 0, internal_formats[i], _width, _height, 0, formats[i], types[i], nullptr);
}
// Separate non-linear depth buffer used for depth testing
glBindTexture(GL_TEXTURE_2D, _depth_tex_id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, _width, _height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
1 ответ
Нормальная точность
Проблема была в том, что моим нормам просто не хватало точности. При 8 битах на компонент это означает 255 дискретных возможных значений. Изучение нормалей в моем gbuffer, наложенном поверх освещения, показало соответствие 1-1 с нормальным значением освещенному "пикселю".
Я не уверен, почему мой одноклассник не получает ту же проблему (он собирается расследовать дальше).
После еще одного исследования я обнаружил, что термин для этого - квантование. Еще один пример этого можно увидеть здесь с бликов на странице 19.
Решение
После изменения моей обычной цели рендеринга на RG16F проблема решена.
Используя метод, предложенный здесь для хранения и получения нормалей, я получаю следующие результаты:
Теперь мне нужно более компактно хранить свои нормали (у меня есть место только для 2 компонентов). Это хороший обзор техник, если кто-то окажется в такой же ситуации.
[РЕДАКТИРОВАТЬ 1]
Как отметили в комментариях Andon и GuyRT, 16 бит - это немного излишне для того, что мне нужно. Я переключился на RGB10_A2, как они предложили, и он дает очень удовлетворительные результаты даже на закругленных поверхностях. 2 дополнительных бита очень помогают (256 против 1024 дискретных значений).
Вот как это выглядит сейчас.
Следует также отметить (для тех, кто ссылается на этот пост в будущем), что изображение, которое я разместил для RG16F, имеет некоторые нежелательные полосы от метода, который я использовал для сжатия / распаковки нормали (там была некоторая ошибка).
[РЕДАКТИРОВАТЬ 2]
После обсуждения этой проблемы еще с одноклассником (который использует RGB8 без вредных последствий), я думаю, стоит упомянуть, что у меня может быть просто идеальная комбинация элементов, чтобы это появилось. Игра, для которой я создаю этот рендерер, является игрой ужасов, которая помещает вас в черную смолу со способностью сонара. Обычно в сцене было бы несколько источников света под разными углами (окружение моего одноклассника очень хорошо освещено - они делают гоночную игру на открытом воздухе). Это в сочетании с тем фактом, что он появляется только на очень круглых объектах относительно близко, возможно, я спровоцировал это. Это всего лишь (немного образованное) предположение с моей стороны.