다음을 통해 공유


BC6H 형식

BC6H 형식은 원본 데이터에서 HDR(High-Dynamic Range) 색 공간을 지원하도록 설계된 텍스처 압축 형식입니다.

BC6H/DXGI_FORMAT_BC6H 정보

BC6H 형식은 3개의 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(부호 있는 float) 1개의 부호 비트 + 5개의 지수 비트 + 10개의 가수 비트

 

 

BC6H 형식은 Texture2D(배열 포함), Texture3D 또는 TextureCube(배열 포함) 텍스처 리소스에 사용할 수 있습니다. 마찬가지로 이 형식은 이러한 리소스와 연결된 모든 MIP 맵 화면에 적용됩니다.

BC6H는 16바이트(128비트)의 고정 블록 크기와 4x4 텍셀의 고정 타일 크기를 사용합니다. 이전 BC 형식과 마찬가지로 지원되는 타일 크기(4x4)보다 큰 질감 이미지는 여러 블록을 사용하여 압축됩니다. 이 주소 지정 ID는 3차원 이미지, MIP 맵, 큐브맵 및 텍스처 배열에도 적용됩니다. 모든 이미지 타일은 동일한 형식이어야 합니다.

BC6H 형식에 대한 몇 가지 중요한 참고 사항:

  • BC6H는 부동 소수점 비정규화를 지원하지만 INF(무한대) 및 NaN(숫자가 아님)을 지원하지 않습니다. 예외는 -INF(음의 무한대)를 지원하는 BC6H(DXGI_FORMAT_BC6H_SF16)의 서명된 모드입니다. -INF 대한 이 지원은 형식 자체의 아티팩트일 뿐이며 이 형식에 대한 인코더에서 특별히 지원되지 않습니다. 일반적으로 인코더가 INF(양수 또는 음수) 또는 NaN 입력 데이터가 발견되면 해당 데이터를 허용되는 최대 비 INF 표현 값으로 변환하고 압축 전에 NaN을 0으로 매핑해야 합니다.
  • BC6H는 알파 채널을 지원하지 않습니다.
  • BC6H 디코더는 텍스처 필터링을 수행하기 전에 압축 해제를 수행합니다.
  • BC6H 압축 해제는 비트 정확해야 합니다. 즉, 하드웨어는 이 설명서에 설명된 디코더와 동일한 결과를 반환해야 합니다.

BC6H 구현

BC6H 블록은 모드 비트, 압축된 엔드포인트, 압축된 인덱스 및 선택적 파티션 인덱스로 구성됩니다. 이 형식은 14가지 모드를 지정합니다.

엔드포인트 색은 RGB 트리플렛으로 저장됩니다. BC6H는 정의된 여러 색 엔드포인트에서 대략적인 선에 색 팔레트를 정의합니다. 또한 모드에 따라 타일을 두 영역으로 나누거나 단일 영역으로 처리할 수 있습니다. 여기서 두 지역 타일에는 각 지역에 대해 별도의 색 엔드포인트 집합이 있습니다. BC6H는 텍셀당 하나의 색상표 인덱스만 저장합니다.

두 지역 사례에는 32개의 가능한 파티션이 있습니다.

BC6H 형식 디코딩

아래 의사 코드는 16바이트 BC6H 블록이 지정된 경우 (x,y)에서 픽셀의 압축을 푸는 단계를 보여 줍니다.

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);
}

다음 표에는 BC6H 블록에 대해 가능한 14개 형식 각각에 대한 비트 수와 값이 포함되어 있습니다.

모드 파티션 인덱스 파티션 색 엔드포인트 모드 비트
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)

 

이 테이블의 각 형식은 모드 비트로 고유하게 식별할 수 있습니다. 처음 10개 모드는 2개 영역 타일에 사용되며 모드 비트 필드는 2비트 또는 5비트 길이일 수 있습니다. 이러한 블록에는 압축된 색 엔드포인트(72비트 또는 75비트), 파티션(5비트) 및 파티션 인덱스(46비트)에 대한 필드도 있습니다.

압축된 색 엔드포인트의 경우 이전 표의 값은 저장된 RGB 엔드포인트의 전체 자릿수와 각 색 값에 사용되는 비트 수를 기록합니다. 예를 들어 모드 3은 색 엔드포인트 정밀도 수준 11을 지정하고, 빨간색, 파랑 및 녹색 색(각각 5, 4 및 4)에 대해 변환된 엔드포인트의 델타 값을 저장하는 데 사용되는 비트 수를 지정합니다. 모드 10에서는 델타 압축을 사용하지 않고 네 가지 색 엔드포인트를 모두 명시적으로 저장합니다.

마지막 4개의 블록 모드는 모드 필드가 5비트인 한 지역 타일에 사용됩니다. 이러한 블록에는 엔드포인트(60비트) 및 압축된 인덱스(63비트)에 대한 필드가 있습니다. 모드 11(예: 모드 10)은 델타 압축을 사용하지 않고 대신 두 색 엔드포인트를 명시적으로 저장합니다.

모드 10011, 10111, 11011 및 11111(표시되지 않음)은 예약되어 있습니다. 인코더에서 사용하지 마세요. 하드웨어가 이러한 모드 중 하나를 지정한 블록으로 전달되는 경우 압축 해제된 블록은 알파 채널을 제외한 모든 채널의 모든 0을 포함해야 합니다.

BC6H의 경우 알파 채널은 모드에 관계없이 항상 1.0을 반환해야 합니다.

BC6H 파티션 집합

두 지역 타일에 대해 32개의 가능한 파티션 집합이 있으며 아래 표에 정의되어 있습니다. 각 4x4 블록은 단일 도형을 나타냅니다.

bc6h 파티션 집합의 테이블

이 파티션 집합 테이블에서 굵게 표시되고 밑줄이 그은 항목은 하위 집합 1에 대한 수정 인덱스의 위치입니다(1비트 이하로 지정됨). 인덱스 0이 항상 하위 집합 0에 있도록 분할이 항상 정렬되므로 하위 집합 0에 대한 수정 인덱스는 항상 인덱스 0입니다. 파티션 순서는 왼쪽 위에서 오른쪽 아래로 이동하고 왼쪽에서 오른쪽으로 이동한 다음 위에서 아래로 이동합니다.

BC6H 압축 엔드포인트 형식

bc6h 압축 엔드포인트 형식에 대한 비트 필드

이 표에서는 압축된 엔드포인트의 비트 필드를 엔드포인트 형식의 함수로 보여 줍니다. 각 열은 인코딩을 지정하고 각 행은 비트 필드를 지정합니다. 이 방법은 2개 지역 타일의 경우 82비트, 한 지역 타일의 경우 65비트가 소요됩니다. 예를 들어 위의 한 지역 [16 4] 인코딩에 대한 처음 5비트(특히 가장 오른쪽 열)는 비트 m[4:0]이고, 다음 10비트는 비트 rw[9:0]이며, 마지막 6비트에서 bw[10:15]를 포함합니다.

위 표의 필드 이름은 다음과 같이 정의됩니다.

변수
m 모드
d 셰이프 인덱스
rw endpt[0]. A[0]
rx endpt[0]. B[0]
endpt[1]. A[0]
rz endpt[1]. B[0]
gw(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]
곁에 endpt[1]. A[2]
bz endpt[1]. B[2]

 

0 또는 1인 Endpt[i]는 각각 0번째 또는 첫 번째 엔드포인트 집합을 참조합니다.

엔드포인트 값에 대한 서명 확장

두 지역 타일의 경우 확장된 서명할 수 있는 4개의 엔드포인트 값이 있습니다. Endpt[0]. A는 형식이 서명된 형식인 경우에만 서명됩니다. 다른 엔드포인트는 엔드포인트가 변환되었거나 형식이 서명된 형식인 경우에만 서명됩니다. 아래 코드는 두 지역 엔드포인트 값의 기호를 확장하기 위한 알고리즘을 보여 줍니다.

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]);
    }
}

엔드포인트 값에 대한 반전 변환

두 지역 타일의 경우 변환은 차이 인코딩의 역방향을 적용하여 endpt[0]에 기본 값을 추가합니다. 총 9개의 추가 작업에 대한 세 개의 다른 항목에 대한 A입니다. 아래 이미지에서 기본 값은 "A0"으로 표시되고 가장 높은 부동 소수점 정밀도를 가집니다. "A1", "B0" 및 "B1"은 모두 앵커 값에서 계산된 델타이며 이러한 델타 값은 더 낮은 정밀도로 표시됩니다. (A0은 endpt[0]에 해당합니다. A, B0은 endpt[0]에 해당합니다. B, A1은 endpt[1]에 해당합니다. A 및 B1은 endpt[1].B.)에 해당합니다.

변환 반전 엔드포인트 값의 계산

한 지역 타일의 경우 델타 오프셋이 하나뿐이므로 3개 추가 작업만 있습니다.

압축 해제기는 역 변환의 결과가 endpt[0].a의 정밀도를 오버플로하지 않도록 해야 합니다. 오버플로의 경우 역 변환에서 발생하는 값은 동일한 비트 수 내에서 래핑되어야 합니다. A0의 전체 자릿수가 "p" 비트이면 변환 알고리즘은 다음과 같습니다.

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

서명된 형식의 경우 델타 계산의 결과도 확장된 기호여야 합니다. 기호 확장 연산에서 0이 양수이고 1이 음수인 두 기호를 모두 확장하는 것을 고려하면 0의 기호 확장이 위의 클램프를 처리합니다. 마찬가지로 위의 클램프 후에는 1(음수) 값만 확장해야 합니다.

색 엔드포인트의 시퀀스 해제

압축되지 않은 엔드포인트가 있는 경우 다음 단계는 색 엔드포인트의 초기 정수화를 수행하는 것입니다. 여기에는 다음 세 단계가 포함됩니다.

  • 색상표의 시퀀스 해제
  • 색상표 보간
  • Unquantization finalization

정량화 프로세스를 두 부분으로 분리하면(보간 전 색상표 정량화 및 보간 후 최종 정량화되지 않음) 색상표 보간 전의 전체 정량화 프로세스와 비교할 때 필요한 곱하기 작업의 수가 줄어듭니다.

아래 코드에서는 원래 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);
}

다음 코드 샘플은 다음과 같은 관찰과 함께 보간 프로세스를 보여 줍니다.

  • unquantize 함수(아래)의 전체 색 값 범위는 -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 색상표 보간 후에 호출됩니다. 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 텍스처 블록 압축