The importance of being linear Перевод
Автор: Bezz | Глава 24. Как важно быть линейным Из книги GPU Gems 3,
Larry Gritz NVIDIA Corporation Eugene d'Eon NVIDIA Corporation
Примечание переводчика: если тема гамма-коррекции для вас совершенно незнакома, то перед прочтением рекомендую ознакомиться
24.1 Вступление
Производительность и прогаммируемость современных GPU позволяет добиваться чрезвычайно реалистичного освещения в реальном времени. Однако неочевидная нелинейность практически каждого устройства для снятия и отображения цифровых изображений требует аккуратного обращения с текстурами и фреймбуферами, чтобы гарантировать корректное отображение всего этого реалистичного освещения. Правильная гамма-коррекция является, вероятно, самым простым, дешевым и широко применяемым методом повышения качества изображения в приложениях, работающих с графикой в реальном времени.
24.2 Свет, Дисплеи и Цветовые Пространства
24.2.1. Проблемы создания, захвата и отображения цифровых изображений
Если Вы заинтересованы в высоком качестве рендера, то могли задаваться вопросом, будут ли цветовые значения изображений, выводимых на бумагу, CRT, LCD-мониторы или пленку восприниматься глазом как реалистичные. Как ни странно, есть аж несколько этапов, на которых все может пойти наперекосяк. Если точнее:
Ответом на этот вопрос будет, как ни странно, - вероятно, нет. В частности, и захват (сканирование, рисование и цифровая фотография), и вывод (на CRT, LCD и проч.) чаще всего происходит нелинейно, что может привести к некорректным и нереалистичным изображениям, если отнестись к этим моментам без надлежащего внимания.
Нелинейность достаточно не очевидна, и поэтому часто без умысла, а иногда и целенаправленно игнорируется, особенно в графике реального времени (real time graphics). Однако влияние нелинейности на рендеринг, в особенности в сценах с широким динамическим диапазоном (with plenty of dynamic range), как на Рисунке 24-1, весьма значительно. Поэтому несколько простых действий, требующихся для коррекции, еще как стоят потраченных усилий.
Рис. 24-1 Плюсы правильной (а) Гамма-Коррекции
24.2.2 Экскурс: Что такое Линейность? C математической точки зрения, линейное преобразование - это такое преобразование, в котором отношение входных и выходных данных представлено следующим образом:
Свет распространяется линейно. Освещение исходящее от двух источников света в сцене суммируется. Не перемножается, не вычитается и не конфликтует друг с другом иным не очевидным образом[1].
24.2.3. Мониторы нелинейны, рендеры линейны Трубчатые мониторы при переводе напряжения в световую интенсивность ведут себя нелинейно. И хотя LCD не унаследовали этой особенности, их чаще всего создают имитирующими поведение CRT экранов.
Отклик монитора ближе к экспоненциальной кривой, показанной на Рис. 24-2. Эта кривая называется гамма. Гамма типичного монитора находится где-то в районе 2.2, что означает,что пиксель 50-ти процентной яркости испускает менее чем четверть света, испускаемого пикселем 100 процентной яркости, а совсем не половину, как вы могли бы ожидать! Для разных устройств гамма разная[2], но обычно колеблется в пределах от 2.0 до 2.4. Правка эффектов этой нелинейной характеристики называется гамма коррекцией.
Рис 24-2. Типичная кривая отклика монитора
Прошу заметить, что вне зависимости от применяемой экспоненты значения черного (ноль) и белого (один) всегда одни и те же. Корректируются и искажаются полутона.
Рендереры, шейдеры и композеры любят работать с линейными данными. Они суммируют результаты множества источников света и умножают значения света на коэффициенты отражения (вроде констант или выборок с текстуры, отвечающей за отражающую способность). Но есть и неявные предположения - вроде того, что текстурная карта, отвечающая за отражающую способность поверхности, тоже представлена в линейном масштабе. Или что при выводе интенсивность света будет пропорциональна значениям, которые рендерер сохранил во фрейм-буфер (frame buffer).
Суть в том, что если у вас входные данные нелинейны, то рендерер, шейдер или композер “все неправильно посчитают”. Это препятствует достижению реалистичных результатов.
Возьмем, к примеру, цифровое рисование. Если во время работы вы смотрели на свое произведение через неправильно настроенную гамму, то картинка будет содержать скрытую нелинейность. Нарисованное может хорошо смотреться на Вашем мониторе, но на чужих мониторах может выглядеть совсем не так, как вы ожидали. То же касается и использования вашего изображения для рендеринга 3д объектов. Рендерер будет использовать предоставленные значения неправильно, если будет считать, что изображение представлено в линейном пространстве. Также, если вы просто возьмете гарантированно линейный результат рендера, и отобразите без гамма-коррекции, картинка получится слишком темной, но темной неравномерно; недостаточно будет просто сделать поярче.
Если ошибиться с обоих концов создания картинки - нарисовать или захватить в гамма-пространстве (нелинейном), линейно отрендерить и затем не скорректировать перед выводом готового изображения, - результат может сперва показаться нормальным. Почему? Потому что нелинейное рисование/захват и нелинейное отображение могут друг друга в какой-то степени взаимоисключить. Но все равно останутся неявные артефакты, вкравшиеся во время рендера и отображения картинки. Такими артефактами могут являться, например, цвета, которые меняются в зависимости от того, осветляется или затемняется нелинейная текстура, ошибочные значения альфа-канала (артефакты при композе); неправильно сгенерированные мипмапы (артефакты текстуринга). Плюс к тому, любые попытки применить реалистичное освещение, вроде high dynamic range (HDR) или imaged-based lighting (IBL, освещение на основе изображения), будут приводить не к тем результатам, что вы ожидаете. Также результаты вашей работы будут по-разному смотреться на разных устройствах, потому что и то, что нарисовано, и что в итоге было отрендерено, содержат в себе, так сказать, отсылку к конкретному монитору, а далеко не факт, что один и тот же монитор будет использоваться при создании текстур и при создании освещения в сцене.
24.3. Симптомы Если игнорировать проблему - рисовать или освещать, используя гамму монитора - можно наблюдать следующие симптомы.
24.3.1. Нелинейные текстуры на входе У среднестатистического пользователя монитор не откалиброван, и вообще этот пользователь никогда и не слышал про гамма-коррекцию. Для примера, во все JPEG файлы встраивается коррекция до гаммы 2.2. Точно не для всех мониторов, но для большинства подходит. Это значит, что JPEG изображения (включая сканы и фотографии с цифровой камеры) не линейны и не должны использоваться как текстурные карты шейдеров, использующих линейные входные данные.
Данный формат откорректирован заранее специально под непосредственное отображение на CRT или LCD дисплеях. Так же при переводе в 8-битный формат оттенки перераспределяются так, что больше “цветового разрешения” добавляется в темных областях, где человеческий глаз чувствительнее к малым вариациям яркости. Однако изображения в данном формате требуется обрабатывать, перед тем, как использовать в любом типе рендера или композа. ----------------------------------------------------
---------------------------------------------------------
24.3.2 Мипмапы Самым очевидным путем создания нового уровня мипмапы для каждого текселя является одна четвертая суммы текселей предыдущего (более высокого) уровня разрешения. Предположим, на самом высоком уровне разрешения присутствует грань: два пикселя яркости 1.0 и два яркости 0.0. Уровнем ниже должно получиться 0.5, не так ли? В половину яркости, так как половина пикселей абсолютно яркие, а половина - абсолютно темные. Так считается мипмапа. Да, но это линейное вычисление. Если у вас изображение в нелинейном пространстве с гаммой, скажем, 2.0, то наш граничный тексель со значением 0.5 будет отображаться лишь с 25-ти процентной яркостью. Поэтому на мипмапах низкого разрешения вы увидите уже не такие малозаметные ошибки. Потому что начнут проявляться ошибки фильтрации верхних уровней разрешения текстуры. Яркость отрендеренных объектов будет меняться вдоль контуров и при удалении от 3д камеры.
Более того, есть еще одна неочевидная проблема, связанная с фильтрацией нелинейных текстур во время GPU (иногда даже CPU) рендеров, даже если вы постарались поправить нелинейности при создании мипмап. GPU использует таблицы поиска по текстуре (texture lookup filter extents), для того, чтобы выбирать уровень детализации текстуры (LOD) он же “уровень мипмапы” и, если необходимо, смешивать два смежных уровня. Надеемся, к этому моменту Вы уже ухватили суть наших доводов и понимаете, что если результат смешения в реальном времени создается с помощью линейных уравнений, а две входные текстуры нелинейны, результатом будут не совсем верные значения яркости цвета. В результате возникнет малозаметная пульсация яркости во время перехода от одного мип уровня к другому.
24.3.3 Освещение Рассмотрим освещение простой ламбертовой сферы, как показано на Рис. 24-3[3]. Если отраженный свет пропорционален N*L, то в точке А на сфере, где N(единичный вектор нормали в точке) и L(единичный вектор направления на источник света) формируют угол в 60 градусов, поверхность будет отражать половину света в сторону камеры. То есть поверхность в точке A будет в половину слабее освещена, чем в точке B, где N направленна прямо на источник света. Если освещенность рассчитывается линейно, то вы увидите объект, который ведет себя как объект реального мира с соответствующими свойствами. Однако если смотреть картинку на мониторе с гаммой 2.0, то A будет только 0.5 в степени gamma или в четверть яркости B.
Рис 24-3 Гамма-откорректированное (а) и неоткорректированное изображение в линейном пространстве (б)
Другими словами, сгенерированные на компьютере материалы и освещение просто не будут совпадать со своими аналогами в реальном мире, если они рендерились с линейными параметрами, а отображаются в нелинейной гамме монитора. Если в целом, то изображение получится темнее. Однако просто поднять яркость на N единиц не достаточно. Яркие места будут более правильными, чем цвета в среднем диапазоне яркости. Границы тени и переходы интенсивности станут острее - например переход от светлого к темному будет быстрее, чем был бы в реальном мире. В углах станет слишком темно. И чем более “продвинутые” техники освещения Вы будете использовать (HDR, global illumination любого вида, подповерхностное рассеивание), тем критичнее будет придерживаться линейного цветового пространства, дабы соответствовать тем линейным вычислениям, которые проводятся Вашей сложной системой освещения и шейдеров. 24.3.4 Дважды неправильно не дает правильно Самой распространенной ошибкой, связанной с гаммой, является рендер с использованием нелинейных текстур без применения гамма коррекции к финальному изображению. Такая сдвоенная ошибка гораздо менее заметна, чем ошибка только в одном из этих моментов, потому что требуемые исправления примерно противоположны друг другу. Однако подобная ситуация создает немало проблем, которых можно было бы легко избежать.
На Рис 24-4 представлено сравнение двух рендеров в реальном времени с применением реалистичного шейдера для кожи. Более подробная информация касательно шейдеров для кожи представлена в 14 главе данной книги,
Рис. 24-4. Рендер с применением необходимой гамма-коррекции (а) и без нее (b)
Изображение справа никаким из перечисленных выше правкам не подвергалось и содержит несколько проблем. Цвет кожи отличается от взятого с фотографий, так как освещение происходило в нелинейном пространстве, и осветление/затемнение цветовых значений ненароком привело к смене оттенков цвета (уровни красного у кожи выше, чем синего и зеленого и потому в другой степени реагируют на свет и тень). Белый спекулярный свет, добавленный к диффузному освещению, становится желтым. Затененные регионы - слишком темными, а подповерхностное рассеяние (в особенности мягкое рассеяние красного в затененных регионах), практически полностью исчезает, смятое кривой гаммы при отображении.
Коррекция освещения в нелинейном пространстве становится довольно проблематичным делом. Если мы возьмем ту же самую сцену, что и на рис 24-4, и отрендерим теперь с повышенной яркостью освещения, проблемы нелинейной версии станут еще хуже, как можно убедиться на рис 24-5. Как правило, если цветовые тона одного и того же объекта меняются при его затемнении или осветлении, то, скорее всего, освещение происходит нелинейно и необходима гамма-коррекция.
Рис 24-5 Рендер с применением гамма коррекции (а) и без (б)
Еще одной частой проблемой, связанной с подповерхностным рассеянием во время рендера кожи (без применения гамма-коррекции), является появление сине-зеленого свечения по краям теней и общая “восковость” кожи, как показано на рис. 24-6. Данные проблемы возникают при настройке параметров рассеяния таким образом, чтобы на краях проявлялось необходимое количество красного (как видно на рис 24-5а). В нелинейном пространстве сложно добиться подобного эффекта, так как требуется применить очень широкое и с большим весом (heavy weighted) размытие интенсивности (irradiance) в красном канале. В результате на освещенных частях лица происходит чересчур много рассеивания (что придает схожести с воском), и это приводит к чрезмерному затемнению красного на краях теней (оставляя сине-зеленое свечение).
Рис 24-6. Настройка подкожного рассеяния в нелинейном пространстве довольно проблематична
24.4 Лекарство Гамма-коррекцией называют процедуру применения трансформации, противоположной той, которую проводит с пикселями изображения монитор перед их непосредственным отображением. Например, мы выводим изображение на монитор, который возведет значение пикселей в степень гамма. Для коррекции этого действия надо предварительно возвести значение пикселя в степень 1/гамма. Тогда эти два действия взаимоисключат друг друга, что в результате даст линейное изображение.(см рис 24-2).
Типичным примером применения можно назвать использование вашей операционной системой цветокорректирующих таблиц отображения (color-correction lookup tables - LUTs), которые включают в себя и гамма-коррекцию. Такая настройка позволяет любому рендерингу, композу или любому иному виду манипуляций с изображением использовать линейные вычисления, а любым линейно созданным сканам, рисункам или отрендеренным изображениям - корректно отображаться.
--------------------------------------------------------------------
--------------------------------------------------------------
Студии анимации и визуальных эффектов, наряду с коммерческими издательствами, очень серьезно подходят к этому процессу. Часто есть отдельный человек, который отвечает за “цветовую политику” (color managment) каждого монитора, программ композа, пленки и результатов видеосканирования (video scanning), а также финального изображения. На деле, для высокоуровневых приложений простой гамма-коррекции недостаточно. Зачастую композеры используют гораздо более “замороченные” трёхмерные таблицы отображения (3D color LUT), полученные путем тщательного измерения индивидуальных дисплеев или спектральных характеристик кинопленки (color-response curves)[4]. Игровые разработчики, в отличие от студий в киноиндустрии, часто допускают ошибки в данном процессе, что приводит к артефактам, описанным в данной главе. Это одна из причин, почему большая часть (но не вся) CG для кино выглядит гораздо лучше, чем в играх. Причина, не имеющая ничего общего с поликаунтом, шейдерами или художественными навыками игроделов (та же причина лежит в основе того, что некоторые в остальном хорошо сделанные CG-эффекты в кино выглядят плохо - потому что невнимательный композер забыл правильно настроить цветовую палитру и применить правильную гамму).
У Васи Обычного Геймера монитор не откалиброван. И мы не знаем, какие LUT подходят под его дисплей, да и в любом случае, нет нужды использовать гамма-корректирующие LUT ко всему дисплею. Почему? потому что тогда обычные JPEG картинки в веб -браузере будут смотреться бледными, и игроку будет непонято, почему (киностудиям же совершенно не важно, если на рабочих станциях художников обычные JPEG из интернета как-то не так смотрятся; лишь бы конечный продукт, собственно фильм, хорошо смотрелся на экране кинотеатра, где у них есть полный контроль над изображением). Однако простой гамма-коррекции достаточно, чтобы на среднестатистическом мониторе все смотрелось хорошо. Оставшуюся часть главы мы посвятим самым простым решениям, направленным на борьбу с вышеописанными артефактами и общее повышение качества картинки в играх, которые вы разрабатываете.
24.4.1 Входные изображения (сканы, рисунки и цифровые фотографии) Большая часть изображений, которые вы захватываете с помощью цифровой фотографии или сканирования, с большой вероятностью уже гамма-откорректированы (особенно если на момент попадания к вам в руки они уже в формате JPEG), а значит их данные - в нелинейном цветовом пространстве (JPEG’и в цифровых фотоаппаратах еще и с повышенной резкостью; старайтесь использовать RAW формат, чтобы избежать сюрпризов). Если вы создавали текстуры без применения LUT, то эти работы, скорее всего, тоже в цветовом пространстве монитора. Если картинка правильно выглядит на вашем мониторе и в веб-браузере, высоки шансы, что она гамма-откорректирована и нелинейна.
Любые входные текстуры, которые уже были гама-откорректированы, должны быть приведены обратно в линейное цветовое пространство перед тем, как использоваться для шэйдинга или композа. Это преобразование необходимо проделывать для тех текстурных значений, которые используются в (линейных) вычислениях освещения. Цветовые значения (такие как яркость света и цвета диффузного отражения), должны всегда переводиться в линейное пространство перед использованием в шейдинге. Альфа-каналы, карты нормалей, дисплейса, и так далее, скорее всего уже и так линейны, и дальше их править не следует, как и любые текстуры, которые были созданы или получены в линейном виде.
Все современные GPU поддерживают sRGB форматы текстур. Такие форматы позволяют привязать информацию о гамма-откорректированных пикселях к текстурам с верной гаммой, которые аппаратно применяются до получения результата шейдинга. У NVIDIA GeForce, начиная с 8-й серии и далее, все выборки с текстуры (samples), используемые в текстурной фильтрации, непосредственно перед самой фильтрацией переводятся в линейное пространство, дабы гарантировать корректный результат (более старые GPU применяют гамма-коррекцию после фильтрации). Применяемая коррекция имеет стандарт IEC (IEC 61966-2-1), что примерно соотносится с гаммой 2.2 и является проверенным вариантом при работе с нелинейной цветовой информацией, если точная кривая гаммы неизвестна. Значения в Альфа канале, если они есть, не корректируются.
Подходящие sRGB версии существуют для всех 8-ми битных форматов текстур (RGB, RGBA, luminance, luminance alpha, DXT compressed) и в OpenGL, и в DirectX. Например, отправляя в переменную glTexImage2D команду GL_SRGB_EXT вместо GL_RGB, мы гарантируем, что любые обращения шейдера к конкретной текстуре будут возвращать линейные значения пикселей.
Автоматические sRGB коррекции ничего не стоят (с точки зрения производительности) и более предпочтительны, чем выполнение коррекций вручную в теле шейдера после каждого обращения к текстуре, как показано в Примере 24-1, потому что каждая инструкция возведения в степень скалярна и расширяется на 2 инструкции. Кроме того, ручная коррекция происходит после фильтрации и в нелинейном пространстве может выполняться некорректно. Форматы sRGB так же могут быть предпочтительней, чем перевод текстур в линейное пространство до их загрузки. Хранение линейных пикселей в 8-ми битном изображении, по сути, приводит к потере степени точности в регионах с низкой интенсивностью и
Пример 24-1. Ручной перевод цветовых значений в линейное пространство Выборки из текстуры сразу могут применять инверсию гамма-коррекции, чтобы остальная часть шейдера уже работала с линейными значениями. Однако использование sRGB текстур работает быстрее, гарантирует корректную линейную фильтрацию (начиная с GeForce 8 ) и не требует дополнительного кода в шейдере.
----------------
----------------- Организация шейдеров, которые должны сочетать линейные и нелинейные данные, может вылиться в жуткую головную боль для программистов движка и художников по текстурам. В большинстве случаев самым простым решением будет просто ввести требование переводить все текстуры в линейное пространство перед использованием в рендеринге. 24.4.2 Выходные изображения (финальный рендер) Гамма-коррекция значений пикселей должна быть самым последним шагом перед их отображением, чтобы при выводе на монитор с нелинейной гаммой, картинка выглядела “правильно”. Указание sRGB фрейм-буфера переносит коррекцию на GPU и не требует изменений в шейдере. Любое значение, полученное шейдером, уже гамма-откорректировано перед попаданием во фрейм-буфер (либо буфер рендера в текстуру). Более того, начиная с версии GeForce 8 и выше, при включенном блендинге ранее сохраненные значения переводятся обратно в линейное пространство непосредственно перед блендингом, а результат подвергается гамма-коррекции. При включенном sRGB буфере значения альфа канала не корректируются. Если sRGB буфер недоступен, можно использовать более дорогостоящее решение с дополнительным кодом в шейдере, как в примере 24-2; однако любой используемый блендинг будет выполняться некорректно.
Пример 24-2. Гамма-коррекция перед самым выводом Если sRGB фрейм-буферы недоступны (либо если пользователь может корректировать гамму в настройках), гамма-коррекцию можно провести при помощи следующего кода: ---------------
-------------
24.4.3 Промежуточные буферы цвета (Color Buffers) Нужно иметь ввиду несколько неочевидных моментов. Если происходит пост-обработка изображения в любом ее виде, гамма-коррекция должна проводиться в самую последнюю очередь. Не стоит рендерить, корректировать, а затем производить дальнейшие вычисления с результатом, как будто это финальный результат.
Если вы рендерите в текстуру, вам стоит либо а) сделать гамма-коррекцию, и затем работать с результатом как с нелинейными данными; либо б) не производить гамма-коррекцию и работать с текстурой, как с линейной. В 8-битных промежуточных цветовых буферах линейные данные по сравнению с гамма-откорректированными теряют часть информации о темных областях. Поэтому может быть выгоднее использовать 16-битные форматы с плавающей точкой, либо sRGB фрейм-буфер и sRGB форматы текстур для рендера и работы с промежуточными цветовыми буферами.
24.5 Заключение OpenGL, DirectX, и любой написанный Вами шейдер, скорее всего, выполняют расчеты, исходя из того, что все текстурные входные данные, взаимодействия свет/материал и результаты всех этих вычислений линейны: то есть световые яркости (intensities) суммируются, отражательные способности (reflectivities) умножаются. Но очень вероятно, что ваши текстурные данные на входе нелинейны, и что почти наверняка ваши пользователи имеют неоткалиброванный монитор с нелинейным откликом. Такой вариант событий приводит ко множеству артефактов и погрешностей, иногда малозаметных (вроде ошибок фильтрации мипмап), а иногда варварски грубых (вроде совершенно неправильного затухания света).
Мы настоятельно рекомендуем разработчикам принять следующие простые правила:
Тщательное следование данным правилам крайне важно для повышения качества картинки в Вашей игре, и в особенности для повышения точности любых расчетов, связанных с освещением или материалами, которые проводятся в Ваших шейдерах.
24.6 Рекомендуем к прочтению В данной главе мы старались давать описания и советы в достаточно простой форме, дабы получился не перегруженный технической информацией текст о том, почему так важно использовать линейное цветовое пространство. По ходу дела что-то мы уж чересчур упростили. Те, кто жаждет кровавых подробностей, могут посмотреть следующее: Статья на википедии по гамма коррекции на удивление хороша: Подробную информацию касательно sRGB форматов в DirectX и OpenGL можно найти на следующих сайтах:
Спасибо Gary King и Mark Kilgard за их знания по sRGB и очень полезные комментарии касательно данной статьи. Отдельное спасибо катеру Doug Jones за разрешение использовать его образ. И, наконец, Рис 24-2 был очень сильно навеян очень похожей диаграммой, которую мы нашли на Википедии. | ||||||||||||||||
Автор: Bezz | Статья старая, но лично я так и не смог найти перевода, так что перевел сам)) Довольно много узнал, пока разбирался, что к чему. Может и еще кому пригодится | ||||||||||||||||
Автор: RIDDICK | Большое спасибо за труд! |
Количество просмотров у этой темы: 6915.
← Предыдущая тема: Перевод статьи Basic Theory of Physically-Based Rendering