sRGB colorPixelFormat в Metal на macOS дает странные результаты - PullRequest
0 голосов
/ 09 мая 2018

Справочная информация: я пытаюсь решить эту проблему в iTerm2: https://gitlab.com/gnachman/iterm2/issues/6703#note_71933498

iTerm2 использует формат пикселей по умолчанию MTLPixelFormatBGRA8Unorm для всех своих текстур, включая рисование MTKView. Если на моей машине функция текстуры возвращает цвет C , то приложение «Цифровой измеритель цвета» в «Отображение в sRGB» покажет, что на экране отображается цвет C . Я не знаю, почему это работает! Он должен конвертировать необработанные значения цвета из моей текстурной функции в sRGB, вызывая их изменение, верно? Например, вот что я вижу, когда моя функция фрагмента возвращает (0, 0.16, 0.20, 1) для цвета фона окна:

Proof that a texture with non-sRGB pixel format takes sRGB values from the fragment function

Это странно, но я мог с этим жить, пока кто-то не пожаловался, что он не работает на их машине (см. Проблему, связанную с выше). Для них значение, которое возвращает функция моего фрагмента, проходит через преобразование Generic -> sRGb и выглядит неправильно. Это хакинтош, так что это может быть ошибка драйвера или что-то в этом роде, но это побудило меня изучить это подробнее.

Если я повсеместно меняю формат пикселя на MTLPixelFormatBGRA8Unorm_sRGB, тогда я получаю цвета, которые не могу объяснить:

enter image description here

Функция фрагмента остается неизменной, возвращая (0, 0.16, 0.2, 1). Это рендеринг в текстуру, которую я создал в формате MTLPixelFormatBGRA8Unorm_sRGB пикселей (не для рисования, если это имеет значение, хотя рисование имеет тот же формат пикселей), и я вижу в отладчике графического процессора, что этой текстуре присвоены слишком яркие значения Вы видите выше. На этом снимке экрана цифровой измеритель цвета показывает цвет миниатюры «Промежуточная текстура»:

enter image description here

Вот функция фрагмента (модифицированная для целей отладки):

fragment float4 iTermBackgroundColorFragmentShader(iTermBackgroundColorVertexFunctionOutput in [[stage_in]]) { return float4(0, 0.16, 0.20, 1); }

Я также попытался взломать пример приложения Apple (MetalTexturedMesh), чтобы использовать пиксельный формат sRGB и вернуть тот же цвет из его фрагментного шейдера, и я получил тот же результат. Вот изменение, которое я внес в него . Я могу только заключить, что я не понимаю, как Metal определяет пиксельные форматы, и я не могу найти какой-либо разумной интерпретации, которая дала бы поведение, которое я вижу.

1 Ответ

0 голосов
/ 11 мая 2018

Сырые байты 7C 6E 00 FF в BGRA.Это, конечно, не то, о чем сообщил цифровой измеритель цвета, но это также очень далеко от того, что я ожидаю.

То, что равно , о чем сообщал измеритель цвета.Эта последовательность байтов соответствует (0, 0,43, 0,49).

Я бы ожидал 33 29 00 ff.Так как функция текстуры жестко закодирована в return float4(0, 0.16, 0.20, 1);, а формат пикселей sRGB, я ожидаю, что 0.2 в синем канале даст 0,2 * 255 = 51 (десятичное число) = 0x33, а не 0x7c.

Ваше ожидание неверно.В шейдерах цвета всегда линейные, а не sRGB.Если текстура sRGB, то ее чтение преобразуется из sRGB в линейную, а запись в нее преобразуется из линейной в sRGB.

Учитывая линейный (0, 0,16, 0,2) в вашем шейдере, описывается преобразование в sRGB.в разделе 7.7.7 спецификации Metal Shading Language (PDF) будет получено (0, 0,44, 0,48) для плавающей запятой и (0, 111, 124) aka (0, 0x6f, 0x7c) дляRGBA8Unorm.Это очень близко к тому, что вы получаете.Итак, результат кажется правильным для sRGB.

Алгоритм преобразования из линейного в sRGB, как указано в спецификации:

if (isnan(c)) c = 0.0;
if (c > 1.0)
    c = 1.0;
else if (c < 0.0)
    c = 0.0;
else if (c < 0.0031308)
    c = 12.92 * c;
else
    c = 1.055 * powr(c, 1.0/2.4) - 0.055;

Алгоритм преобразования из sRGB в линейный:

if (c <= 0.04045)
    result = c / 12.92;
else
    result = powr((c + 0.055) / 1.055, 2.4);

Я также вижу, что он явно намного ярче, чем тот же цвет, нарисованный без металла (например, в -drawRect :).

Как вы создали цвет для рисования для этого случая?Обратите внимание, что общее (калиброванное) цветовое пространство RGB не линейное.Имеет гамму ~ 1,8.Базовая графика имеет kCGColorSpaceGenericRGBLinear, которая является линейной.

Загадка заключается в том, почему вы видите намного более темный цвет, когда используете текстуру не-sRGB.Жестко закодированный цвет в вашем шейдере должен всегда представлять один и тот же цвет.Пиксельный формат текстуры не должен влиять на то, как она в конечном итоге проявляется (в пределах ошибки округления).То есть он преобразуется из линейного в пиксельный формат текстуры и, на дисплее, из пиксельного формата текстуры в цветовой профиль дисплея.Это транзитивно, поэтому результат должен быть таким же, как переход от линейного к цветному профилю дисплея.

Вы уверены, что не извлекаете байты из этой текстуры, а затем интерпретируете их, как если бы они были sRGB?Или, может быть, используя -newTextureViewWithPixelFormat:...?

...