HDR, адаптивное тональное отображение и MSAA в GLSL - PullRequest
6 голосов
/ 01 марта 2011

Стремясь научить себя OpenGL, я пробираюсь через 5-е издание Superbible .

В настоящее время я пытаюсь выяснить, как объединить HDR и MSAA (как описано в главе 9).

Для HDR в книге предлагается метод адаптивного тонального отображения, основанный на расчете средней яркости для сверточного фильтра 5x5 для каждого фрагмента.

Для MSAA используемый метод усредняет все выборки по весам, рассчитанным по расстоянию выборки.

Моя попытка объединить оба, найденная в пастбине ниже, применяет тональное отображение к каждому семплу, а затем усредняет их для вычисления окончательного цвета фрагмента.

Производительность (как и следовало ожидать?) Ужасна: при 25 поисках на выборку, 4 раза для 4xMSAA, я предполагаю, что GPU тратит большую часть своего времени на поиск текстуры FBO. Переключение на путь кода, управляемый униформой use_HDR в коде, снижает производительность на 400 + fps до 10 для простой сцены.

У меня двоякий вопрос:

  • это нормальный метод выполнения тонального отображения? Если нет, что бы вы предложили?

  • как объединять фильтры MSAA и свертки? Я предполагаю, что у меня снова возникнет эта проблема для любого фильтра, который должен искать соседние тексели, т. Е. Почти все, что угодно, например, цветение, размытие и т. Д.

Код:

#version 330
in Data
{
    vec4 position;
    vec4 normal;
    vec4 color;
    vec2 texCoord;
    mat4 mvp;
    mat4 mv;
} gdata;

out vec4 outputColor;
uniform sampler2DMS tex;
uniform sampler1D lum_to_exposure;
uniform samplerBuffer weights;
uniform int samplecount;
uniform bool use_HDR;

vec4 tone_map(vec4 color, float exp)
{
    return 1.0f - exp2(-color * exp);
}

const ivec2 tc_offset[25] = ivec2[](ivec2(-2, -2), ivec2(-1, -2), ivec2(0, -2), ivec2(1, -2), ivec2(2, -2),
                                    ivec2(-2, -1), ivec2(-1, -1), ivec2(0, -1), ivec2(1, -1), ivec2(2, -1),
                                    ivec2(-2,  0), ivec2(-1,  0), ivec2(0,  0), ivec2(1,  0), ivec2(2,  0),
                                    ivec2(-2,  1), ivec2(-1,  1), ivec2(0,  1), ivec2(1,  1), ivec2(2,  1),
                                    ivec2(-2,  2), ivec2(-1,  2), ivec2(0,  2), ivec2(1,  2), ivec2(2,  2));

void main()
{
    ivec2 itexcoords = ivec2(floor(textureSize(tex) * gdata.texCoord));
    float tex_size_x = textureSize(tex).x;
    float tex_size_y = textureSize(tex).y;
    outputColor = vec4(0.0f, 0.0f, 0.0f, 1.0f);
    // for each sample in the multi sample buffer...
    for (int i = 0; i < samplecount; i++)
    {
        // ... calculate exposure based on the corresponding sample of nearby texels
        vec4 sample;
        if (use_HDR)
        {
            sample = texelFetch(tex, itexcoords, i);

            // look up a 5x5 area around the current texel
            vec4 hdr_samples[25];
            for (int j = 0; j < 25; ++j)
            {
                ivec2 coords = clamp(itexcoords + tc_offset[j], ivec2(0, 0), ivec2(tex_size_x, tex_size_y));
                hdr_samples[j] = texelFetch(tex, coords, i);
            }
            // average the surrounding texels
            vec4 area_color = (
                     ( 1.0f * (hdr_samples[0] + hdr_samples[4] + hdr_samples[20] + hdr_samples[24])) +
                     ( 4.0f * (hdr_samples[1] + hdr_samples[3] + hdr_samples[5] + hdr_samples[9]
                             + hdr_samples[15] + hdr_samples[19] + hdr_samples[21] + hdr_samples[23])) +
                     ( 7.0f * (hdr_samples[2] + hdr_samples[10] + hdr_samples[14] + hdr_samples[22])) +
                     (16.0f * (hdr_samples[6] + hdr_samples[8] + hdr_samples[16] + hdr_samples[18])) +
                     (26.0f * (hdr_samples[7] + hdr_samples[11] + hdr_samples[13] + hdr_samples[17])) +
                     (41.0f * (hdr_samples[12]))
                     ) / 273.0f;
            // RGB to luminance formula : lum = 0.3R + 0.59G + 0.11B
            float area_luminance = dot(area_color.rgb, vec3(0.3, 0.59, 0.11));
            float exposure = texture(lum_to_exposure, area_luminance/2.0).r;
            exposure = clamp(exposure, 0.02f, 20.0f);


            sample = tone_map(sample, exposure);
        }
        else
            sample = texelFetch(tex, itexcoords, i);

        // weight the sample based on its position
        float weight = texelFetch(weights, i).r;
        outputColor += sample * weight;
    }
}

Ответы [ 2 ]

5 голосов
/ 02 марта 2011

У меня нет копии Superbible, поэтому я не знаю их точного предложения, но этот подход кажется очень неэффективным и неточным: ваш фильтр 5x5 только получает доступ к 'i'th образец каждого texel, и полностью пропускает другие образцы.

Для фазы фильтрации я бы пошел, как уже предлагал kvark, для разрешения в другой текстуре, используя glBlitFramebuffer, чтобы все сэмплы накапливались в HDR. После этого выполните фильтрацию в другой HDR-текстуре, возможно, используя отделяемый фильтр для повышения производительности, или даже используя аппаратное обеспечение графического процессора для дальнейшего увеличения производительности, используя билинейную фильтрацию .

Это дало бы вам размытую текстуру, которую вы могли бы затем сэмплировать в своем шейдерном картографическом тоне. Это должно значительно повысить производительность, но использовать больше памяти.

Обратите внимание, что существуют другие операторы тонального отображения, и что в этой области нет «наземной правды». Вы можете выбрать более эффективный подход, не используя такую ​​детальную оценку светимости.

Вы можете посмотреть на недавнее сообщение в блоге Мэтта Петтинео о тональном отображении, это может дать вам советы о том, как улучшить вещи, возможно, с помощью glGenerateMipMaps для создания текстуры яркости.

Что касается конкретных проблем, связанных с тональным отображением с помощью MSAA, единственное, что мне известно, - это то, что рекомендуется тонировать карту отдельных сэмплов до того, как MSAA разрешит , для предотвращения появления артефактов сглаживания.

2 голосов
/ 01 марта 2011

Насколько я вижу из вашего кода GLSL, вес для всех выборок пикселя одинаков. Из этого я делаю вывод, что код интересует сумма этих выборок для каждого пикселя. Сумма представляет собой среднее значение, умноженное на количество выборок. Отсюда как минимум две методики оптимизации. Оба используют промежуточную текстуру с одной выборкой, из которой ваш код должен сэмплировать вместо оригинальной сэмплированной:

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

  2. (приближается быстро). Позвольте промежуточной текстуре быть только разрешенной оригинальной. Это можно сделать, позвонив по номеру glBlitFramebuffer(). Это даст немного другой результат (потому что выборки расположены не на сетке), но для вашей задачи - HDR - это не должно иметь значения, поскольку все это в значительной степени приблизительно:)

Удачи!

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