D3D関連 DDSテクスチャの取り扱い

昔に比べたら扱えるツールもかなり増えました。
DDS 形式のデータの取り扱いも、もうさほど困ることがないとは
思いますが、一応手に入りやすい DDS 関連ツールのまとめを
作ってみました。

DDSテクスチャの表示&作成ツールの解説

ただ、Maya とか CGツール系も DDS 自体には対応したものの、扱える
フォーマットがそれぞれ微妙に食い違っていたり制限があったりします。
この辺はなかなか厄介です。

さらに今後 Direct3D10 向けの画像データをどう扱うのかが、
問題になってきそうです。

例えばこの blog でも過去に書いてますが、テクスチャ画素の RGB の
並びが Direct3D 10 では逆順になりました。
ベクトル系データの x y z w にあわせて統一するためです。
従来 A R G B だった色の並びが A B G R になります。

Direct3D もうひとつのユニファイド

ちなみにメモリ上ではリトルエンディアンなので、従来 B G R A と
並んでいたものが R G B A になります。
ある意味自然な配列になりました。
DXGI_FORMAT での表記も R G B A 順となっています。

D3D10 専用フォーマットが登場したとしても、ツール側での対応はまた
しばらく時間がかかりそうですね。

Nucleus blog サーバー移行メモ (2)

サーバー移行時のメモ、前日のエントリの続きです。
blog サーバー移行メモ
チャンネル北国tv から Nucleus へ blog データの変換を行いました。
同じシステムを使っている他の地域系blogでも、おそらく変換可能だと
思います。

先日の NP_ImpExp の改造で、データ自体は問題なくインポート
できるようになりました。

●参照リンクの解決

次に少々気になるのは、自分自身の blog へのリンク、つまり過去記事を
参照している部分です。当然ですが、そのままだと移転前のページに
飛んでしまいます。

元の blog では、記事固有ページの URL は特に法則性のない名前に
なっていました。たぶん記事の管理番号か何かがそのままページ名に
なっているのだと思われます。
なので、export された MT形式のデータだけみてもどの URL がどの
データに対応していたのかわかりません。

問題はもう1つあります。Nucleus は個別の記事を itemid で区別して
おり、記事固有ページもこの item 番号が割り振られます。ところが
どの番号が割り振られるのかは、実際にデータをインポートしてみるまで
わかりません。
実験で何度も削除やインポートを繰り返しましたが、itemid は
インポートするたびに別に値になってしまいます。

問題点まとめ
・MT形式のデータでは、自分自身への参照リンクがわからない。
 参照している元の URL と、記事の対応付けが必要。

・Nucleus で個別記事を識別する番号は Import してみるまでわから
 ない。事前のツールで対応付けを静的に解決するのは難しい。
 (読み込んだ後の plug-in ならできる?)

そこで、記事個別に対する参照リンクをあきらめて、日付ごとの
アーカイブへのリンクに置き換えることにしました。
もともと1日に1記事程度のペースだったし、これなら事前に URL
も求まるし、何度も Import/Export を繰り返しても大丈夫です。

Nucleus の場合、日付によるアーカイブの URL は

http://wlog.flatlib.jp/archive/1/2007-8-12

こんな感じになります。

最初はリンク先の置き換えを手で修正してみましたが・・8月分だけで
断念。そこで、

 1. 自分の blog への参照リンク URL 抽出
 2. その URL へアクセスしてページデータダウンロード
 3. ページデータ内から日付情報の切り出し
 4. 切り出した日付から、新 blog 用のアーカイブページの URL を求めて置換

こんなスクリプトを書いて一気に置換してみました。

●画像リンクの置換

参照リンクと同じように、埋め込まれている画像の参照も変換する必要が
あります。これは参照リンクに比べたら非常に簡単なので、スクリプトで
単純にパスを置き換えました。

注意点はサーバー側で自動生成されたサムネイルを見ている可能性がある
ことです。変換時に再び生成しなおすか、または移転元から引っ張ってくる
方法もあるかと思います。

この blog ではもともと画像は少ないし容量も小さいので、今回はないも
せずに縮小前の画像へのリンクに置き換えてしまいました。

●チャンネル北国tv から Nucleus への移行手順のまとめ

・MT形式で Export
・自分への参照リンク URL の置換
・画像参照パスの置換

・Nucleus を設置して設定
・Import 前に先に NP_TrackBack Plug-in を入れておく必要あり
修正版のプラグイン NP_ImpExp を install
・NP_ImpExp で読み込み

・移行する画像データは別手段で用意しておき media/1 にアップロード

つい、いつものくせで投稿前にタイトルの文字数を数えてしまいました。
チャンネル北国tv のシステムでは、タイトル文字数が unicode 換算で
32文字までと制限があったからです。

もし逆に Nucleus から チャンネル北国tv 変換する場合は、この
タイトル文字数の対策が必要になるでしょう。

blog サーバー移行メモ

blog データの引越しを行いました。
そのときの作業メモです。

●MT形式での出力

移転前の blog で対応している入出力フォーマットは MT形式と呼ばれるもの
でした。MT形式のインポートとエクスポートはほとんどの blog でサポート
しており、事実上標準となっているようです。

中身は階層を持たないプレーンテキストで、ツールやスクリプトでも比較容易に
扱うことができます。
各記事の内容や日付などの情報、コメントやトラックバックの情報も含まれています。

●MT形式での入力

Nucleus はプラグインを使うことで MT 形式の読み込みができます。
下記の NP_ImpExp を使用しました。
NP_ImpExp

ただ移転前の blog のデータをそのまま読み込むと、かなり行が詰まった画面と
なりうまく再現されていません。サイトやもとのデータに起因する問題だとは
思いますが、空行が失われてしまうようです。

WordPress ではきれいに読み込めていたので、少々原因を調べました。
NP_ImpExp の

 plugins/sharedlibs/cles/MTFileParser.php

の最後、parseTextSection() を下記のように修正しました。(+の削除)

$lines = preg_split("/(\r?\n)+/", $comment);
	↓
$lines = preg_split("/(\r?\n)/", $comment);

これで空行もそのまま読み込めるようになりました。

●TrackBack

インポート時のログを見るといくつかエラーがでていました。トラックバック
のデータでした。Nucleus は標準で TrackBack に対応していないので、
インポート前に下記の Plugin が必要です。

NP_TrackBack

これできちんと読み込めるようになります。
何度もインポートと全データ削除を繰り返していると、TrackBack のデータ
だけが重複して何個も残ってしまうことがありました。
消すときは、item データ削除前に TrackBack のデータも全部消しておいた方が
よさそうです。

●カテゴリの違い

次の問題はカテゴリの分類です。NP_InpExp では読み込む前に、元の blog と
同じカテゴリをあらかじめ追加しておく必要があります。
それでもきちんと読み込めているものもあれば、分類情報が失われてしまう
ものもありました。

NP_ImpExp は標準で PRIMARY CATEGORY を優先し、なければ CATEGORY を
参照しています。元の blog のデータは CATEGORY に情報が入っていたので、
読み込み時に無理やり CATEGORY を参照するように書き換えました。

NP_ImpExp.php importEntry() の先頭に下記の行を追加。

$entry['PRIMARY CATEGORY'] = $entry['CATEGORY'];

●コメントとトラックバックの時刻

読み込んだデータのコメントとトラックバックだけ、日付がリセット(0=1970年)
されてしまう問題が発生しました。
いろいろ調べたところ、DB に渡す日付が ‘/’ スラッシュ区切りになっていた
のが原因でした。これを ‘-‘ ハイフン区切りにしたところ、日付も正しく
読み込まれるようになりました。

NP_ImpExp.php importPing() と importComment() の2箇所あります。
下記のように変更しました。

$timestamp = date("Y/m/d H:i:s", $timestamp);
	↓
$timestamp = date("Y-m-d H:i:s", $timestamp);

このとき試していたのは SQLite 版だったので、もしかしたら MySQL では
スラッシュでも問題ないのかもしれません。マニュアル上はハイフンの方が
正しい形式のようです。

Direct3D 10 ss00 サンプルの解説 (2)

前回の続きで、サンプル ss00 の簡単なソース解説を続けます。
Direct3D 10 ss00 サンプルの解説 (1)

ソースはこちらからどうぞ
HYPERでんち

●ウィンドウ作成 WinMain()

Windows のアプリケーションは Window を作成する必要があります。
簡単にするために、WinMain() 内部でウィンドウの作成も
メッセージループも記述してしまっています。
フレームレートの調整やウエイト等も一切せずにループしているので
ご注意ください。(一応 Sleep が入っています)

●描画 Render()

最初にフレームバッファをクリアします。

float color[4]= {
 0.0f, 0.0f, 1.0f, 0.0f
};
g_iDevice->ClearRenderTargetView( g_iRenderTargetView, color );
g_iDevice->ClearDepthStencilView( g_iDepthStencilView,
  D3D10_CLEAR_DEPTH|D3D10_CLEAR_STENCIL, 1.0f, 0 );

Direct3D9 以前までは Clear() 命令ひとつで行われていた部分です。
D3D10 では、RenderTarget と Depth Buffer それぞれ個別にクリアします。
またカラー値が R8G8B8A8_UINT ではなく R32G32B32A32_FLOAT による
指定が可能となりました。
Direct3D9 では HDR 値の初期化には Shader を使う等の別の手段が必要でした。

サンプルではデモのためにカメラを回転しているのでその計算が若干入ります。
固定ならば Effect に対して Matrix を書き込んで終わりです。

g_iEffect->GetVariableByName( “WorldToView” )->AsMatrix()->SetMatrix(
 (float*)&view );

ID3D10Effect のパラメータ類を反映させるために

g_iEffect->GetTechniqueByIndex( 0 )->GetPassByIndex( 0 )->Apply( 0 );

を実行します。Effect 内に Technique が1つしかないこと、Pass も 1つ
しかないことがわかっているなら、これだけでも十分動きます。

Direct3D10 では初期値が未登録で、描画前に必ず設定しなければならないのが
この IASetPrimitiveTopology() でした。
オーソドックスな TRIANGLELIST にします。
g_iDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

このシェーダーは頂点バッファもインデックスバッファも不要なので、
いきなり描画です。

g_iDevice->DrawInstanced( 36, 12, 0, 0 );

Cube なので Triangle ×2× 6面分=36頂点、さらに 12個のインスタンスを
描画しています。

最後は g_iSwapChain->Present( 0, 0 ); です。
Direct3D9 以前の BeginScene()~EndScene() 系が必要ないので、
D3D10 では非常にすっきりしています。

Matrix 計算、ID3DEffect への変数アクセス以外には、描画のために 5つの API
しか呼び出していません。

Direct3D 10 ss00 サンプルの解説 (1)

以前掲載した D3D10 のサンプルプログラム ss00 を少々解説します。
ss00.cpp 以外にライブラリやら DXUT も使わず、できるだけ
Direct3D 10 の API だけで済むようコードを減らしていったものです。
それでも初期化は思ったより長い関数になりました。
Direct3D 10 シェーダー4.0サンプルプログラム

あらためてゼロから書いてみると結構手間がかかりますね。
それでも DirectX3~6 あたりまでと比べると、シェーダーが必須になった
だけで、初期化自体は簡単になっているはずです。

ソースはこちらからどうぞ
HYPERでんち

動作には Vista + DirectX10 対応 GPU が必要です。

ss00.cpp

●初期化 InitDevice()

InitDevice() は初期化を行います。

Direct3D10 で最初に必要なインターフェースは次の2つです。

ID3D10Device
IDXGISwapChain

この2つは名称が異なるものの、D3D10CreateDeviceAndSwapChain()
関数一発で簡単に作ることができます。
D3D10CreateDeviceAndSwapChain()

DirectX9 以前のように、Device の前に IDirect3D を作る必要がなくなりました。
IDirect3D が行っていた Display 周りの選択や制御が DXGI に移行した形と
なっています。

D3DX10CreateDeviceAndSwapChain() 作成時に与えるパラメータは、
Direct3D9 の D3DPRESENT_PARAMETERS とほとんど同じです。
D3DPRESENT_PARAMETERS
ここは Direct3D9 のコードを基にしていても比較的容易に対応できる
部分でしょう。

フレームバッファのサイズ、リフレッシュレート、フレームバッファのフォーマット、
フロントバッファへ反映させるときのエフェクトやタイミング、フルスクリーン
かどうか、などを与えています。

DXGI_SWAP_CHAIN_DESC swapdesc= {
 {
  width, height, // フレームバッファサイズ
  { 60, 1, }, // リフレッシュレート
  DXGI_FORMAT_R8G8B8A8_UNORM, // フォーマット
  DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED,
  DXGI_MODE_SCALING_UNSPECIFIED,
 },
 { 1, 0, }, // sample
 DXGI_USAGE_RENDER_TARGET_OUTPUT,
 1,
 hwnd, // Window ハンドル
 winmode, // WindowMode か FullScreen か
 DXGI_SWAP_EFFECT_DISCARD,
 0
};

D3D10CreateDeviceAndSwapChain(
 NULL,
 D3D10_DRIVER_TYPE_HARDWARE,
 NULL,
 D3D10_CREATE_DEVICE_DEBUG,
 D3D10_SDK_VERSION,
 &swapdesc,
 &g_iSwapChain, // IDXGISwapChain
 &g_iDevice // ID3D10Device
);

ID3D10Device と IDXGISwapChain ができたら View を作ります。
この View は Direct3D10 になって登場した新しい概念です。
メモリ上のテクスチャ空間に対して、実際にどのような手段で Shader が
アクセスするのか決定します。
リソースの自由度が上がった分、具体的にどのような使い方をするのか
ひと手間必要になったわけですね。

IDXGISwapChain から BackBuffer を取り出し、Direct3D からアクセスするための
ID3D10RenderTargetView を作成します。

ID3D10Texture2D* iBackBuffer= NULL;
g_iSwapChain->GetBuffer( 0, __uuidof( ID3D10Texture2D ),
  reinterpret_cast( &iBackBuffer ) );
g_iDevice->CreateRenderTargetView( iBackBuffer, NULL, &g_iRenderTargetView );
ZRELEASE( iBackBuffer );

次に Depth Buffer を作成しています。
ここでは何も考えずに Stencil のない D3D10 新フォーマットと思われる
D32_FLOAT を使っています。

ID3D10Texture2D* iTexture= NULL;
D3D10_TEXTURE2D_DESC tex2ddesc= {
 width, height, 1, 1, // width, height, mip, array
 DXGI_FORMAT_D32_FLOAT, // format
 { 1, 0, }, // sample
 D3D10_USAGE_DEFAULT,
 D3D10_BIND_DEPTH_STENCIL,
 0, // cpu flags
 0 // misc flags
};
g_iDevice->CreateTexture2D( &tex2ddesc, NULL, &iTexture );

D3D10_DEPTH_STENCIL_VIEW_DESC vdesc= {
 DXGI_FORMAT_D32_FLOAT,
 D3D10_DSV_DIMENSION_TEXTURE2D
};
g_iDevice->CreateDepthStencilView( iTexture, &vdesc, &g_iDepthStencilView );
ZRELEASE( iTexture );

初期化はこれでおしまいです。

ここからは実際にレンダリングに必要な設定を行います。
今回は非常に単純な描画なので、特に毎フレーム実行しなくても良い処理を
初期化の部分に書いてしまっています。
シェーダーの生成以外の部分、パラメータ設定関連は、必要であれば1フレーム
の描画中に何回か書き換えることになるでしょう。

// レンダーターゲットの設定
g_iDevice->OMSetRenderTargets( 1,
  &g_iRenderTargetView, g_iDepthStencilView );

// ビューポートの設定
D3D10_VIEWPORT viewport= {
 0, 0, width, height, 0.0f, 1.0f
};
g_iDevice->RSSetViewports( 1, &viewport );

これら関数はどちらも一度に複数個登録できます。今回は1つだけなので
定数 1 を最初に渡しています。

// shader の作成
ID3D10Blob* iblob= NULL;
if( FAILED( D3DX10CreateEffectFromFile(
  TEXT(“cube.fx”),
  NULL, NULL, “fx_4_0”, 0, 0, g_iDevice,
  NULL, NULL, &g_iEffect, &iblob, NULL ) ) ){

 OutputDebugStringA( reinterpret_cast(
   iblob->GetBufferPointer() ) ); // エラーが発生したら表示
 ZRELEASE( iblob );
}

ここでは fx ファイルから Shader を作成しています。
今回のサンプルでは ID3D10Effect をそのまま使っています。
HLSL のコンパイル時にはエラーが発生することがあるので、
コンパイラのエラーメッセージをそのまま表示出力しています。

最後に Effect の変数を初期化しています。

// projection の設定
D3DXMATRIX projection;
D3DXMatrixPerspectiveFovLH( &projection, 3.141592f/3.0f,
 WINDOW_SIZE_WIDTH/(float)WINDOW_SIZE_HEIGHT, 1.0f, 10000.0f );
g_iEffect->GetVariableByName( “Projection” )->AsMatrix()->SetMatrix( (float*)&projection );

次回は残りの処理と描画です。