DDS Texture format memo

最終更新 2015/05/26 01:04
[戻る]
● 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 ) までのものがほとんどです。

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


新しいページに Direct3D 10 以降の DDS 拡張ヘッダについての解説を追加しました。
新しい DDS フォーマットの解説は下記のリンクよりどうぞ。(2015/05/26)

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


● DDS のヘッダ構造の例

128byte 固定。ヘッダ以後 実データが順番に並ぶ。 Mip/Cube の順番など 並びについては DirectX9 の online help に解説があるのでそちらもあわせて 参考にしてください。
// ヘッダ構造体
struct _iDDSHEAD {
public:
	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 であらわされる場合のみ
	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;
};

● 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〜 等の直接定義が有効
DDSD_MIPMAPCOUNT 0x00020000 dwMipMapCount が有効
DDSD_LINEARSIZE 0x00080000 dwPitchOrLinearSize が LinearSize を表す
DDSD_DEPTH 0x00800000 dwDepth が有効

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

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

● dwCaps

DDSCAPS_ALPHA 0x00000002 Alpha が含まれている場合 (あまり参照されない)
DDSCAPS_COMPLEX 0x00000008 複数のデータが含まれている場合 Palette/Mipmap/Cube/Volume 等
DDSCAPS_TEXTURE 0x00001000 常に 1
DDSCAPS_MIPMAP 0x00400000 MipMap が存在する場合

● dwCaps2

DDSCAPS2_CUBEMAP 0x00000200 Cubemap が存在する場合
DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400
DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800
DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000
DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000
DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000
DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000
DDSCAPS2_VOLUME 0x00400000 VolumeTexture の場合
● dwFourCC

RGB Bitmask 等で表現しきれない特殊なフォーマットは dwFourCC を使って識別し ます。

dwFourCC に入る値は 4文字を組み合わせた ascii 値ですが、 直接 32bit の値が用いられているものもあるようです。

この値は d3d9types.h にある D3DFORMAT の enum 定義値そのままです。 たとえば R32F の場合 0x72 == D3DFMT_R32F == 114 となります。

そのため同じフォーマットでも Bitmask で表現可能な場合、 RGB Bitmask で表現される場合と dwFourCC の D3DFMT 値で表現される場合の 2種類の DDS ヘッダが存在し得ることになります。 実際 nVIDIA のツールと DX9 付属のツールで Q8W8V8U8 のヘッダが異なっていました。 より柔軟なツールにするなら、両方対応しておく必要がありそうです。

FourCC format
'1TXD' DXT1
'2TXD' DXT2
'3TXD' DXT3
'4TXD' DXT4
'5TXD' DXT5
'2ITA' 3Dc ATI2 ATI の Normal 圧縮フォーマット
0x00000024 A16B16G16R16
0x0000006e Q16W16V16U16
0x0000006f R16F
0x00000070 G16R16F
0x00000071 A16B16G16R16F
0x00000072 R32F
0x00000073 G32R32F
0x00000074 A32B32G32R32F
0x00000075 CxV8U8
0x0000003f Q8W8V8U8 (nVIDIA の tool のみ DirectX9 texture tool では DDPF_BUMPDUDV で bitmask を返す)

● 実際のフォーマット例

format dwBitCount dwRBitMask dwGBitMask dwBBitMask AlphaMask dwFourCC dwPfFlags
A8R8G8B8 32 0x00ff0000 0x0000ff00 0x000000ff 0xff000000 0 DDPF_RGB|DDPF_ALPHAPIXELS
A8B8G8R8 32 0x000000ff 0x0000ff00 0x00ff0000 0xff000000 0 DDPF_RGB|DDPF_ALPHAPIXELS
G16R16 32 0x0000ffff 0xffff0000 0x00000000 0x00000000 0 DDPF_RGB
R5G6B5 16 0x0000f800 0x000007e0 0x0000001f 0x00000000 0 DDPF_RGB
L16 16 0x0000ffff 0x00000000 0x00000000 0x00000000 0 DDPF_LUMINANCE
Q8W8V8U8 32 0x000000ff 0x0000ff00 0x00ff0000 0xff000000 0 DDPF_BUMPDUDV
DXT1 0 0 0 0 0 "DXT1" DDPF_FOURCC
A32B32G32R32F 0 0 0 0 0 0x00000074 DDPF_FOURCC
A8L8 16 0x000000ff 0x00000000 0x00000000 0x0000ff00 0 DDPF_LUMINANCE|DDPF_ALPHAPIXELS

● 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 に変換。新しいページはこちらです。

Hyperでんち [メニューに戻る] フルパワー全開

Hiroyuki Ogasawara