Нужно ли гамма-коррекции окончательно выводить цвет на современный компьютер / монитор
Я предполагал, что мой конвейер гамма-коррекции должен выглядеть следующим образом:
- Использовать формат sRGB для всех текстур, загруженных в (
GL_SRGB8_ALPHA8
) так как все художественные программы предварительно гамма исправляют свои файлы. Когда выборка изGL_SRGB8_ALPHA8
Текстура в шейдере OpenGL автоматически преобразует в линейное пространство. - Делайте все расчеты освещения, постобработки и т. Д. В линейном пространстве.
- Преобразуйте обратно в пространство sRGB при написании окончательного цвета, который будет отображаться на экране.
Обратите внимание, что в моем случае окончательная цветная запись включает в себя запись из FBO (которая является линейной RGB-текстурой) в задний буфер.
Мое предположение было оспорено, как будто я на последнем этапе гамма-коррекции, мои цвета стали ярче, чем должны быть. Я установил сплошной цвет для рисования моими источниками света {255, 106, 0}, но когда я рендеринг, я получаю {255, 171, 0} (как определено при просмотре печати и выборе цвета). Вместо оранжевого я получаю желтый. Если я не буду корректировать гамму на последнем шаге, я получу правильное значение {255, 106, 0}.
По некоторым данным современные ЖК-экраны имитируют ЭЛТ-гамму. Они всегда? Если нет, как я могу определить, следует ли мне корректировать гамму? Я что-то не так делаю?
Редактировать 1
Теперь я заметил, что, хотя цвет, который я пишу с подсветкой, правильный, места, где я использую цвета из текстур, не являются правильными (но довольно темнее, как я ожидал бы без гамма-коррекции). Я не знаю, откуда это неравенство.
Редактировать 2
После попытки GL_RGBA8
для моих текстур вместо GL_SRGB8_ALPHA8
все выглядит идеально, даже при использовании значений текстуры в расчетах освещения (если я наполовину уменьшу интенсивность света, выходные значения цвета будут наполовину).
Мой код больше не учитывает гамма-коррекцию, и мой вывод выглядит правильно.
Это смущает меня еще больше, гамма-коррекция больше не нужна / не используется?
Изменить 3 - В ответ на ответ datenwolf
После еще нескольких экспериментов я запутался в нескольких моментах здесь.
1 - большинство форматов изображений хранятся нелинейно (в пространстве sRGB)
Я загрузил несколько изображений (в моем случае оба изображения.png и.bmp) и исследовал необработанные двоичные данные. Мне кажется, что изображения на самом деле находятся в цветовом пространстве RGB, как будто я сравниваю значения пикселей с помощью программы редактирования изображений с байтовым массивом, который я получаю в своей программе, они идеально совпадают. Так как мой редактор изображений дает мне значения RGB, это будет означать изображение, хранящееся в RGB.
Я использую stb_image.h /.c для загрузки своих изображений и следовал по нему до конца, загружая.png, и нигде не видел, чтобы гамма-коррекция изображения при загрузке. Я также изучил.bmps в шестнадцатеричном редакторе, и значения на диске соответствовали им.
Если эти изображения на самом деле хранятся на диске в линейном пространстве RGB, как я должен (программно) знать, когда указывать изображение в пространстве sRGB? Есть ли какой-нибудь способ сделать запрос для этого, который может предоставить более мощный загрузчик изображений? Или создатели изображений должны сохранить свое изображение как гамма-коррекцию (или нет), то есть установить соглашение и следовать ему для данного проекта. Я спросил пару художников, и ни один из них не знал, что такое гамма-коррекция.
Если я укажу, что мои изображения sRGB, они слишком темные, если в конце я не буду гамма-корректировать (что было бы понятно, если бы монитор выводил с использованием sRGB, но см. Пункт #2).
2 - "На большинстве компьютеров эффективное сканирование LUT является линейным! Что это значит?"
Я не уверен, что смогу найти, где эта мысль закончилась в вашем ответе.
Из того, что я могу сказать, поэкспериментировав, все мониторы, которые я тестировал, выводили линейные значения. Если я рисую полноэкранный четырехугольник и закрашиваю его жестко закодированным значением в шейдере без гамма-коррекции, монитор отображает правильное значение, которое я указал.
То, что я привел выше из вашего ответа и моих результатов, заставило меня поверить в то, что современные мониторы выводят линейные значения (т.е. не эмулируют гамма-излучение CRT).
Целевой платформой для нашего приложения является ПК. Для этой платформы (исключая людей с ЭЛТ или действительно старыми мониторами), было бы разумно сделать так, каков ваш ответ на #1, а затем #2, чтобы не гамма-корректно (т.е. не выполнять окончательное преобразование RGB->sRGB - либо вручную или используя GL_FRAMEBUFFER_SRGB)?
Если это так, то для каких платформ предназначен GL_FRAMEBUFFER_SRGB (или где было бы уместно использовать его сегодня), или мониторы, использующие линейный RGB, действительно новы (учитывая, что GL_FRAMEBUFFER_SRGB был представлен в 2008 году)?
-
Я разговаривал с несколькими другими разработчиками графики в моей школе, и, судя по всему, никто из них не учел гамма-коррекцию, и они не заметили ничего неправильного (некоторые даже не знали об этом). Один из разработчиков, в частности, сказал, что он получил неправильные результаты при учете гаммы, поэтому он решил не беспокоиться о гамме. Я не уверен, что делать в моем проекте для моей целевой платформы, учитывая противоречивую информацию, которую я получаю онлайн / вижу с моим проектом.
Редактировать 4 - В ответ на обновленный ответ datenwolf
Да, в самом деле. Если где-то в цепочке сигналов применяется нелинейное преобразование, но все значения пикселей не изменяются с изображения на дисплей, то эта нелинейность уже была предварительно применена к значениям пикселей изображения. Это означает, что изображение уже находится в нелинейном цветовом пространстве.
Ваш ответ имел бы смысл для меня, если бы я изучал изображение на моем дисплее. Чтобы быть уверенным, что я был ясен, когда я сказал, что проверял массив байтов для изображения, я имел в виду, что я проверял числовое значение в памяти для текстуры, а не вывод изображения на экран (что я и сделал для точки № 2), Для меня единственный способ увидеть то, что вы говорите, чтобы быть правдой, это если бы редактор изображений давал мне значения в пространстве sRGB.
Также обратите внимание, что я попытался проверить вывод на мониторе, а также изменить цвет текстуры (например, разделив пополам или удвоить его), и результат оказался правильным (измеренный с использованием метода, который я опишу ниже).
Как вы измерили реакцию на сигнал?
К сожалению, мои методы измерения намного грубее, чем ваши. Когда я сказал, что экспериментировал с моими мониторами, я имел в виду, что я выводил одноцветный полноэкранный четырехугольник, цвет которого был жестко закодирован в шейдере, в простой кадровый буфер OpenGL (который не выполняет никакого преобразования цветового пространства при записи в него). При выводе белого, 75% серого, 50% серого, 25% серого и черного отображаются правильные цвета. Теперь моя интерпретация правильных цветов наверняка может быть неправильной. Я делаю снимок экрана, а затем использую программу для редактирования изображений, чтобы увидеть, каковы значения пикселей (а также визуальную оценку, чтобы убедиться, что значения имеют смысл). Если я правильно понимаю, если бы мои мониторы были нелинейными, мне нужно было бы выполнить преобразование RGB->sRGB, прежде чем представить их на устройство отображения, чтобы они были правильными.
Я не собираюсь лгать, я чувствую, что немного теряю глубину здесь. Я думаю, что решение, которое я мог бы использовать для моей второй точки путаницы (окончательное преобразование RGB->sRGB), будет настройка яркости и настройка по умолчанию на то, что выглядит правильно на моих устройствах (без коррекции гаммы).
2 ответа
Прежде всего вы должны понимать, что нелинейное отображение, применяемое к цветным каналам, часто является чем-то большим, чем просто степенная функция. Нелинейность sRGB может быть аппроксимирована примерно на x^2,4, но это не совсем так. В любом случае ваши основные предположения более или менее верны.
Если ваши текстуры хранятся в более распространенных форматах файлов изображений, они будут содержать значения в том виде, в котором они представлены в графическом сканировании. Теперь есть два общих аппаратных сценария:
Интерфейс сканирования выводит линейный сигнал, и устройство отображения затем применяет нелинейное отображение. Старые ЭЛТ-мониторы были нелинейными из-за своей физики: усилители могли вводить только очень большой ток в электронный пучок, насыщать люминофор и т. Д., Поэтому в первую очередь была введена вся гамма-модель для моделирования нелинейностей ЭЛТ-дисплеев.,
Современные жидкокристаллические и OLED-дисплеи либо используют резисторные лестницы в своих усилителях-драйверах, либо имеют в своих процессорах изображений таблицы поиска гамма-всплесков.
Некоторые устройства, однако, являются линейными, и требуют, чтобы устройство формирования изображения предоставляло правильную соответствующую LUT для желаемого выходного цветового профиля на сканировании.
На большинстве компьютеров эффективное сканирование LUT является линейным! Что это значит, хотя? Небольшой объезд:
В качестве иллюстрации я быстро подключил аналоговый дисплей моего ноутбука (разъем VGA) к аналоговому осциллографу: синий канал на канал 1 области видимости, зеленый канал на канал 2 области видимости, внешний сигнал синхронизации по линии (HSync). Быстрая и грязная программа OpenGL, специально написанная в непосредственном режиме, использовалась для генерации линейной цветовой шкалы:
#include <GL/glut.h>
void display()
{
GLuint win_width = glutGet(GLUT_WINDOW_WIDTH);
GLuint win_height = glutGet(GLUT_WINDOW_HEIGHT);
glViewport(0,0, win_width, win_height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1, 0, 1, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glBegin(GL_QUAD_STRIP);
glColor3f(0., 0., 0.);
glVertex2f(0., 0.);
glVertex2f(0., 1.);
glColor3f(1., 1., 1.);
glVertex2f(1., 0.);
glVertex2f(1., 1.);
glEnd();
glutSwapBuffers();
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutCreateWindow("linear");
glutFullScreen();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
Графический вывод был настроен с помощью Modeline
"1440x900_60.00" 106.50 1440 1528 1672 1904 900 903 909 934 -HSync +VSync
(потому что это тот же режим, в котором работает плоская панель, и я использовал режим клонирования)
- гамма =2 LUT на зеленом канале.
- линейная (гамма =1) LUT на синем канале
Вот как выглядят сигналы одной линии сканирования (верхняя кривая: Ch2 = зеленый, нижняя кривая: Ch1 = синий):
Вы можете ясно видеть отображения x⟼x² и x⟼x (параболы и линейные формы кривых).
Теперь, после этого небольшого обхода, мы знаем, что значения пикселей, которые поступают в основной кадровый буфер, идут туда, как они есть: линейное линейное изменение OpenGL не претерпело дальнейших изменений, и только при применении LUT нелинейного сканирования это изменило сигнал, отправляемый на дисплей.,
В любом случае значения, которые вы представляете для сканирования (что означает экранные кадровые буферы), будут подвергаться нелинейному отображению в некоторой точке цепочки сигналов. И для всех стандартных потребительских устройств это отображение будет соответствовать стандарту sRGB, поскольку это наименьший общий фактор (т. Е. Изображения, представленные в цветовом пространстве sRGB, могут воспроизводиться на большинстве устройств вывода).
Поскольку большинство программ, таких как веб-браузеры, предполагают, что вывод подвергается sRGB для отображения сопоставления цветового пространства, они просто копируют значения пикселей стандартных форматов файлов изображений на экранную рамку, как они есть, без выполнения преобразования цветового пространства, что подразумевает что значения цвета в этих изображениях находятся в цветовом пространстве sRGB (или они часто просто преобразуются в sRGB, если цветовой профиль изображения не является sRGB); правильное действие (если и только если значения цвета, записанные в буфер кадров, сканируются на дисплее без изменений; при условии, что LUT сканирования является частью дисплея), это преобразование в указанный цветовой профиль, который ожидает дисплей.
Но это подразумевает, что сам по себе экранный кадровый буфер находится в цветовом пространстве sRGB (я не хочу расстраиваться из-за того, насколько это идиотизм, давайте просто примем этот факт).
Как объединить это с OpenGL? Прежде всего, OpenGL выполняет все свои операции с цветом линейно. Тем не менее, поскольку предполагается, что отсканированное изображение находится в некотором нелинейном цветовом пространстве, это означает, что конечный результат операций рендеринга OpenGL каким-то образом должен быть встроен в экранное цветовое пространство кадрового буфера.
Это где ARB_framebuffer_sRGB
Расширение (которое стало ядром с OpenGL-3) входит в картину, которая представила новые флаги, используемые для настройки оконных пиксельных форматов:
New Tokens
Accepted by the <attribList> parameter of glXChooseVisual, and by
the <attrib> parameter of glXGetConfig:
GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20B2
Accepted by the <piAttributes> parameter of
wglGetPixelFormatAttribivEXT, wglGetPixelFormatAttribfvEXT, and
the <piAttribIList> and <pfAttribIList> of wglChoosePixelFormatEXT:
WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9
Accepted by the <cap> parameter of Enable, Disable, and IsEnabled,
and by the <pname> parameter of GetBooleanv, GetIntegerv, GetFloatv,
and GetDoublev:
FRAMEBUFFER_SRGB 0x8DB9
Так что если у вас есть окно, настроенное с таким форматом пикселей sRGB, и включите режим растеризации sRGB в OpenGL с помощью glEnable(GL_FRAMEBUFFER_SRGB);
результат операций рендеринга линейного цветового пространства будет преобразован в цветовое пространство sRGB.
Другим способом было бы преобразовать все в FBO вне экрана и преобразовать цвета в шейдере постобработки.
Но это только выходная сторона рендеринга сигнальной цепочки. Вы также получили входные сигналы в виде текстур. И это обычно изображения с их значениями пикселей, хранящимися нелинейно. Поэтому, прежде чем их можно будет использовать в линейных операциях с изображениями, такие изображения должны быть сначала приведены в линейное цветовое пространство. Давайте пока просто игнорируем, что отображение нелинейных цветовых пространств в линейные цветовые пространства открывает для себя несколько банок червей - вот почему цветовое пространство sRGB настолько смехотворно мало, а именно, чтобы избежать этих проблем.
Так что для решения этого расширения EXT_texture_sRGB
был представлен, который оказался настолько жизненно важным, что он никогда не был ARB, а шел прямо в саму спецификацию OpenGL: вот GL_SRGB…
внутренние форматы текстур.
Текстура, загруженная с этим форматом, подвергается преобразованию цветового пространства sRGB в линейное RGB, а затем используется для получения исходных образцов. Это дает линейные значения пикселей, подходящие для операций линейного рендеринга, и результат затем может быть корректно преобразован в sRGB при переходе к основному экранному буферу кадров.
Персональная заметка по всей проблеме: представление изображений на экранном кадровом буфере в цветовом пространстве целевого устройства ИМХО является огромным недостатком дизайна. Там нет никакого способа сделать все правильно в такой настройке, не сходя с ума.
Что действительно нужно, так это иметь экранный кадровый буфер в линейном контактном цветовом пространстве; естественным выбором будет CIEXYZ. Операции рендеринга, естественно, будут происходить в том же цветовом пространстве контакта. Выполнение всех графических операций в цветовых пространствах контактов позволяет избежать открытия вышеупомянутых банок червей, связанных с попыткой протолкнуть квадратный колышек с именем linear RGB через нелинейное круглое отверстие с именем sRGB.
И хотя мне не очень нравится дизайн Weston/Wayland, по крайней мере, он дает возможность реально реализовать такую систему отображения, заставляя клиентов отображать, а композитор работает в цветовом пространстве контакта и применяет цветовые профили устройства вывода. на последнем этапе постобработки.
Единственный недостаток контактных цветовых пространств заключается в том, что здесь необходимо использовать глубокий цвет (т. Е. > 12 бит на цветовой канал). На самом деле 8 битов совершенно недостаточно, даже с нелинейным RGB (нелинейность помогает немного скрыть отсутствие ощутимого разрешения).
Обновить
Я загрузил несколько изображений (в моем случае оба изображения.png и.bmp) и исследовал необработанные двоичные данные. Мне кажется, что изображения на самом деле находятся в цветовом пространстве RGB, как будто я сравниваю значения пикселей с помощью программы редактирования изображений с байтовым массивом, который я получаю в своей программе, они идеально совпадают. Так как мой редактор изображений дает мне значения RGB, это будет означать изображение, хранящееся в RGB.
Да, в самом деле. Если где-то в цепочке сигналов применяется нелинейное преобразование, но все значения пикселей не изменяются с изображения на дисплей, то эта нелинейность уже была предварительно применена к значениям пикселей изображения. Это означает, что изображение уже находится в нелинейном цветовом пространстве.
2 - "На большинстве компьютеров эффективное сканирование LUT является линейным! Что это значит, хотя?
Я не уверен, что смогу найти, где эта мысль закончилась в вашем ответе.
Эта мысль подробно рассматривается в следующем разделе, где я показываю, как значения, которые вы помещаете в простой (OpenGL) кадровый буфер, напрямую попадают на монитор без изменений. Идея sRGB заключается в том, чтобы "помещать значения в изображения в точности так, как они отправляются на монитор, и создавать пользовательские дисплеи, соответствующие цветовому пространству sRGB".
Из того, что я могу сказать, поэкспериментировав, все мониторы, которые я тестировал, выводили линейные значения.
Как вы измерили реакцию на сигнал? Использовали ли вы калиброванный измеритель мощности или подобное устройство для измерения интенсивности света, излучаемого монитором в ответ на сигнал? Вы не можете доверять своим глазам, потому что, как и все наши чувства, наши глаза имеют логарифмическую реакцию на сигнал.
Обновление 2
Для меня единственный способ увидеть то, что вы говорите, чтобы быть правдой, это если бы редактор изображений давал мне значения в пространстве sRGB.
Это действительно так. Поскольку управление цветом было добавлено ко всем широко распространенным графическим системам в качестве запоздалой мысли, большинство редакторов изображений редактируют значения пикселей в целевом цветовом пространстве. Обратите внимание, что одним конкретным параметром проектирования sRGB было то, что он должен просто задним числом задавать неуправляемые, прямые цветовые операции передачи значения, как они были (и в основном все еще выполняются) на пользовательских устройствах. Поскольку управление цветом вообще не происходит, значения, содержащиеся в изображениях и управляемые в редакторах, должны быть уже в sRGB. Это работает до тех пор, пока длинные изображения не создаются синтетически в процессе линейного рендеринга; в последующем система рендеринга должна учитывать целевое цветовое пространство.
Я делаю снимок экрана, а затем использую программу для редактирования изображений, чтобы увидеть значения пикселей.
Что, конечно, дает только необработанные значения в буфере сканирования без гаммы LUT и примененной нелинейности дисплея.
Я хотел дать простое объяснение того, что пошло не так в первоначальной попытке, потому что, хотя принятый ответ подробно описывает теорию цветового пространства, на самом деле он не отвечает на это.
Настройка пайплайна была совершенно правильной: использовать для текстур,GL_FRAMEBUFFER_SRGB
(или код пользовательского шейдера) для преобразования обратно в sRGB в конце, и все ваши промежуточные вычисления будут использовать линейный свет.
Последний бит, где вы столкнулись с проблемой. Вам нужен свет с цветом (255, 106, 0), но это цвет sRGB, и вы работаете с линейным светом.Чтобы получить нужный цвет, вам нужно преобразовать этот цвет в линейное пространство так же, какGL_SRGB8_ALPHA8
делает для ваших текстур. В вашем случае это будет свет vec3 с интенсивностью (1, .1441, 0) — это значение после применения гамма-сжатия.