Формат BC6H

Формат BC6H — это формат сжатия текстур, предназначенный для поддержки цветовых пространств с высоким динамическим диапазоном (HDR) в исходных данных.

Сведения о BC6H/DXGI_FORMAT_BC6H

Формат BC6H обеспечивает высокое качество сжатия изображений, использующих три канала цветов HDR, с 16-разрядным значением для каждого цветового канала значения (16:16:16). Нет поддержки альфа-канала.

BC6H указывается следующими значениями перечисления DXGI_FORMAT:

  • DXGI_FORMAT_BC6H_TYPELESS.
  • DXGI_FORMAT_BC6H_UF16. Этот формат BC6H не использует бит знака в 16-разрядных значениях цветового канала с плавающей запятой.
  • DXGI_FORMAT_BC6H_SF16. В этом формате BC6H используется бит знака в 16-разрядных значениях цветового канала с плавающей запятой.

Примечание.

16-разрядный формат с плавающей запятой для цветового канала часто называется "половина" плавающей запятой. Этот формат имеет следующий битовый макет:

Форматировать Макет бита
UF16 (без знака float) 5 экспонентных бит + 11 мантисса бит
SF16 (подписанный с плавающей запятой) 1 знак бит + 5 экспонентных бит + 10 мантисса бит

 

 

Формат BC6H можно использовать для текстур Текстур2D (включая массивы), Текстур3D или текстуры (включая массивы). Аналогичным образом этот формат применяется к любым поверхностям карты MIP, связанным с этими ресурсами.

BC6H использует фиксированный размер блока 16 байт (128 бит) и фиксированный размер плитки 4x4 texels. Как и в предыдущих форматах BC, изображения текстур больше поддерживаемого размера плитки (4x4) сжимаются с помощью нескольких блоков. Это удостоверение адресации применяется также к трехмерным изображениям, картам MIP, кубам и массивам текстур. Все плитки изображения должны иметь одинаковый формат.

Некоторые важные заметки о формате BC6H:

  • BC6H поддерживает денормализацию с плавающей запятой, но не поддерживает INF (бесконечность) и NaN (не число). Исключением является подписанный режим BC6H (DXGI_FORMAT_BC6H_SF16), который поддерживает -INF (отрицательное бесконечность). Обратите внимание, что эта поддержка -INF является лишь артефактом самого формата и не поддерживается кодировщиками этого формата. Как правило, если кодировщики сталкиваются с INF (положительными или отрицательными) или входными данными NaN, они должны \ преобразовывать эти данные в максимально допустимое значение представления, отличное от INF, и сопоставлять NaN с 0 до сжатия.
  • BC6H не поддерживает альфа-канал.
  • Декодатор BC6H выполняет декомпрессию перед выполнением фильтрации текстур.
  • Декомпрессия BC6H должна быть точной; То есть оборудование должно возвращать результаты, идентичные декодатору, описанному в этой документации.

Реализация BC6H

Блок BC6H состоит из битов режима, сжатых конечных точек, сжатых индексов и необязательного индекса секции. Этот формат задает 14 различных режимов.

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

В двух регионе существует 32 возможных секций.

Декодирование формата BC6H

В приведенном ниже псевдокоде показаны шаги для распаковки пикселя (x,y), заданного блоком BC6H 16 байтов.

decompress_bc6h(x, y, block)
{
    mode = extract_mode(block);
    endpoints;
    index;
    
    if(mode.type == ONE)
    {
        endpoints = extract_compressed_endpoints(mode, block);
        index = extract_index_ONE(x, y, block);
    }
    else //mode.type == TWO
    {
        partition = extract_partition(block);
        region = get_region(partition, x, y);
        endpoints = extract_compressed_endpoints(mode, region, block);
        index = extract_index_TWO(x, y, partition, block);
    }
    
    unquantize(endpoints);
    color = interpolate(index, endpoints);
    finish_unquantize(color);
}

В следующей таблице содержатся число битов и значения для каждого из 14 возможных форматов для блоков BC6H.

Режим Индексы секций Секция Цветные конечные точки Биты режима
1 46 бит 5 битов 75 бит (10.555, 10.555, 10.555) 2 бита (00)
2 46 бит 5 битов 75 бит (7666, 7666, 7666) 2 бита (01)
3 46 бит 5 битов 72 бита (11.555, 11.444, 11.444) 5 бит (00010)
4 46 бит 5 битов 72 бита (11.444, 11.555, 11.444) 5 бит (00110)
5 46 бит 5 битов 72 бита (11.444, 11.444, 11.555) 5 бит (01010)
6 46 бит 5 битов 72 бита (9555, 9555, 9555) 5 бит (01110)
7 46 бит 5 битов 72 бита (8666, 8555, 8555) 5 бит (10010)
8 46 бит 5 битов 72 бита (8555, 8666, 8555) 5 бит (10110)
9 46 бит 5 битов 72 бита (8555, 8555, 8666) 5 бит (11010)
10 46 бит 5 битов 72 бита (6666, 6666, 6666) 5 бит (11110)
11 63 бита 0 битов 60 бит (10.10, 10.10, 10.10) 5 бит (00011)
12 63 бита 0 битов 60 бит (11.9, 11.9, 11.9) 5 бит (00111)
13 63 бита 0 битов 60 бит (12.8, 12.8, 12.8) 5 бит (01011)
14 63 бита 0 битов 60 бит (16.4, 16.4, 16.4) 5 бит (01111)

 

Каждый формат в этой таблице можно однозначно определить по битам режима. Первые десять режимов используются для двухрегионных плиток, а битовое поле режима может иметь длину двух или пяти битов. Эти блоки также имеют поля для сжатых конечных точек цвета (72 или 75 битов), секции (5 битов) и индексов секций (46 бит).

Для сжатых конечных точек цвета значения в предыдущей таблице отмечают точность сохраненных конечных точек RGB и количество битов, используемых для каждого значения цвета. Например, в режиме 3 указывается уровень точности цветовой конечной точки 11 и количество битов, используемых для хранения разностных значений преобразованных конечных точек для красных, голубых и зеленых цветов (5, 4 и 4 соответственно). Режим 10 не использует разностное сжатие и вместо этого сохраняет все четыре цветные конечные точки явным образом.

Последние четыре режима блока используются для плиток в одном регионе, где поле режима составляет 5 битов. Эти блоки имеют поля для конечных точек (60 бит) и сжатых индексов (63 бита). Режим 11 (например, режим 10) не использует разностное сжатие, а вместо этого сохраняет оба цвета конечных точек явным образом.

Зарезервированы режимы 10011, 1011, 11011 и 11111 (не показаны). Не используйте их в кодировщике. Если оборудование передается блоками с одним из указанных режимов, результирующий декомпрессованный блок должен содержать все нули во всех каналах, кроме альфа-канала.

Для BC6H альфа-канал должен всегда возвращать значение 1.0 независимо от режима.

Набор секций BC6H

Существует 32 возможных наборов секций для двухрегионной плитки, и которые определены в таблице ниже. Каждый блок 4x4 представляет одну фигуру.

table of bc6h partition sets

В этой таблице наборов секций полужирная и подчеркнутая запись — это расположение индекса исправления для подмножества 1 (которое указывается с одним менее битом). Индекс исправления для подмножества 0 всегда равен индексу 0, так как секционирование всегда упорядочивается таким образом, что индекс 0 всегда находится в подмножестве 0. Порядок секций идет от верхнего левого до нижнего справа, перемещаясь слева направо, а затем сверху вниз.

Формат сжатой конечной точки BC6H

bit fields for bc6h compressed endpoint formats

В этой таблице показаны битовые поля для сжатых конечных точек в качестве функции формата конечной точки с каждым столбцом, указывающим кодировку и каждую строку, указывающую битовое поле. Этот подход занимает 82 бита для двухрегионных плиток и 65 битов для плиток в одном регионе. Например, первые 5 битов для кодировки одного региона [16 4] выше (в частности правый столбец) являются битами m[4:0], следующие 10 битов rw[9:0], и т. д. с последними 6 битами, содержащими bw[10:15].

Имена полей в таблице выше определены следующим образом:

Поле Переменная
m mode
дн. Индекс фигуры
Rw endpt[0]. A[0]
Rx endpt[0]. B[0]
Ry endpt[1]. A[0]
Rz endpt[1]. B[0]
Gw endpt[0]. A[1]
Gx endpt[0]. B[1]
Gy endpt[1]. A[1]
Gz endpt[1]. B[1]
Bw endpt[0]. A[2]
Bx endpt[0]. B[2]
by endpt[1]. A[2]
Bz endpt[1]. B[2]

 

Endpt[i], где имеет значение 0 или 1, относится к 0-му или 1-му набору конечных точек соответственно.

Расширение подписи для значений конечных точек

Для двухрегионных плиток существует четыре значения конечных точек, которые могут быть подписаны расширенными. Endpt[0]. Подпись подписывается только в том случае, если формат подписывается; Другие конечные точки подписываются только в том случае, если конечная точка была преобразована или формат подписывается. Приведенный ниже код демонстрирует алгоритм расширения знака значений двухрегионной конечной точки.

static void sign_extend_two_region(Pattern &p, IntEndpts endpts[NREGIONS_TWO])
{
    for (int i=0; i<NCHANNELS; ++i)
    {
      if (BC6H::FORMAT == SIGNED_F16)
        endpts[0].A[i] = SIGN_EXTEND(endpts[0].A[i], p.chan[i].prec);
      if (p.transformed || BC6H::FORMAT == SIGNED_F16)
      {
        endpts[0].B[i] = SIGN_EXTEND(endpts[0].B[i], p.chan[i].delta[0]);
        endpts[1].A[i] = SIGN_EXTEND(endpts[1].A[i], p.chan[i].delta[1]);
        endpts[1].B[i] = SIGN_EXTEND(endpts[1].B[i], p.chan[i].delta[2]);
      }
    }
}

Для плиток в одном регионе поведение совпадает только с удалением endpt[1].

static void sign_extend_one_region(Pattern &p, IntEndpts endpts[NREGIONS_ONE])
{
    for (int i=0; i<NCHANNELS; ++i)
    {
    if (BC6H::FORMAT == SIGNED_F16)
        endpts[0].A[i] = SIGN_EXTEND(endpts[0].A[i], p.chan[i].prec);
    if (p.transformed || BC6H::FORMAT == SIGNED_F16) 
        endpts[0].B[i] = SIGN_EXTEND(endpts[0].B[i], p.chan[i].delta[0]);
    }
}

Преобразование инверсии для значений конечных точек

Для двухрегиональных плиток преобразование применяет обратное кодировку разницы, добавив базовое значение в концеpt[0]. А до трех других записей для общей сложности 9 операций добавления. На рисунке ниже базовое значение представлено как "A0" и имеет самую высокую точность с плавающей запятой. "A1", "B0" и "B1" являются всеми разностями, вычисляемыми из значения привязки, и эти разностные значения представлены с меньшей точностью. (A0 соответствует endpt[0]. A, B0 соответствует endpt[0]. B, A1 соответствует endpt[1]. A и B1 соответствуют endpt[1].B.)

calculation of transform inversion endpoint values

Для плиток в одном регионе существует только одно разностное смещение, поэтому только 3 операции добавления.

Декомпрессор должен гарантировать, что результаты обратного преобразования не будут переполнены точностью endpt[0].a. В случае переполнения значения, полученные из обратного преобразования, должны быть упаковываются в одно и то же количество битов. Если точность A0 равна "p" битам, то алгоритм преобразования:

B0 = (B0 + A0) & ((1 << p) - 1)

Для подписанных форматов результаты вычисления разностного значения также должны быть подписаны. Если операция расширения знака рассматривает расширение обоих признаков, где 0 является положительным и 1 отрицательным, то расширение знака 0 заботится о зажатой выше. Аналогично, после приведенного выше зажима только значение 1 (отрицательное) должно быть продлено.

Некванизация цветных конечных точек

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

  • Некванизация цветовой палитры
  • Интерполяция палитр
  • Отмена финализации

Разделение процесса unquantization на две части (цветовая палитра неquantization до интерполяции и окончательной неквалантизации после интерполяции) уменьшает количество операций умножения, необходимых при сравнении с полным процессом некванизации перед интерполяцией палитры.

Приведенный ниже код иллюстрирует процесс получения оценок исходных 16-разрядных значений цвета, а затем с помощью предоставленных значений веса для добавления в палитру 6 дополнительных значений цвета. Одна и та же операция выполняется на каждом канале.

int aWeight3[] = {0, 9, 18, 27, 37, 46, 55, 64};
int aWeight4[] = {0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64};

// c1, c2: endpoints of a component
void generate_palette_unquantized(UINT8 uNumIndices, int c1, int c2, int prec, UINT16 palette[NINDICES])
{
    int* aWeights;
    if(uNumIndices == 8)
        aWeights = aWeight3;
    else  // uNumIndices == 16
        aWeights = aWeight4;

    int a = unquantize(c1, prec); 
    int b = unquantize(c2, prec);

    // interpolate
    for(int i = 0; i < uNumIndices; ++i)
        palette[i] = finish_unquantize((a * (64 - aWeights[i]) + b * aWeights[i] + 32) >> 6);
}

Следующий пример кода демонстрирует процесс интерполяции со следующими наблюдениями:

  • Так как полный диапазон значений цвета для неквантизируемой функции (ниже) составляет от -32768 до 65535, интерполятор реализуется с помощью 17-разрядной арифметики со знаком.
  • После интерполяции значения передаются в функцию finish_unquantize (описанную в третьем примере в этом разделе), которая применяет окончательное масштабирование.
  • Все аппаратные декомпрессоры должны возвращать точные результаты с этими функциями.
int unquantize(int comp, int uBitsPerComp)
{
    int unq, s = 0;
    switch(BC6H::FORMAT)
    {
    case UNSIGNED_F16:
        if(uBitsPerComp >= 15)
            unq = comp;
        else if(comp == 0)
            unq = 0;
        else if(comp == ((1 << uBitsPerComp) - 1))
            unq = 0xFFFF;
        else
            unq = ((comp << 16) + 0x8000) >> uBitsPerComp;
        break;

    case SIGNED_F16:
        if(uBitsPerComp >= 16)
            unq = comp;
        else
        {
            if(comp < 0)
            {
                s = 1;
                comp = -comp;
            }

            if(comp == 0)
                unq = 0;
            else if(comp >= ((1 << (uBitsPerComp - 1)) - 1))
                unq = 0x7FFF;
            else
                unq = ((comp << 15) + 0x4000) >> (uBitsPerComp-1);

            if(s)
                unq = -unq;
        }
        break;
    }
    return unq;
}

finish_unquantize вызывается после интерполяции палитры. Функция без знака откладывает масштабирование на 31/32 для подписи, 31/64. Это поведение необходимо для получения окончательного значения в допустимый диапазон половины (-0x7BFF ~ 0x7BFF) после завершения интерполяции палитры, чтобы уменьшить количество необходимых умножений. finish_unquantize применяет окончательное масштабирование и возвращает без знака короткое значение, которое повторно интерпретируется на половину.

unsigned short finish_unquantize(int comp)
{
    if(BC6H::FORMAT == UNSIGNED_F16)
    {
        comp = (comp * 31) >> 6;                                         // scale the magnitude by 31/64
        return (unsigned short) comp;
    }
    else // (BC6H::FORMAT == SIGNED_F16)
    {
        comp = (comp < 0) ? -(((-comp) * 31) >> 5) : (comp * 31) >> 5;   // scale the magnitude by 31/32
        int s = 0;
        if(comp < 0)
        {
            s = 0x8000;
            comp = -comp;
        }
        return (unsigned short) (s | comp);
    }
}

Сжатие блоков текстур в Direct3D 11