目次
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 フォーマットのヘッダが拡張されています。 具体的には下記の手順で判別します。(詳細は後述の判定コードを参考にして下さい)
- dwPfFlags の DDPF_FOURCC が有効
- 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 の追加。