Шейдер металлических фрагментов - значения цвета уже квантованы до 8 бит?

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

Вот мой шейдер и матрица, которую я использую для дизеринга. Я основал это на этих двух статьях / сообщениях:

Артефакты полос градиента OpenGL

http://www.anisopteragames.com/how-to-fix-color-banding-with-dithering/

var dither_pattern:[UInt8] = 
   [0, 32,  8, 40,  2, 34, 10, 42,   /* 8x8 Bayer ordered dithering  */
    48, 16, 56, 24, 50, 18, 58, 26,  /* pattern.  Each input pixel   */
    12, 44,  4, 36, 14, 46,  6, 38,  /* is scaled to the 0..63 range */
    60, 28, 52, 20, 62, 30, 54, 22,  /* before looking in this table */
    3, 35, 11, 43,  1, 33,  9, 41,   /* to determine the action.     */
    51, 19, 59, 27, 49, 17, 57, 25,
    15, 47,  7, 39, 13, 45,  5, 37,
    63, 31, 55, 23, 61, 29, 53, 21]

// this array is passed to the frag shader via a MTLBuffer

fragment float4 window_gradient_fragment(WindowGradientVertexOut interpolated [[stage_in]],
   const device unsigned char* pattern [[ buffer(0) ]]) {

    int x = (int)interpolated.position.x % 8;
    int y = (int)interpolated.position.y % 8;
    int val = pattern[x+y*8];

    float bayer = 255.0 * (float)val / 64.0;
    const float rgbByteMax = 255.0;
    float4 rgba = rgbByteMax*interpolated.color;
    float4 head = floor(rgba);
    float4 tail = rgba-head;
    float4 color = head+step(bayer,tail);

    return color/255.0;
}

Я проверил это, изменив матрицу на серию чередующихся 0,63 пар, и вблизи она действительно давала слабые вертикальные полосы через каждый второй пиксель. Но в целом полосатость остается прежней, насколько я могу судить. Это заставляет меня думать, что полосатость уже «встроена» к тому времени, когда она достигает фрагментного шейдера. Так что применение дизеринга на самом деле не поможет, потому что ущерб уже нанесен. Я надеюсь, что это неправда ... это было мое наивное предположение, что, поскольку цвета являются плавающими, они будут иметь полную точность с плавающей запятой, и преобразование в 8-битное произойдет после фрагментного шейдера.


person bsabiston    schedule 16.08.2018    source источник
comment
Эти градиенты создаются из-за интерполяции в растеризаторе Metal? Или градиенты создаются где-то еще? Если да, то где и как? Кроме того, вы можете попробовать color = all(rgba == head) ? float4(1, 0, 0, 1) : float4(0, 1, 0, 1) или что-то в этом роде, чтобы напрямую определить, квантуется ли цвет до 8 бит.   -  person Ken Thomases    schedule 17.08.2018
comment
В этом случае они просто создаются интерполяционными цветами Metal, которые указаны для каждой вершины. Завтра попробую твой тест - спасибо.   -  person bsabiston    schedule 17.08.2018
comment
Это маловероятно, но: есть ли шанс, что математика просто так сложится в вашем случае? Подобно тому, как v1.color белый, v2.color черный, и они трансформируются точно на 256 пикселей друг от друга в координатах области просмотра?   -  person Ken Thomases    schedule 17.08.2018
comment
Я действительно не понимаю, что делает этот тест - я никогда не использовал all (), и в документации говорится, что он просто определяет, все ли компоненты аргумента верны. Но это используется иначе. Во всяком случае вижу все зеленое. Относительно вашего второго комментария - если я понимаю ваш вопрос, нет. Это полноэкранный фон размером 1920 пикселей. Ситуация, которую вы описываете, не будет иметь полос, поскольку каждый пиксель будет иметь свой оттенок, верно?   -  person bsabiston    schedule 17.08.2018
comment
Итак, тест, который я предложил: == между двумя векторами дает вектор логических значений, представляющий результаты покомпонентно. all() затем проверяет, все ли они верны. Итак, он проверяет, равны ли векторы для всех компонентов; то есть фактически равны друг другу. Если у вас есть зеленый цвет, это означает, что, по крайней мере, для некоторых фрагментов, какой-то компонент interpolated.color не был квантован до 8 бит. Получение всего зеленого цвета без красного - это немного удивительно. Это говорит о том, что ни один фрагмент не имел цвета, все компоненты которого были целым кратным 1 / 255.0.   -  person Ken Thomases    schedule 17.08.2018
comment
Полагаю, мой тест может быть сорван. Например, сравнение чисел с плавающей запятой на равенство, как правило, плохая идея, хотя это было бы не так, если бы они были квантованы. Вы можете попробовать all(tail < 0.002). Однако я замечаю, что ваша логика кажется неправильной. Я не знаком с техникой дизеринга, но tail находится в [0.0,1.0) по конструкции. step() сравнивает это с bayer, которое находится в [0.0,255.0). Если val равно 1, bayer равно 3,98, что всегда будет больше tail. Итак, только val из 0 изменяет цвет. Я думаю, вы просто хотите не умножать на 255,0 при вычислении bayer.   -  person Ken Thomases    schedule 17.08.2018
comment
Ах да, скорее всего, это была моя ошибка! Я ошибся при адаптации кода по одной из этих ссылок. Он имел дело с фрагментным шейдером OpenGL со значениями 0-255 вместо значений Metal 0-1. Теперь это выглядит намного лучше. По-прежнему есть небольшие полосы по всему экрану, но это огромное улучшение. Я мог бы поэкспериментировать с более крупными паттернами, чтобы увидеть, может ли это улучшить ситуацию дальше. Спасибо!   -  person bsabiston    schedule 17.08.2018


Ответы (1)


Ваша логика кажется неправильной. tail находится в [0.0,1.0) по конструкции. bayer находится в [0.0,255.0) более или менее. step() сравнивает их. Для всех значений val> = 1, bayer> = 3,98 и, следовательно, больше tail. Так что это не очень "дизеризм"; только val из 0 округляет цвет вниз, все остальные округляют в большую сторону.

Я думаю, вы просто не хотите умножать на 255.0 при вычислении bayer.

person Ken Thomases    schedule 17.08.2018