Как конвертировать в HDR рендерер?
Я нахожусь в процессе преобразования моего рендерера с отложенным webgl в тот, который использует широкий динамический диапазон. Я много читал на эту тему из различных источников в Интернете, и у меня есть несколько вопросов, которые я надеюсь уточнить. Большая часть чтения, который я сделал, охватывает рендеринг HDR -изображений, но мои вопросы касаются того, как рендереру, возможно, придется измениться для поддержки HDR.
Насколько я понимаю, HDR по сути пытается захватить более высокие световые диапазоны, чтобы мы могли видеть детали как в чрезвычайно освещенных, так и в темных сценах. Обычно в играх мы используем интенсивность 1 для представления белого света и 0 для черного. Но в HDR / реальном мире диапазоны гораздо более разнообразны. Т.е. солнце в двигателе может быть в 10000 раз ярче 10 лампочки.
Чтобы справиться с этими большими диапазонами, вы должны преобразовать свой рендерер в использование целей рендеринга с плавающей запятой (или, в идеале, наполовину с плавающей запятой, поскольку они используют меньше памяти) для проходов света.
Мой первый вопрос по освещению. Помимо целей рендеринга с плавающей запятой, означает ли это, что если раньше у меня был свет, представляющий солнце, которое имело интенсивность 1, то теперь оно могло бы / должно быть представлено как 10000? Т.е.
float spec = calcSpec();
vec4 diff = texture2D( sampler, uv );
vec4 color = diff * max(0.0, dot( N, L )) * lightIntensity + spec; //Where lightIntensity is now 10000?
return color;
Есть ли другие принципиальные изменения в системе освещения (кроме текстур с плавающей точкой и более высоких диапазонов)?
Исходя из этого, теперь у нас есть цель рендеринга с плавающей точкой, которая суммировала все значения освещения (в более высоких диапазонах, как описано). На этом этапе я мог бы сделать некоторую постобработку на цели рендера с такими вещами, как bloom. После завершения он должен быть отображен в тон, прежде чем его можно будет отправить на экран. Это потому, что световые диапазоны должны быть преобразованы обратно в диапазон наших мониторов.
Так что для фазы тонального отображения я бы, вероятно, использовал постобработку, а затем с помощью формулы тонального отображения преобразовал освещение HDR в низкий динамический диапазон. Техникой, которую я выбрал, был Джон Хейблс из Uncharted 2:
const float A = 0.15;
const float B = 0.50;
const float C = 0.10;
const float D = 0.20;
const float E = 0.02;
const float F = 0.30;
const float W = 11.2;
vec3 Uncharted2Tonemap(vec3 x)
{
return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;
}
... // in main pixel shader
vec4 texColor = texture2D(lightSample, texCoord );
texColor *= 16; // Hardcoded Exposure Adjustment
float ExposureBias = 2.0;
vec3 curr = Uncharted2Tonemap( ExposureBias * texColor.xyz );
vec3 whiteScale = 1.0 / Uncharted2Tonemap(W);
vec3 color = curr * whiteScale;
// Gama correction
color.x = pow( color.x, 1.0 /2.2 );
color.y = pow( color.y, 1.0 /2.2 );
color.z = pow( color.z, 1.0 /2.2 );
return vec4( color, 1.0 );
Мой второй вопрос связан с этой фазой тонального отображения. Есть ли что-то большее, чем просто эта техника? Просто использовать более высокую интенсивность света и настроить экспозицию - это все, что нужно считать HDR - или это еще не все? Я понимаю, что в некоторых играх есть функция автоэкспозиции для определения среднего свечения, но нужно ли это на самом базовом уровне? Предположительно, вы можете просто использовать вручную настроить экспозицию?
Что-то еще, что обсуждается во многих документах, это исправление гаммы. Коррекция гаммы, кажется, делается в двух областях. Сначала, когда текстуры читаются, а затем еще раз, когда они отправляются на экран. Когда текстуры читаются, их просто нужно заменить на что-то вроде этого:
vec4 diff = pow( texture2D( sampler, uv), 2.2 );
Затем в вышеупомянутой технике тонального отображения коррекция выходного сигнала выполняется посредством:
pow(color,1/2.2);
Из презентации Джона Хаблса он говорит, что не все текстуры должны быть исправлены таким образом. Диффузные текстуры должны быть, но такие вещи, как карты нормалей, необязательно.
Мой третий вопрос об этой гамма-коррекции. Это необходимо для того, чтобы это работало? Означает ли это, что я должен менять свой движок во всех местах, где читаются диффузные карты?
Это мое текущее понимание того, что связано с этим преобразованием. Это правильно и есть что-то, что я неправильно понял или ошибся?
1 ответ
Расчет / накопление света Да, как правило, вы можете сохранить свой расчет молнии на одном уровне и увеличивать, говоря, что интенсивность направленного света выше 1,0, безусловно, в порядке. Другой способ, которым значение может превысить единицу, - это просто сложить вклады нескольких источников света вместе.
Tone Mapping
Вы, конечно, поняли концепцию. Существует довольно много разных способов сделать фактическое отображение, от более простого / наивного color = clamp(hdrColor * exposure)
на более сложный (и лучший), который вы разместили.
Адаптивное отображение тонов может быстро усложниться. Опять же, наивный способ - просто нормализовать цвета, погружаясь с самым ярким пикселем, что, безусловно, затруднит / сделает невозможным восприятие деталей в более темных частях изображения. Вы также можете усреднить яркость и зажим. Или вы можете сохранить целые гистограммы нескольких последних кадров и использовать их в своем отображении.
Другой способ состоит в том, чтобы нормализовать каждый пиксель только значениями соседних пикселей, то есть "локальным тональным отображением". Это обычно не делается в режиме реального времени рендеринга.
Хотя это может показаться сложным, формула, которую вы опубликовали, даст очень хорошие результаты, поэтому с ней все в порядке. Если у вас есть рабочая реализация, не стесняйтесь экспериментировать здесь. Есть также отличные документы:)
Гамма Гамма-коррекция важна, даже если вы не используете HDR-рендеринг. Но не волнуйтесь, это не сложно.
Самое главное - всегда знать, в каком цветовом пространстве вы работаете. Как и число без единицы, цвет без цветового пространства редко имеет смысл. Теперь нам нравится работать в линейном (rgb) цветовом пространстве в наших шейдерах, то есть цвет с удвоенными значениями rgb должен быть в два раза ярче. Однако это не так, как работают мониторы.
Камеры и программное обеспечение для редактирования фотографий часто просто скрывают все это от нас и просто сохраняют изображения в формате, который нравится монитору (так называемый sRGB).
В sRGB есть дополнительное преимущество - сжатие. Обычно мы сохраняем изображение с 8/16/32 бит на пиксель на канал. Если вы сохраняете изображения в линейном пространстве и у вас есть маленькие, но очень яркие пятна на изображении, ваш бит 8/16/32 может быть недостаточно точным, чтобы сохранить различия яркости в более темных частях изображения и если вы снова их отображаете (из конечно гамма правильная) детали могут быть потеряны в темноте.
Вы можете изменить цветовое пространство, в котором ваши изображения сохраняются во многих камерах и программах, даже если оно иногда немного скрыто. Поэтому, если вы скажете своим художникам сохранять все изображения в линейном (rgb) цветовом пространстве, вам вообще не нужно гамма-исправлять изображения. Поскольку большинство программ, таких как sRGB и sRGB, предлагают лучшее сжатие, обычно рекомендуется сохранять изображения, описывающие цвет, в sRGB, поэтому их необходимо корректировать с помощью гамма-коррекции. Изображения, которые описывают значения / данные, такие как карты нормалей или карты рельефа, обычно сохраняются в линейном цветовом пространстве (если ваш нормальный [1.0, 0.5, 0.0] просто не имеет угла 45 градусов, все будут сбиты с толку; преимущество сжатия также ничто с не-цвета).
Если вы хотите использовать текстуру sRGB, просто скажите об этом OpenGL, и он преобразует ее в линейное цветовое пространство без снижения производительности.
void glTexImage2D( GLenum target,
GLint level,
GLint internalFormat, // Use **GL_SRGB** here
GLsizei width,
GLsizei height,
GLint border,
GLenum format,
GLenum type,
const GLvoid * data);
Да, и, конечно, вам нужно гамма-коррекцию всего, что вы отправляете на дисплей (так что переходите с линейного на sRGB или гамму 2.2). Вы можете сделать это в своем тональном отображении или другом шаге постобработки. Или пусть OpenGL сделает это за вас; увидеть glEnable(GL_FRAMEBUFFER_SRGB)