Конвертировать текстуру SRGB в линейную в OpenGL - PullRequest
1 голос
/ 03 апреля 2020

В настоящее время я пытаюсь правильно применить гамма-коррекцию в моем рендерере. Я установил мой кадровый буфер как SRGB с glEnable(GL_FRAMEBUFFER_SRGB), и теперь мне осталось правильно импортировать текстуры SRGB. Я знаю три подхода к этому:

  1. Конвертировать значение в шейдер: vec3 realColor = pow(sampledColor, 2.2)
  2. Заставить OpenGL сделать это для меня: glTexImage2D(..., ..., GL_SRGB, ..., ..., ..., GL_RGB, ..., ...);

  3. Преобразование значений напрямую:

    for (GLubyte* pixel = image; pixel < image + size; ++pixel) 
        *pixel = GLubyte(pow(*pixel, 2.2f) + 0.5f);
    

Теперь я пытаюсь используйте третий подход, но он не работает.

  1. Это очень медленно (я знаю, что оно должно l oop через все пиксели, но все же).
  2. Это заставляет все выглядеть совершенно неправильно (см. Изображение ниже).

Вот несколько изображений.

  1. Без гамма-коррекции: enter image description here
  2. Метод 2 (коррекция при выборке фрагмента шейдер) enter image description here
  3. Что-то странное при попытке метода 3 enter image description here

Так что теперь мой вопрос, что не так с методом 3 потому что он выглядит совершенно иначе, чем правильный результат (при условии, что метод 2 является правильным, что, если я думаю, что это так).

1 Ответ

4 голосов
/ 03 апреля 2020

Я установил свой кадровый буфер как SRGB с glEnable (GL_FRAMEBUFFER_SRGB);

Это не устанавливает ваш кадровый буфер в формат sRGB - он включает только преобразование sRGB , если фреймбуфер уже использует формат sRGB - они используют только состояние включения GL_FRAMEBUFFER_SRGB, чтобы фактически отключить преобразование sRGB на фреймбарах, которые имеют формат sRGB. Вам по-прежнему необходимо специально запросить ваш кадровый буфер по умолчанию windows 'на sRGB-совместимый (или, возможно, вам удастся получить его без запроса, но это будет сильно отличаться в реализациях и платформах), или вам нужно создать текстуру sRGB или render-target, если вы визуализируете в FBO.

Преобразование значений напрямую:

for (GLubyte* pixel = image; pixel < image + size; ++pixel) 
      *pixel = GLubyte(pow(*pixel, 2.2f) + 0.5f);

Прежде всего pow(x,2.2) это не правильная формула для sRGB - реальный использует небольшой линейный отрезок около 0, а для остальных - степень 2,4 - использование степени 2,2 - это всего лишь дальнейшее приближение.

Однако большая проблема с этим подходом состоит в том, что GLubyte является 8-битный целочисленный тип без знака с диапазоном [0,255] и выполнением pow(...,2.2), который возвращает значение в [0,196964.7], которое при преобразовании обратно в GLubyte будет игнорировать старшие биты и в основном вычислит модуль 256, так что вы будете получить действительно бесполезные результаты. Концептуально вам нужно 255.0 * pow(x/255.0,2.2), что, конечно, может быть еще более упрощено.

Большая проблема здесь заключается в том, что выполняя это преобразование, вы в основном теряете большую точность из-за нелинейных искажений диапазона значений. , Если вы сделаете такое преобразование заранее, вам придется использовать текстуры с более высокой точностью для хранения линеаризованных значений цвета (например, 16-битовых половинных чисел на канал), просто сохраняя материал, так как 8-битный UNORM является полным катастрофа - и именно поэтому графические процессоры выполняют преобразование непосредственно при доступе к текстуре, чтобы вам не пришлось увеличивать объем памяти ваших текстур в 2 раза.

Так что я действительно сомневаюсь, что Ваш подход 3 будет «правильно импортировать текстуры SRGB». Это просто уничтожит любую верность, даже если все сделано правильно. Подходы 1 и 2 не имеют такой проблемы, но подход 1 просто глуп, учитывая, что аппаратное обеспечение сделает это за вас бесплатно. поэтому мне очень интересно, почему вы вообще учитываете 1 и 3.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...