ユーザ用ツール

サイト用ツール


ddsformat

DDS ファイルフォーマットの詳細解説

以前↓こちらのページで公開していた DDS ファイルフォーマットの解説を移行しました。また新たに DX10 拡張ヘッダについての説明や、フォーマット判定のサンプルコードを追加しています。

DDS フォーマット

さがしてもさほど情報がなかった DDS フォーマットの 細かいところのメモです。 DDS は DirectDrawSurface の意味です。 もともと DirectX7 以前に使われていた DirectDraw の Surface 構造をそのままファイルに 格納したものだと思われます。

そのため単純な画像フォーマットとしてはやや異質で、 ヘッダには使わないシンボルや変数等が残っています。 ヘッダを見てもよくわからないのですが、 実際に使われるのはそのうちごく一部だけです。 以下このページに載せるのはそのうち使用されると思われる シンボルだけを抜粋しました。

DirectX8 以降 DirectDraw も DirectDrawSurface も無くなりましたが ファイルフォーマットとしての DDS のみ残っています。 DXT1~DXT5 の圧縮形式や CubeMapTexture を格納できること、 また普及具合やツールがそろっている点を考えると結局 DDS が便利なようです。

各チャンネル 8bit 以上 (R G B A それぞれ 10bit~32bit ) のカラー値を取り扱えるフォーマットやツールにはあまり選択肢がありません。 ハード側ではこれら HDR テクスチャを扱えるものの ツールやソフトウエア側では 各チャンネル 8bit ( 32bit A8R8G8B8 ) までのものがほとんどです。

対応ツールが増えると良いな、と思い わかる範囲で今回調べた情報を公開させていただきます。

DDS のヘッダ構造

基本ヘッダは 128byte です。この後ろに DX10 拡張ヘッダ 20byte が挿入される可能性があります。

// 基本ヘッダ構造体 Little-endian
struct T_DDSHEADER {
    DWORD   dwMagic;        // == 常に 0x20534444  ' SDD'
    DWORD   dwSize;         // == 常に 124
    DWORD   dwFlags;        // ヘッダ内の有効な情報 DDSD_* の組み合わせ
    DWORD   dwHeight;       // 画像の高さ x size
    DWORD   dwWidth;        // 画像の幅   y size
    DWORD   dwPitchOrLinearSize;    // 横1 line の byte 数 (pitch)
                                    // または 1面分の byte 数 (linearsize)
    DWORD   dwDepth;        // 画像の奥行き z size (Volume Texture 用)
    DWORD   dwMipMapCount;  // 含まれている mipmap レベル数
    DWORD   dwReserved1[11];
    DWORD   dwPfSize;       // == 常に 32
    DWORD   dwPfFlags;      // pixel フォーマットを表す DDPF_* の組み合わせ
    DWORD   dwFourCC;       // フォーマットが FourCC で表現される場合に使用する。
                            // DX10 拡張ヘッダが存在する場合は 'DX10' (0x30315844) が入る。
    DWORD   dwRGBBitCount;  // 1 pixel の bit 数
    DWORD   dwRBitMask;     // RGB format 時の mask
    DWORD   dwGBitMask;     // RGB format 時の mask
    DWORD   dwBBitMask;     // RGB format 時の mask
    DWORD   dwRGBAlphaBitMask;      // RGB format 時の mask
    DWORD   dwCaps;         // mipmap 等のフラグ指定用
    DWORD   dwCaps2;        // cube/volume texture 等のフラグ指定用
    DWORD   dwReservedCaps[2];
    DWORD   dwReserved2;
};

DX10 (DXT10) 拡張ヘッダ。20byte 。dwFourCC が 'DX10' (0x30315844) の場合に T_DDSHEADER の直後に入ります。

struct T_DDSHEADER_DX10 {
    unsigned int    Format;         // DXGI_FORMAT (dxgiformat.h 参照)
    unsigned int    Dimension;      // D3D10_RESOURCE_DIMENSION が入る。(1D=2, 2D=3, 3D=4)
    unsigned int    MiscFlag;       // 0
    unsigned int    ArraySize;      // Texture Array を格納する場合に必要
    unsigned int    MiscFlag2;      // 0
};
enum {
    DDS10_DIMENSION_1D = 2,
    DDS10_DIMENSION_2D = 3,
    DDS10_DIMENSION_3D = 4,
};

DWORD は unsigned int (4byte)。ヘッダ情報はすべて little endian です。

dwFlags はヘッダ内の有効な情報を表す

シンボル 内容
DDSD_CAPS 0x00000001 dwCaps/dwCpas2 が有効
DDSD_HEIGHT 0x00000002 dwHeight が有効
DDSD_WIDTH 0x00000004 dwWidth が有効。すべてのテクスチャで必須。
DDSD_PITCH 0x00000008 dwPitchOrLinearSize が Pitch を表す
DDSD_PIXELFORMAT 0x00001000 dwPfSize/dwPfFlags/dwRGB/dwFourCC~ 等の Pixel 定義が有効。
DDSD_MIPMAPCOUNT 0x00020000 dwMipMapCount が有効。mipmap を格納する場合は必須。
DDSD_LINEARSIZE 0x00080000 dwPitchOrLinearSize が LinearSize を表す
DDSD_DEPTH 0x00800000 dwDepth が有効。3D (Volume) Texture 時に設定

ヘッダの情報が有効かどうかを判定します。 例えば dwFlags に DDSD_MIPMAPCOUNT flag が立っていなければ、dwMipMapCount の値は無効なものとなります。 ただしすべてのツールが厳密にこのフラグに従っているとは限らないので、このフラグを設定しなかったとしても誤動作しないようにヘッダのフィールドには常に有効な値を設定することをおすすめします。

2D のテクスチャは DDSD_HEIGHT, DDSD_WIDTH が必須となります。 また事実上ファイルフォーマット情報が必要なので、DDSD_PIXELFORMAT は常に On になります。 DDSD_CAPS も常に On であると思って間違いありません。

dwPitchOrLinearSize

dwFlags に DDSD_PITCH または DDSD_LINEARSIZE が含まれている場合に有効になります。あまり重要なフィールドではありません。

DDSD_LINEARSIZE 最初の face の最初の miplevel 0 の画像の byte 数。
DDSD_PITCH 最初の miplevel 0 の画像の横 1列の byte 数。圧縮テクスチャの場合、block 単位の 1列分の byte サイズ。

圧縮テクスチャの場合は pitch には 1列分の block サイズが入ります。 例えば 256×256 の DXT1 なら、横は 256/4 = 64 block となります。 DXT は 1block = 64bit なので pitch = 64block x 64bit / 8bit = 512byte です。 同様に 250×250 のような半端なサイズのテクスチャも、block 単位に切り上げて計算する必要があります。 ( ( 250 + 3) » 2) block x 64bit / 8bit = 504byte

ただし、圧縮テクスチャの場合も結局 MipmapLevel 毎に同様の block 単位のアライメントサイズの再計算を行う必要があるので、実際の実装ではこのフィールドの値が必要になることがありません。

dwMipMapCount

含まれている Mipmap Level 数が入ります。最小値は 1 です。 このフィールドは dwFlags に DDSD_MIPMAPCOUNT が含まれている場合のみ有効です。 もし dwFlags に DDSD_MIPMAPCOUNT が無い場合は、Mipmap Count は常に 1 であるとみなしてください。

ただし dwFlags DDSD_MIPMAPCOUNT を正しく判定していないツールやライブラリが存在する可能性があるため、安全のために常に 1 以上の有効な値を入れて置くことをおすすめします。 また読み出す場合、DDSD_MIPMAPCOUNT が立っていたとしてもこのフィールドが 0 である可能性を考慮した方が良いでしょう。

unsigned int  mip_map_count= (header->dwFlags & DDSD_MIPMAPCOUNT) ? max( header->dwMipMapCount, 1 ) : 1 ;

この項目に 2 以上の値を設定する場合は、dwCaps にも DDSCAPS_MIPMAP を設定して下さい。

dwPfFlags は PixelFormat の有効な情報や形式を表す

シンボル 内容
DDPF_ALPHAPIXELS 0x00000001 RGB 以外に alpha が含まれている。つまり dwRGBAlphaBitMask が有効。
DDPF_ALPHA 0x00000002 pixel は Alpha 成分のみ含まれている。
DDPF_FOURCC 0x00000004 dwFourCC が有効。
DDPF_PALETTEINDEXED4 0x00000008 * Palet 16 colors (DX9 以降はたぶん使用されない)
DDPF_PALETTEINDEXED8 0x00000020 * Palet 256 colors (DX10 以降は使用されない)
DDPF_RGB 0x00000040 dwRGBBitCount/dwRBitMask/dwGBitMask/dwBBitMask/dwRGBAlphaBitMask によってフォーマットが定義されていることを示す
DDPF_LUMINANCE 0x00020000 * 1ch のデータが R G B すべてに展開される
DDPF_BUMPDUDV 0x00080000 * pixel が符号付であることを示す (本来は bump 用) DX10以降は使用しない

上記 '*' が付いた項目はレガシーで DX10 以降の API では使用しません。ただし古いデータを読み込む場合に必要になる場合があります。

本来 dwFlags に DDSD_PIXELFORAMT が設定されている場合に有効なフィールドですが、このフラグが常に有効と思って間違いありません。

dwCaps

シンボル 内容
DDSCAPS_ALPHA 0x00000002 Alpha が含まれている場合 (あまり参照されない)
DDSCAPS_COMPLEX 0x00000008 複数のデータが含まれている場合。Palette/Mipmap/Cubemap/VolumeTexture では On にする。
DDSCAPS_TEXTURE 0x00001000 常に On
DDSCAPS_MIPMAP 0x00400000 MipMap が存在する場合。(dwFlags の DDSD_MIPMAPCOUNT が On でかつ dwMipMapCount が 2以上の場合に On)

dwCaps フラグは実際に含まれる機能を定義します。

dwCaps2

シンボル 内容
DDSCAPS2_CUBEMAP 0x00000200 Cubemap の場合
DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400 Cubemap +X が含まれている
DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800 Cubemap -X が含まれている
DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000 Cubemap +Y が含まれている
DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000 Cubemap -Y が含まれている
DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000 Cubemap +Z が含まれている
DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000 Cubemap -Z が含まれている
DDSCAPS2_VOLUME 0x00400000 VolumeTexture の場合

dwFourCC

dwPfFlags に DDPF_FOURCC が含まれている場合はこのフィールドが有効となります。

  • 圧縮テクスチャなど、特殊なフォーマットの定義
  • HDR 形式など、D3D9 (DX9) 拡張フォーマットの定義
  • DX10 (D3D10) 拡張ヘッダが存在していることを宣言

RGB Bitmask では 32bpp までの非圧縮ピクセルしか表現することが出来ません。 圧縮テクスチャなど、Bitmask で定義できないピクセルデータは dwFourCC を使って定義します。

dwFourCC に入る値は 4文字を組み合わせた ascii 値なので、新たなフォーマットの追加が容易です。 そのためメーカー独自の dwFourCC 拡張コードが用いられる場合があります。 分かる範囲で下記の表にまとめています。 ( こちらの圧縮テクスチャ一覧もあわせてご覧ください。 )

Direct3D 9 拡張フォーマット

Direct3D 9 以降では 4文字 ascii 値ではなく、直接 32bit の数値を格納する場合があります。 この値は d3d9types.h にある D3DFORMAT の enum 定義値そのままです。たとえば R32F の場合 0x72 == D3DFMT_R32F == 114 となります。 HDR フォーマットに対応していない古いツールでは、この Direct3D 9 D3DFORMAT 値を正しく識別できない可能性があります。

Direct3D 10 拡張フォーマット

Direct3D 10 以降使用可能なフォーマットの種類が大幅に増えており、また TextureArray 等の新しい概念も登場しました。 これらの情報に対応するため DDS フォーマットのヘッダが拡張されています。 具体的には下記の手順で判別します。(詳細は後述の判定コードを参考にして下さい)

  1. dwPfFlags の DDPF_FOURCC が有効
  2. dwFourCC の値が 'DX10' ( MAKE_FOURCC( 'D','X','1','0' ) == 0x30315844 )

上記条件をみたす場合は T_DDSHEADER の直後に 20byte の T_DDSHEADER_DX10 が挿入されます。 ピクセルデータの開始位置は sizeof(T_DDSHEADER_DX10) (20byte) の分だけ後ろにずれます。

T_DDSHEADER_DX10 が存在している場合は、ピクセルフォーマットは Format フィールドの DXGI_FORMAT によって定義されます。 TextureArray を用いる場合や、Direct3D 10 以降新たに追加された BC6H, BC7 などの情報を格納する場合は、この DX10 拡張ヘッダが必要になります。

古いツールはこの DX10 拡張ヘッダに対応していない可能性が高いので注意して下さい。 例えば DX11 SDK June2010 に含まれている dxtex.exe は最後まで DX10 ヘッダを識別することが出来ませんでした。

FourCC の定義方法の例

#define	MAKE_FOURCC( x, y, z, w )  (((w)<<24)|((z)<<16)|((y)<<8)|(x))
 
header->dwFourCC= MAKE_FOURCC( 'D', 'X', 'T', '1' );

FourCC の有効な値

FourCC format 解説
'DX10' DXGI_FORMAT有効 DX10 拡張ヘッダが存在していることを意味する
'DXT1' DXT1 DXT1, BC1
'DXT2' DXT2 DXT2
'DXT3' DXT3 DXT3, BC2
'DXT4' DXT4 DXT4
'DXT5' DXT5 DXT5, BC3
'BC4U' BC4 UNORM BC4-U
'BC4S' BC4 SNORM BC4-S
'BC5U' BC5 UNORM BC5-U
'BC5S' BC5 SNORM BC5-S
0x00000024 (36) A16B16G16R16 R16G16B16A16_UNORM
0x0000006e (110) Q16W16V16U16 R16G16B16A16_SNORM
0x0000006f (111) R16F R16_FLOAT
0x00000070 (112) G16R16F R16G16_FLOAT
0x00000071 (113) A16B16G16R16F R16G16B16A16_FLOAT
0x00000072 (114) R32F R32_FLOAT
0x00000073 (115) G32R32F R32G32_FLOAT
0x00000074 (116) A32B32G32R32F R32G32B32A32_FLOAT
0x00000075 (117) CxV8U8
0x0000003f (63) Q8W8V8U8 R8G8B8A8_SNORM (nVIDIA の tool のみ DirectX9 texture tool では DDPF_BUMPDUDV で bitmask を返す)
'ATI2' 3Dc ATI2 ATI の Normal 圧縮フォーマット
'3DC1' 3Dc ATI1 ATI/Qualcomm の Texture Tool が生成する。
'3DC2' 3Dc ATI2 ATI/Qualcomm の Texture Tool が生成する。
'ATC ' ATITC RGB ATI/Qualcomm の Texture Tool が生成する。
'ATCA' ATITC E-Alpha ATI/Qualcomm の Texture Tool が生成する。
'ATCI' ATITC I-Alpha ATI/Qualcomm の Texture Tool が生成する。
'ETC1' ETC1 ATI/Qualcomm の Texture Tool が生成する。
'PTC2' PVRTC 2bpp (v1) Imagination PowerVR PVRTexTool が生成する。
'PTC4' PVRTC 4bpp (v1) Imagination PowerVR PVRTexTool が生成する。
  • 他にも存在していると思われます。

実際のフォーマット例

format (D3D10) format (D3D9) dwBitCount dwRBitMask dwGBitMask dwBBitMask dwRGBAlphaMask dwFourCC DX10 Format dwPfFlags
B8G8R8A8_UNORM A8R8G8B8 32 0x00ff0000 0x0000ff00 0x000000ff 0xff000000 0 DDPF_RGB|DDPF_ALPHAPIXELS
R8G8B8A8_UNORM A8B8G8R8 32 0x000000ff 0x0000ff00 0x00ff0000 0xff000000 0 DDPF_RGB|DDPF_ALPHAPIXELS
R16G16_UNORM G16R16 32 0x0000ffff 0xffff0000 0x00000000 0x00000000 0 DDPF_RGB
B5G6R5_UNORM R5G6B5 16 0x0000f800 0x000007e0 0x0000001f 0x00000000 0 DDPF_RGB
R32G32B32A32_FLOAT A32B32G32R32F 0 0 0 0 0 0x00000074 DDPF_FOURCC
B8G8R8X8_UNORM X8R8G8B8 32 0x00ff0000 0x0000ff00 0x000000ff 0x00000000 0 DDPF_RGB
L16 16 0x0000ffff 0x00000000 0x00000000 0x00000000 0 DDPF_LUMINANCE
A8L8 16 0x000000ff 0x00000000 0x00000000 0x0000ff00 0 DDPF_LUMINANCE|DDPF_ALPHAPIXELS
BC7 0 0 0 0 0 “DX10” DXGI_FORMAT_BC7_UNORM (98) DDPF_FOURCC
BC1 DXT1 0 0 0 0 0 “DXT1” DDPF_FOURCC
BC1 DXT1 0 0 0 0 0 “DX10” DXGI_FORMAT_BC1_UNORM (70) DDPF_FOURCC
R8G8B8A8_SNORM Q8W8V8U8 32 0x000000ff 0x0000ff00 0x00ff0000 0xff000000 0 DDPF_BUMPDUDV
R8G8B8A8_SNORM Q8W8V8U8 0 0 0 0 0 0x0000003f DDPF_FOURCC
R8G8B8A8_SNORM Q8W8V8U8 0 0 0 0 0 “DX10” DXGI_FORMAT_R8G8B8A8_SNORM DDPF_FOURCC

歴史的事情から DDS の PixelFormat の定義は少々複雑な構造になっています。下記 3通りの定義方法が存在しています。

  • Bitmask で表現される場合 (dwPfFlags が DDPF_RGB|DDPF_ALPHAPIXELS の場合)
  • FourCC で表現される場合。
  • DX10 拡張ヘッダの Format (DXGI_FORMAT) で表現される場合。

実際の上記の表では、R8G8B8A8_SNORM の表現方法が 3通り存在してることがわかります。(dwFourCC == 0x3f は古い nVIDIA のツールが出力していました。) また DXT1 にも 2種類のヘッダが存在していることがわかるかと思います。

  • BC6H, BC7 等、Direct3D 10 以降に追加されたフォーマットを格納するには DX10 拡張ヘッダが必須となります。
  • Texture Array を格納する場合も DX10 拡張ヘッダが必要です。よって、Bitmask 表現が可能な B8G8R8A8_UNORM や B5G6R5_UNORM 等も、TextureArray 使用時は DXGI_FORMAT による表現になります。

フォーマット判定コードの例

#define MAKE_FOURCC( x, y, z, w )  (((w)<<24)|((z)<<16)|((y)<<8)|(x))
 
void* memory= malloc( file_size );
ReadFile( file, memory, file_size, &read_size, NULL );assert( file_size >= sizeof(T_DDSHEADER) );
 
 
const T_DDSHEADER*      header= reinterpret_cast<const T_DDSHEADER*>( memory );
const unsigned char*    data_ptr= reinterpret_cast<const unsigned char*>( memory ) + sizeof(T_DDSHEADER);
 
if( header->dwMagic != 0x20534444 || header->dwSize != 124 ){
    // error
    return  false;
}
 
unsigned int  width= header->dwWidth;
unsigned int  height= max( header->dwWidth, 1 );
unsigned int  depth= (header->dwFlags & DDSD_DEPTH) ? max( header->dwDepth, 1 ) : 1;
unsigned int  mip_map_count= (header->dwFlags & DDSD_MIPMAPCOUNT) ? max( header->dwMipMapCount, 1 ) : 1;
unsigned int  array_count= 1;
unsigned int  face_count= 1;
 
if( header->dwPfFlags & DDPF_FOURCC ){
    unsigned int  fourCC= header->dwFourCC;
    if( fourCC == MAKE_FOURCC( 'D','X','1','0' ) ){ // DX10 拡張ヘッダあり
 
        // 追加ヘッダの読み込み
        const T_DDSHEADER_DX10* header10= reinterpret_cast<const T_DDSHEADER_DX10*>( data_ptr );
        // ピクセルデータ位置の再計算
        data_ptr+= sizeof(T_DDSHEADER_DX10);
 
        DXGI_FORMAT  format_code= header10->Format; // DXGI_FORMAT による定義
        array_count= max( header10->ArraySize, 1 );
 
    }else{
        // FourCC による定義 D3DFORMAT や MAKE_FOURCC('D','X','T','1') など
        switch( fourCC ){
        case MAKE_FOURCC('D','X','T','1'):}
    }
}else if( header->dwPfFlags & (DDPF_RGB|DDPF_ALPHAPIXELS|DDPF_ALPHA|DDPF_BUMPDUDV|DDPF_LUMINANCE) ){
    // Bitmask による定義
    unsigned int  bit_count= header->dwRGBBitCount;
    unsigned int  r_mask= header->dwRBitMask;
    unsigned int  g_mask= header->dwGBitMask;
    unsigned int  b_mask= header->dwBBitMask;
    unsigned int  a_mask= header->dwRGBAlphaBitMask;
}else{
    // error
}
 
if( !(header->dwCaps & DDSCAPS_MIPMAP) ){
    mip_map_count= 1;
}
if( !(header->dwCaps2 & DDSCAPS2_VOLUME) ){
    depth= 1;
}
 
if( header->dwCaps2 & DDSCAPS2_CUBEMAP ){
    face_count= 6; // 厳密には DDSCAPS2_CUBEMAP_POSITIVEX ~ の判定が必要
}

ヘッダ設定コードの例

T_DDSHEADER         header;
T_DDSHEADER_DX10    header10;
memset( &header, 0, sizeof(T_DDSHEADER) );
memset( &header10, 0, sizeof(T_DDSHEADER_DX10) );
bool  isdx10= false;
 
header.dwMagic= 0x20534444;
header.dwSize= 124;
header.dwFlags= DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS|DDSD_PIXELFORMAT;
 
header.dwWidth= width;
header.dwHeight= height;
header.dwDepth= 1;
header.dwMipMapCount= 1;
header10.ArraySize= 1;
header10.Dimension= DDS10_DIIMENSION_2D;
 
if( 非圧縮時 ){
    header.dwFlags|= DDSD_PITCH;
    header.dwPitchOrLinearSize= (width * bit_count) >> 3;
}else{
    header.dwFlags|= DDSD_LINEARSIZE;
    header.dwPitchOrLinearSize= (block_width * block_height * block_bit_count) >> 3;
}
 
if( mipmap_count > 1 ){
    header.dwFlags|= DDSD_MIPMAPCOUNT;
    header.dwCaps|= DDSCAPS_COMPLEX|DDSCAPS_MIPMAP;
    header.dwMipMapCount= mipmap_count;
}
 
if( face_count > 1 ){
    header.dwCaps|= DDSCAPS_COMPLEX;
    header.dwCaps2|= DDSCAPS2_CUBEMAP
            |DDSCAPS2_CUBEMAP_POSITIVEX
            |DDSCAPS2_CUBEMAP_NEGATIVEX
            |DDSCAPS2_CUBEMAP_POSITIVEY
            |DDSCAPS2_CUBEMAP_NEGATIVEY
            |DDSCAPS2_CUBEMAP_POSITIVEZ
            |DDSCAPS2_CUBEMAP_NEGATIVEZ;
}
 
if( depth > 1 ){
    header.dwCaps|= DDSCAPS_COMPLEX;
    header.dwCaps2|= DDSCAPS2_VOLUME;
    header.dwDepth= depth;
    header10.Dimension= DDS10_DIMENSION_3D; 
}
 
if( array_count > 1 ){
    header10.ArraySize= array_count;
    isdx10= true; // Array なら DX10 ヘッダが必須
}
 
header.dwPfSize= 32;
 
if( !isdx10 &&  BitMask 表現可能  ){
    header.dwPfFlags|= DDPF_RGB|DDPF_ALPHAPIXELS;
    header.dwRGBBitCount= bit_count;
    header.dwRBitMask= ..
    header.dwGBitMask= ..
    header.dwBBitMask= ..
    header.dwRGBAlphaBitMask= ..
}else if( !isdx10 &&  FourCC 表現が可能  ){
    header.dwPfFlags|= DDPF_FOURCC;
    header.dwFourCC= ..
}else{
    isdx10= true;
    header.dwPfFlags|= DDPF_FOURCC;
    header.dwFourCC= 0x30315844;    // 'DX10'
 
    // DXGI_FORMAT に変換
    header10.Format= dxgi_format;
}
 
Write( &header, sizeof(T_DDSHEADER) );
if( isdx10 ){
    Write( &header10, sizeof(T_DDSHEADER_DX10) );
}

fp16 (half) のフォーマットと変換

A32B32G32R32F 等の fp32 形式は IEEE754 そのままなので、 通常 32bit float 値を格納するだけです。 A16B16G16R16F 等の fp16 (half) の場合 CPU ネィティブな形式ではありません。 CPU 上で演算を行う場合は変換が必要になります。

fp16 は s10e5 の形をしており、符号1bit、指数部 5bit、 仮数部 10bit となります。

bit15 ------- bit0
s eeeee ffffffffff
------------------

   s = 1bit  sign
   e = 5bit  offset 15 ( 0~31 の値の範囲で 15 を 0とみなす)
   f = 10bit

offset による指数の表現、正規化された仮数部の隠れbit 等は IEEE754 と同じです。 相互変換の簡単なコード例を載せます。(このコードは特殊な状況を想定していません)

ui32 F32to16( float fval )
{
    ui32 ival= * (ui32*)( &fval );
    if( !ival ){
        return  0;
    }
    si32    e=((ival & 0x7f800000)>>23) - 127 + 15;
    if( e < 0 ){
        return  0;
    }else if( e > 31 ){
        e= 31;
    }
    ui32    s=  ival & 0x80000000;
    ui32    f=  ival & 0x007fffff;
    return  ((s>>16)&0x8000) | ((e<<10) & 0x7c00) | ((f>>13) & 0x03ff);
}
float F16to32( ui32 ival )
{
    if( !ival ){
        return  0.0f;
    }
    ui32    s=  ival & 0x8000;
    si32    e=((ival & 0x7c00) >>10) - 15 + 127;
    ui32    f=  ival & 0x03ff;
    ui32    fval= (s<<16) | ((e <<23)&0x7f800000) | (f<<13);
    return  * (float*)( &fval );
}

エンディアンとメモリ並び順

DirectX9 の pixel 形式には A8R8G8B8 等の通常のカラー形式だけでなく A8B8G8R8 や、A2B10G10R10、A32B32G32R32F 等 RGB 順ではなく、BGR 順のフォーマットが増えています。 Intel CPU は little endian なので 32bit ARGB はメモリに格納されると 'B' 'G' 'R' 'A' の順で並びます。

Shader 等、4 要素の vector は x y z w がそれぞれカラー演算時 r g b a に対応するため、 このカラー並びはあまり都合が良くありません。

そのため ABGR というピクセル並びにしておけばメモリ上で 'R' 'G' 'B' 'A' になるのでそのまま x y z w としてアクセスすることができます。

シェーダー互換性のために今後は A B G R が一般的になっていくのかもしれません。 フォーマットはますますややこしくなりますが。

符号付ピクセル

符号付ピクセルのテクスチャフォーマット Q8W8V8U8, V8U8 等は 2の補数表現でそのまま格納されます。

Pixel Shader の _bx2 modifier ( (n-0.5)*2 ) のように unsigned の 0~1.0 へ畳み込んだ値ではありません。 そのため (1.0, 1.0, 1.0, 1.0) を Q8W8V8U8 に変換すると 7f 7f 7f 7f という値になります。

チャンネルとしては U V W Q がそれぞれ x y z w = r g b a に対応しています。

存在しないチャンネル

R8G8B8 等、A チャンネルが存在しないフォーマットや、 R だけのフォーマット等があります。

原則として未定義のチャンネルはデフォルト値 1.0 を持つようです。 ただしカラーチャンネルの場合は例外があるようです。 A チャンネルの未定義は常に 1.0 です。

また法線用の圧縮形式では、未定義のチャンネルを 計算で求める必要があります。 CxV8U8、3Dc (ATI2) はそれぞれ x y の値から z を算出します。

(z= sqrt( 1-(x*x+y*y) ) )

3Dc (ATI2) は符号なしなので、x y に対して事前に _bx2 相当 ( (n-0.5)*2 ) の変換が必要になります。 また z 値の算出は Shader 側で行う必要があり、 テクスチャフォーマットとしては単なる 2ch の R8G8 です。 正規化されたベクトル以外の値を入れて使うこともできます。

L8 L16 等の DDPF_LUMINANCE チャンネルは bitmask のフォーマット上 R として定義されますがカラー 3チャンネル全部にコピーされます。

CUBEMAP の並び順

CUBEMAP はマニュアルにあるとおり X+/X-/Y+/Y-/Z+/Z- 順番に並びます。 問題は各面の上下構造で、この並びは次のようになっています。 一般的な Cross 形式の Cubemap では Z- の UpVect は上下逆になります。

面の向き 上方向(UpVect)
X + Yup (0,1,0)
X - Yup (0,1,0)
Y + Z- (0,0,-1)
Y - Z+ (0,0,1)
Z + Yup (0,1,0)
Z - Yup (0,1,0)

変更履歴

  • 2005/07/10 作成
  • 2005/07/11 Q8W8V8U8 の nVIDIA FourCC 追加 (最初間違っていたのであとで修正)
  • 2005/07/12 ATI2 FourCC 追加
  • 2005/07/13 fp16 変換コードサンプル修正、 符号付ピクセル、 存在しないチャンネルの説明追加
  • 2005/07/19 FourCC と D3DFMT の対応について追記
  • 2007/03/23 COMPLEX のスペルミス修正、Cubemap の説明追加
  • 2015/05/26 Wiki page に変換。DX10 拡張ヘッダについて追記修正。実際の判定コード例を記載。判明している FourCC の追加。
ddsformat.txt · 最終更新: 2015/05/26 16:45 by oga