Понимание сглаживания изображений и как они помогают смешивать CSM - PullRequest
1 голос
/ 15 апреля 2020

Так что я буду sh, чтобы реализовать сглаживание как режим наложения между моими каскадными разбиениями карты теней.

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

Теперь из этого видео я понимаю, как рассчитать, какой цвет будет отображать мой глаз см. на основе веса шаблона дизеринга. Что я не понимаю, так это то, как мы берем изображение с данными в 4 байта пикселей и, например, пытаемся сопоставить его с данными 1 байта пикселей. Как можно сопоставить каждый цвет пикселя в исходном изображении с шаблоном сглаживания, для которого его средневзвешенное значение будет выглядеть так, как если бы оно было исходным цветом, если мы в основном ограничены? Скажем, мы были ограничены только 5 цветами, я предполагаю, что не каждая возможная средневзвешенная комбинация шаблона дизеринга, использующая эти 5 цветов паллет, может привести к исходному цвету пикселя, так как же этого можно достичь? Также рассчитывается ли шаблон сглаживания для каждого пикселя, чтобы получить размытое изображение?

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

float GetDither2(ivec2 p)
{
    float d = 0.0;

    if((p.x & 1) != (p.y & 1))
        d += 2.0;
    if((p.y & 1) == 1)
        d += 1.0;

    d *= 0.25;

    return d;
}

float GetDither4(ivec2 p)
{
    float d = GetDither2(p);
    d = d * 0.25 + GetDither2(p >> 1);
    return d;
}

float threshold = GetDither4(ivec2(gl_FragCoord.xy));

if(factor <= threshold)
{
    // sample current cascade
}
else
{
    // sample next cascade
}

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

Был бы признателен за хорошее объяснение этого ?

РЕДАКТИРОВАТЬ:

Хорошо, я вижу корреляцию между алгоритмом я был предоставляется статья в Википедии о упорядоченном дизеринге, которая, насколько я понимаю, является предпочтительным алгоритмом дизеринга, потому что согласно статье:

Кроме того, потому что местоположение дизеринга шаблоны всегда остаются одинаковыми по отношению к кадру дисплея, они менее подвержены дрожанию, чем методы диффузии ошибок, что делает их пригодными для анимации.

Теперь я вижу, что код пытается получить это пороговое значение для данная пространственная координата, хотя мне кажется, что она немного ошибочна, потому что следующий расчет порога следующий: Mpre(i,j) = (Mint(i,j)+1) / n^2

И его необходимо установить: float d = 1.0 вместо float d = 0.0, если Im не ошибаюсь. Во-вторых, я не уверен, как сдвиг влево пространственной координаты ivec2 (я даже не уверен, каково поведение побитового сдвига на векторе в glsl…), но я предполагаю, что это просто побитовая операция компонента, и я попробовал подключаемый модуль (вычисление головы) для данной пространственной координаты (2,1) (согласно моим предположениям о побитовой операции) и получил другой пороговый результат для того, каким должно быть пороговое значение этой позиции в матрице Байера 4x4.

Итак Я скептически настроен c по поводу того, насколько хорошо этот код реализует упорядоченный алгоритм сглаживания.

Во-вторых, я до сих пор не уверен, как это пороговое значение связано с выбором карты теней 1 или 2, а не с просто уменьшая цветовую палитру данного пикселя, эта логика c еще не пришла мне в голову, так как я не понимаю использования порогового значения сглаживания для заданной пространственной координаты, чтобы выбрать правильную карту для выборки.

Наконец, выбор пространственной координаты вызовет дрожание? Данный фрагмент в мировом положении (x,y,z) кто в тени. Для данного фрагмента пространственные координаты для данного кадра равны (i,j). Если камера перемещается, не изменится ли эта пространственная координата фрагмента, связанная с изменением, и порог дизеринга, рассчитанный для этого фрагмента, изменится с каждым движением, вызывающим дрожание шаблона дизеринга?

EDIT2: Попытка смешивания карты, как следует, хотя результат выглядит не очень хорошо, какие-либо идеи?

const int indexMatrix8x8[64] = int[](
    0, 32, 8, 40, 2, 34, 10, 42,
    48, 16, 56, 24, 50, 18, 58, 26,
    12, 44, 4, 36, 14, 46, 6, 38,
    60, 28, 52, 20, 62, 30, 54, 22,
    3, 35, 11, 43, 1, 33, 9, 41,
    51, 19, 59, 27, 49, 17, 57, 25,
    15, 47, 7, 39, 13, 45, 5, 37,
    63, 31, 55, 23, 61, 29, 53, 21
);

for (int i = 0; i < NR_LIGHT_SPACE; i++) {
        if (fs_in.v_FragPosClipSpaceZ <= u_CascadeEndClipSpace[i]) {
            shadow = isInShadow(fs_in.v_FragPosLightSpace[i], normal, lightDirection, i) * u_ShadowStrength;
                int x = int(mod(gl_FragCoord.x, 8));
                int y = int(mod(gl_FragCoord.y, 8));
                float threshold = (indexMatrix8x8[(x + y * 8)] + 1) / 64.0;
                if (u_CascadeBlend >= threshold)
                {
                    shadow = isInShadow(fs_in.v_FragPosLightSpace[i + 1], normal, lightDirection, i + 1) * u_ShadowStrength;
                }
            }
            break;
        }
    }

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

Вот результаты: enter image description here В большем красном поле находится разделение между картами. Красное поле меньшего размера показывает, что существует некоторая картина дизеринга, но изображение не настолько смешано, как мне кажется.

Ответы [ 2 ]

1 голос
/ 16 апреля 2020

Прежде всего, я ничего не знаю о CSM, поэтому я сосредоточен на сглаживании и смешивании. Сначала посмотрите на это:

Они в основном отвечают на ваш вопрос о том, как рассчитать шаблон / пиксели дизеринга.

Также важно иметь хорошую палитру для дизеринга, которая уменьшает ваши 24/32 бит / с в 8 bpp (или меньше). Существует 2 базовых c подхода

  1. уменьшение цветов (квантование цветов)

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

  2. палитра сглаживания

    Дизеринг использует усреднение пикселей для генерации желаемого цвета, поэтому нам нужно иметь такие цвета, которые могут генерировать все возможные цвета, которые мы хотим. Поэтому хорошо иметь несколько (2..4) оттенков каждого базового цвета (R, G, B, C, M, Y) и несколько (> = 4) оттенков серого. Из них вы можете комбинировать любой цвет и интенсивность по вашему желанию (если у вас достаточно пикселей)

# 1 является лучшим, но это для каждого изображения, так что вам нужно рассчитать палитру для каждого изображения. Это может быть проблемой, так как эти вычисления являются неприятными процессорами, требующими много ресурсов процессора. Кроме того, в старых 256 цветовых режимах вы не могли одновременно отображать 2 разные палитры (что с истинным цветом больше не является проблемой), поэтому выбор цвета обычно является лучшим выбором.

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

Чем лучше используемая палитра, тем менее зернистой получается результат ...

Стандартные цветовые палитры VGA 16 и 256 специально разработаны для сглаживания, поэтому рекомендуется использовать их. ...

Стандартная цветовая палитра VGA 16: VGA 16 colors

Стандартная цветовая палитра VGA 256: VGA 256 colors

Здесь также код C ++ для 256 цветов:

//---------------------------------------------------------------------------
//--- EGA VGA pallete -------------------------------------------------------
//---------------------------------------------------------------------------
#ifndef _vgapal_h
#define _vgapal_h
//---------------------------------------------------------------------------
unsigned int vgapal[256]=
    {
    0x00000000,0x00220000,0x00002200,0x00222200,
    0x00000022,0x00220022,0x00001522,0x00222222,
    0x00151515,0x00371515,0x00153715,0x00373715,
    0x00151537,0x00371537,0x00153737,0x00373737,
    0x00000000,0x00050505,0x00000000,0x00030303,
    0x00060606,0x00111111,0x00141414,0x00101010,
    0x00141414,0x00202020,0x00242424,0x00202020,
    0x00252525,0x00323232,0x00303030,0x00373737,
    0x00370000,0x00370010,0x00370017,0x00370027,
    0x00370037,0x00270037,0x00170037,0x00100037,
    0x00000037,0x00001037,0x00001737,0x00002737,
    0x00003737,0x00003727,0x00003717,0x00003710,
    0x00003700,0x00103700,0x00173700,0x00273700,
    0x00373700,0x00372700,0x00371700,0x00371000,
    0x00371717,0x00371727,0x00371727,0x00371737,
    0x00371737,0x00371737,0x00271737,0x00271737,
    0x00171737,0x00172737,0x00172737,0x00173737,
    0x00173737,0x00173737,0x00173727,0x00173727,
    0x00173717,0x00273717,0x00273717,0x00373717,
    0x00373717,0x00373717,0x00372717,0x00372717,
    0x00372525,0x00372531,0x00372536,0x00372532,
    0x00372537,0x00322537,0x00362537,0x00312537,
    0x00252537,0x00253137,0x00253637,0x00253237,
    0x00253737,0x00253732,0x00253736,0x00253731,
    0x00253725,0x00313725,0x00363725,0x00323725,
    0x00373725,0x00373225,0x00373625,0x00373125,
    0x00140000,0x00140007,0x00140006,0x00140015,
    0x00140014,0x00150014,0x00060014,0x00070014,
    0x00000014,0x00000714,0x00000614,0x00001514,
    0x00001414,0x00001415,0x00001406,0x00001407,
    0x00001400,0x00071400,0x00061400,0x00151400,
    0x00141400,0x00141500,0x00140600,0x00140700,
    0x00140606,0x00140611,0x00140615,0x00140610,
    0x00140614,0x00100614,0x00150614,0x00110614,
    0x00060614,0x00061114,0x00061514,0x00061014,
    0x00061414,0x00061410,0x00061415,0x00061411,
    0x00061406,0x00111406,0x00151406,0x00101406,
    0x00141406,0x00141006,0x00141506,0x00141106,
    0x00141414,0x00141416,0x00141410,0x00141412,
    0x00141414,0x00121414,0x00101414,0x00161414,
    0x00141414,0x00141614,0x00141014,0x00141214,
    0x00141414,0x00141412,0x00141410,0x00141416,
    0x00141414,0x00161414,0x00101414,0x00121414,
    0x00141414,0x00141214,0x00141014,0x00141614,
    0x00100000,0x00100004,0x00100000,0x00100004,
    0x00100010,0x00040010,0x00000010,0x00040010,
    0x00000010,0x00000410,0x00000010,0x00000410,
    0x00001010,0x00001004,0x00001000,0x00001004,
    0x00001000,0x00041000,0x00001000,0x00041000,
    0x00101000,0x00100400,0x00100000,0x00100400,
    0x00100000,0x00100002,0x00100004,0x00100006,
    0x00100010,0x00060010,0x00040010,0x00020010,
    0x00000010,0x00000210,0x00000410,0x00000610,
    0x00001010,0x00001006,0x00001004,0x00001002,
    0x00001000,0x00021000,0x00041000,0x00061000,
    0x00101000,0x00100600,0x00100400,0x00100200,
    0x00100303,0x00100304,0x00100305,0x00100307,
    0x00100310,0x00070310,0x00050310,0x00040310,
    0x00030310,0x00030410,0x00030510,0x00030710,
    0x00031010,0x00031007,0x00031005,0x00031004,
    0x00031003,0x00041003,0x00051003,0x00071003,
    0x00101003,0x00100703,0x00100503,0x00100403,
    0x00000000,0x00000000,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x00000000,
    };
//---------------------------------------------------------------------------
class _vgapal_init_class
        {
public: _vgapal_init_class();
        } vgapal_init_class;
//---------------------------------------------------------------------------
_vgapal_init_class::_vgapal_init_class()
        {
        int i;
        BYTE a;
        union { unsigned int dd; BYTE db[4]; } c;
        for (i=0;i<256;i++)
            {
            c.dd=vgapal[i];
            c.dd=c.dd<<2;
            a      =c.db[0];
            c.db[0]=c.db[2];
            c.db[2]=      a;
            vgapal[i]=c.dd;
            }
        }
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
//--- end. ------------------------------------------------------------------
//---------------------------------------------------------------------------

Теперь вернемся к вашему вопросу о смешивании путем сглаживания

Смешивание - это слияние 2 изображения одного и того же разрешения вместе на некоторое количество (вес). Таким образом, цвет каждого пикселя вычисляется следующим образом:

color = w0*color0 + w1*color1;

, где color? - это пиксели в исходных изображениях, а w? - это веса, где все веса вместе составляют до 1:

w0 + w1 = 1;

Вот пример:

и предварительный просмотр (точки смазываются из моего GIF-кодировщика):

Blending

Но смешивание с помощью сглаживания выполняется по-другому. Вместо смешивания цветов мы используем некоторый процент пикселей от одного изображения, а другие от второй. Итак:

if (Random()<w0) color = color0;
 else            color = color1;

Где Random() возвращает псевдослучайное число в диапазоне <0,1>. Как видите, комбинирование цветов не выполняется, просто вы выбираете, из какого изображения вы копируете пиксель ... Вот предварительный просмотр:

Blending by dithering

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

Чтобы ускорить это, обычно нужно предварительно вычислить Random() выходы для некоторого блока (8x8, 16x16, ...) и использовать его для всего изображения (оно немного блочное, но это вроде используется как забавный эффект ...). Таким образом, это может быть сделано также без ответвлений (если вы храните указатели на исходные изображения вместо случайных значений). Также это может быть сделано полностью на целых числах (без фиксированной точности), если веса являются целыми числами, например <0..255> ...

Теперь, чтобы сделать каскад / переход от image0 к image1 или что-то еще просто просто сделайте что-то вроде этого:

for (w0=1.0;w0>=0.0;w0-=0.05)
 {
 w1=1.0-w0;
 render blended images;
 Sleep(100);
 }
render image1;
0 голосов
/ 16 апреля 2020

Я получил смешение дизеринга для работы в моем коде следующим образом:

for (int i = 0; i < NR_LIGHT_SPACE; i++) {
        if (fs_in.v_FragPosClipSpaceZ <= u_CascadeEndClipSpace[i])
        {
            float fade = fadedShadowStrength(fs_in.v_FragPosClipSpaceZ, 1.0 / u_CascadeEndClipSpace[i], 1.0 / u_CascadeBlend);
            if (fade < 1.0) {
                int x = int(mod(gl_FragCoord.x, 8));
                int y = int(mod(gl_FragCoord.y, 8));
                float threshold = (indexMatrix8x8[(x + y * 8)] + 1) / 64.0;
                if (fade < threshold)
                {
                    shadow = isInShadow(fs_in.v_FragPosLightSpace[i + 1], normal, lightDirection, i + 1) * u_ShadowStrength;
                }
                else
                {
                    shadow = isInShadow(fs_in.v_FragPosLightSpace[i], normal, lightDirection, i) * u_ShadowStrength;
                }
            }
            else
            {
                shadow = isInShadow(fs_in.v_FragPosLightSpace[i], normal, lightDirection, i) * u_ShadowStrength;
            }
            break;
        }
}

Сначала проверьте, не приблизились ли мы к каскадному расщеплению по коэффициенту затухания, принимая во внимание пространство отсечения позиции фрагмента и конец пространства клипа для каскада с fadedShadowStrength (я использую эту функцию для нормального смешивания между каскадами, чтобы знать, когда начинать смешивание, в основном, если коэффициент смешивания u_CascadeBlend установлен, например, на 0.1, тогда мы смешиваем, когда мы, по крайней мере, 90% в текущем каскаде (по оси Z).

Затем, если нам нужно затухание (if (fade <1.0)), я просто сравниваю коэффициент затухания с порогом из матрицы и выбираю карту теней соответственно. Результаты: enter image description here

...