カメラと射影


動かないで下さい。私が動きますから

前回は、2次元的な処理で三角形の一つの頂点を平行移動させました
このようなラスタ処理は、特別な座標変換を必要としないため比較的簡単ですが
今回は、いよいよ Z 座標を用いて回転させる高度な3次元処理を実現します

今までは Z 座標については無視しましたが、ここでちょっと詳しく説明します
すでに理解していると思いますが、Z 座標とは奥行きを表す数値です
左手座標系では、最も手前が 0 であり、奥に行くほど数値が増えていきます
当然、手前のものは大きく、奥のものは小さく見えます

2次元の座標 X と Y に Z 座標が加わった座標系を ワールド座標系と呼びます
単純に、スクリーン座標系に Z が加わっただけと考えてよいでしょう
ただし、数学的に位置を計算するため、Y 座標は上に向かって増えていきます
デフォルトではクライアント領域の左上部分が原点 (0 , 0 , 0) となります

ワールド座標空間に配置される頂点を原点とする座標を モデル座標系と呼び
前回生成した行列はモデル座標系と考えることができます
行列をワールド座標と乗算することによって、新しいワールド座標に変換できましたね



この概念を理解すれば、オブジェクトを自在に動かせることができるかもしれません
Z 座標も含む変換処理を行う時は、もう一つ重大な要素が存在します
それは、どこからどこを見ているかというものです

Z 座標を変化させないラスタ処理では、これは考えなくてもよいことでした
しかし、3次元の世界では「紙」ではなく「空間」をシュミレーションすることになるため
視点と焦点によって、同じオブジェクトでも異なる見え方になるのです
ちょうど、カメラのレンズを覗いている状態だと考えてください

カメラのレンズに写っているものが動けば、もちろん映像は変化しますが
対象が動かなくてもカメラが動けば、やはり映像は変化します
対象が動かなければ変化することのない2次元とは大きな違いがここにあります
この座標空間のことをカメラ座標系と呼びます

さて、実世界で人間が前方を向いている時の座標空間を考えてください
頭の上から空に向かって Y 座標、右に向かって X 座標と考えることができると思います
そして、Z 座標は見えている前方に向かって伸びていると考えられるため
カメラ座標系の場合、座標軸は次のようになります



つまり、クライアント領域に描画される空間は
まさに、このカメラのレンズから覗いて見える空間ということになります
これによって、座標に対する考え方も変えなければなりません
もし、カメラがワールド座標空間の原点を焦点に合わせているとすれば
クライアント領域の中央部分が原点 (0 , 0 , 0) となってしまうのです

カメラを動かせば、それに伴って見える世界も変化します
3DCG のサバイバルゲームでよくあるキャラクターの視点モード等はこの部類です
これは、概念的には頂点ではなくカメラの焦点を移動させて実現させます

このような3次元における様々な変換処理は、自分で行うこともできますが
複雑な変換処理は DirectX Graphics がほとんど補ってくれます
私たちは、デバイスに必要な設定を行い、後は目的の座標を指定するだけでよいのです

変換を DirectX Graphics に任せる場合、頂点フォーマットを変える必要があります
これまでは D3DFVF_XYZRHW を使いましたが D3DFVF_XYZ を座標に使います
頂点フォーマットは変換された同次座標ベクトルは含みません

次に、どのように変換するか、デバイスの設定を行わなければなりません
これを行うには IDirect3DDevice8::SetRenderState() メソッドを使います
ここで、ライティングや面の背面などの設定等を行います

HRESULT SetRenderState(D3DRENDERSTATETYPE State , DWORD Value);

State には、D3DRENDERSTATETYPE 列挙型で設定するステートを指定します
これによって、変更するデバイスのパラメータを指定することができます
Value は設定する値で、その意味は State によって異なります
成功した場合は D3D_OK を、そうでなければ D3DERR_INVALIDCALL を返します

D3DRENDERSTATETYPE 列挙型は次のように定義されています
typedef enum _D3DRENDERSTATETYPE {
    D3DRS_ZENABLE                   =   7,
    D3DRS_FILLMODE                  =   8,
    D3DRS_SHADEMODE                 =   9,
    D3DRS_LINEPATTERN               =  10,
    D3DRS_ZWRITEENABLE              =  14,
    D3DRS_ALPHATESTENABLE           =  15,
    D3DRS_LASTPIXEL                 =  16,
    D3DRS_SRCBLEND                  =  19,
    D3DRS_DESTBLEND                 =  20,
    D3DRS_CULLMODE                  =  22,
    D3DRS_ZFUNC                     =  23,
    D3DRS_ALPHAREF                  =  24,
    D3DRS_ALPHAFUNC                 =  25,
    D3DRS_DITHERENABLE              =  26,
    D3DRS_ALPHABLENDENABLE          =  27,
    D3DRS_FOGENABLE                 =  28,
    D3DRS_SPECULARENABLE            =  29,
    D3DRS_ZVISIBLE                  =  30,
    D3DRS_FOGCOLOR                  =  34,
    D3DRS_FOGTABLEMODE              =  35,
    D3DRS_FOGSTART                  =  36,
    D3DRS_FOGEND                    =  37,
    D3DRS_FOGDENSITY                =  38,
    D3DRS_EDGEANTIALIAS             =  40,
    D3DRS_ZBIAS                     =  47,
    D3DRS_RANGEFOGENABLE            =  48,

    D3DRS_STENCILENABLE             =  52,
    D3DRS_STENCILFAIL               =  53,
    D3DRS_STENCILZFAIL              =  54,
    D3DRS_STENCILPASS               =  55,
    D3DRS_STENCILFUNC               =  56,
    D3DRS_STENCILREF                =  57,
    D3DRS_STENCILMASK               =  58,
    D3DRS_STENCILWRITEMASK          =  59,
    D3DRS_TEXTUREFACTOR             =  60,

    D3DRS_WRAP0                     = 128,
    D3DRS_WRAP1                     = 129,
    D3DRS_WRAP2                     = 130,
    D3DRS_WRAP3                     = 131,
    D3DRS_WRAP4                     = 132,
    D3DRS_WRAP5                     = 133,
    D3DRS_WRAP6                     = 134,
    D3DRS_WRAP7                     = 135,
    D3DRS_CLIPPING                  = 136,
    D3DRS_LIGHTING                  = 137,
    D3DRS_AMBIENT                   = 139,
    D3DRS_FOGVERTEXMODE             = 140,
    D3DRS_COLORVERTEX               = 141,
    D3DRS_LOCALVIEWER               = 142,
    D3DRS_NORMALIZENORMALS          = 143,
    D3DRS_DIFFUSEMATERIALSOURCE     = 145,
    D3DRS_SPECULARMATERIALSOURCE    = 146,
    D3DRS_AMBIENTMATERIALSOURCE     = 147,
    D3DRS_EMISSIVEMATERIALSOURCE    = 148,
    D3DRS_VERTEXBLEND               = 151,
    D3DRS_CLIPPLANEENABLE           = 152,

    D3DRS_SOFTWAREVERTEXPROCESSING  = 153,

    D3DRS_POINTSIZE                 = 154,
    D3DRS_POINTSIZE_MIN             = 155,
    D3DRS_POINTSPRITEENABLE         = 156,

    D3DRS_POINTSCALEENABLE          = 157,
    D3DRS_POINTSCALE_A              = 158,
    D3DRS_POINTSCALE_B              = 159,
    D3DRS_POINTSCALE_C              = 160,

    D3DRS_MULTISAMPLEANTIALIAS      = 161,
    D3DRS_MULTISAMPLEMASK           = 162,

    D3DRS_PATCHEDGESTYLE            = 163,
    D3DRS_PATCHSEGMENTS             = 164,

    D3DRS_DEBUGMONITORTOKEN         = 165,
    D3DRS_POINTSIZE_MAX             = 166,
    D3DRS_INDEXEDVERTEXBLENDENABLE  = 167,
    D3DRS_COLORWRITEENABLE          = 168,
    D3DRS_TWEENFACTOR               = 170,
    D3DRS_BLENDOP                   = 171,

    D3DRS_FORCE_DWORD               = 0x7fffffff
} D3DRENDERSTATETYPE;
この列挙型の各メンバは、次のような意味があります
非常に多くの、しかも複雑な意味を持つメンバがあるため
必要に応じてリファレンスを参照する形で、少しずつ覚えていけばよいでしょう

メンバ解説
D3DRS_ZENABLE D3DZBUFFERTYPE 列挙型の 1 つのメンバとしての深度バッファ ステート
Z バッファリングを有効にするには D3DZB_TRUE
w バッファリングを有効にするには D3DZB_USEW
深度バッファリングを無効にするには D3DZB_FALSE を、それぞれ設定する
D3DPRESENT_PARAMETERS 構造体の EnableAutoDepthStencil メンバに
TRUE を設定して深度ステンシルがスワップ チェーンと共に作成されている場合
このレンダリング ステートのデフォルト値は TD3DZB_TRUE
そうでない場合は D3DZB_FALSE である
D3DRS_FILLMODE D3DFILLMODE 列挙型のメンバのいずれかまたは複数
デフォルト値は D3DFILL_SOLID である
D3DRS_SHADEMODE D3DSHADEMODE 列挙型のメンバのいずれかまたは複数
デフォルト値は D3DSHADE_GOURAUD である
D3DRS_LINEPATTERN D3DLINEPATTERN 構造体
デフォルト値は wRepeatPattern が 0 で、wLinePattern が 0 である
D3DRS_ZWRITEENABLE アプリケーションによる深度バッファへの書き込みを有効にするには、TRUE を設定する
デフォルト値は TRUE である
アプリケーションはこのメンバを利用することにより
システムが新しい深度値で深度バッファを更新してしまうのを防ぐことができる
FALSE の場合は、レンダリング ステート D3DRS_ZFUNC
(深度バッファリングが行われていると仮定) に従って深度比較が実行されるが
深度値はバッファに書き込まれない
D3DRS_ALPHATESTENABLE アルファ テストを可能にするには、TRUE を設定する
デフォルト値は FALSE である
このメンバを使用すると、アルファ値に基づいて
ピクセルを受け取るか拒否するかを判断するテストをオフにできる

入ってくるアルファ値は、D3DRS_ALPHAFUNC レンダリング ステートにより
提供される比較関数を用いて参照アルファ値と比較される
このモードが可能になると、テストが成功した場合に限り、アルファ ブレンディングが起きる
D3DRS_LASTPIXEL 直線や三角形の最後のピクセルを描画可能にするには、FALSE を設定する
デフォルト値は TRUE である。
D3DRS_SRCBLEND D3DBLEND 列挙型のメンバのいずれかである。デフォルト値は D3DBLEND_ONE である
D3DRS_DESTBLEND D3DBLEND 列挙型のメンバのいずれかである。デフォルト値は D3DBLEND_ZERO である
D3DRS_CULLMODE 背面の三角形をカリングする場合に、その方法を指定する
D3DCULL 列挙型のメンバのいずれかを設定できる。デフォルト値は D3DCULL_CCW である
D3DRS_ZFUNC D3DCMPFUNC 列挙型のメンバのいずれかである
デフォルト値は D3DCMP_LESSEQUAL である
このメンバは、アプリケーションにカメラからの
距離を基準とするピクセルの受け取りや受け取り拒否を許可する

ピクセルの深度値を深度バッファの値と比較する
ピクセルの深度値が比較関数で許可された場合にピクセルが書き込まれる

深度値が深度バッファに書き込まれるのは、レンダリング ステートが TRUE の場合だけである

この深度テストに失敗した方が
ソフトウェアによるラスタ化や多くのハードウェア アクセラレータの処理は速くなる
これは、ピクセルのレンダリング処理を行わない場合に
テクスチャをフィルタ処理したり乗算したりする必要がなくなるためである
D3DRS_ALPHAREF アルファ テストが有効に設定されている場合に
ピクセルをテストするための基準アルファ値を指定する値
これは、DWORD レンダリング ステート値の下位 8 ビットにあたる 8 ビット値である
値は 0x00000000 から 0x000000FF の範囲内である
D3DRS_ALPHAFUNC D3DCMPFUNC 列挙型のメンバのいずれかである
デフォルト値は、D3DCMP_ALWAYS である
このメンバは、アプリケーションにアルファ値を基準とする
ピクセルの受け取りや受け取り拒否を許可する
D3DRS_DITHERENABLE ディザリングを可能にするには、TRUE を設定する。デフォルト値は FALSE である
D3DRS_ALPHABLENDENABLE アルファ ブレンドによる透明化を有効にするには、TRUE を設定する
デフォルト値は FALSE である
アルファ ブレンディングのタイプは、D3DRS_SRCBLEND と
D3DRS_DESTBLEND のレンダリング ステートによって決定される

アプリケーションでは D3DCAPS8 構造体の DevCaps メンバの
D3DDEVCAPS_DRAWPRIMTLVERTEX フラグを調べて
このレンダリング ステートがサポートされているかどうかをチェックする
D3DRS_FOGENABLE フォグ ブレンディングを可能にするには、TRUE を設定する
デフォルト値は FALSE である
D3DRS_SPECULARENABLE スペキュラ ハイライトを可能にするには、TRUE を設定する
デフォルト値は FALSE である
スペキュラ ハイライトは、光の当たっているオブジェクトの各頂点が
そのオブジェクトの原点にあるかのように算出される
これによって、オブジェクトが原点の周囲でモデル化され
光源とオブジェクト間の距離が比較的大きい場合に限り結果を予測することができる
それ以外の場合は、結果は不定である

このメンバを TRUE に設定すると、テクスチャ カスケード後
アルファ ブレンディング前に、ベース カラーにスペキュラ色が追加される
D3DRS_ZVISIBLE サポートされない
D3DRS_FOGCOLOR D3DCOLOR 型の値。デフォルト値は 0 である
D3DRS_FOGTABLEMODE ピクセル フォグに使用されるフォグ式
D3DFOGMODE 列挙型のメンバのいずれかを設定する
デフォルト値は D3DFOG_NONE である
D3DRS_FOGSTART 線形フォグ モードにおける、ピクセルまたは頂点フォグ エフェクトの開始深度
頂点フォグの場合、深度はワールド空間で指定される
ピクセル フォグの場合は、デバイス空間 [0.0, 1.0]
またはワールド空間のいずれかで指定される
ピクセル フォグでは、フォグ計算に Z が使用される場合、これらの値はデバイス空間にあり
視点との相対フォグ (W フォグ) が使用される場合はワールド空間にある

このレンダリング ステートの値は浮動小数点値である
IDirect3DDevice8::SetRenderState メソッドは DWORD 値を使用するため
次のコードに示すように、アプリケーションでは
値を格納した変数をキャストしなければならない

pd3dDevice8->SetRenderState(D3DRS_TWEENFACTOR, *((DWORD*)&TweenFactor));
D3DRS_FOGEND 線形フォグ モードにおける、ピクセルまたは頂点フォグ エフェクトの終了深度
頂点フォグの場合、深度はワールド空間で指定される
ピクセル フォグの場合は、デバイス空間 [0.0, 1.0]
またはワールド空間のいずれかで指定される
ピクセル フォグでは、フォグ計算に Z が使用される場合
これらの値はデバイス空間にあり
視点との相対フォグ (W フォグ) が使用される場合はワールド空間にある

このレンダリング ステートの値は浮動小数点値である
IDirect3DDevice8::SetRenderState メソッドは DWORD 値を使用するため
次のコードに示すように、アプリケーションでは
値を格納した変数をキャストしなければならない

pd3dDevice8->SetRenderState(D3DRS_FOGEND, *((DWORD*) (&fFogEnd)));
D3DRS_FOGDENSITY 指数フォグ モード (D3DFOG_EXP および D3DFOG_EXP2) で
使用するピクセルまたは頂点フォグのフォグ密度
有効な密度は 0.0 から 1.0 の範囲内である。デフォルト値は 1.0 である
このレンダリング ステートの値は浮動小数点値である
IDirect3DDevice8::SetRenderState メソッドは DWORD 値を使用するため
次のコードに示すように、アプリケーションでは
値を格納した変数をキャストしなければならない

pd3dDevice8->SetRenderState(D3DRS_FOGDENSITY, *((DWORD*) (&fFogDensity)));
D3DRS_EDGEANTIALIAS オブジェクトの凸面の輪郭を形成する線をアンチエイリアスするには、TRUE を設定する
デフォルト値は FALSE である
TRUE の場合、レンダリングの対象となるのは線のみで
シーン内のポリゴンの外側エッジに限定される
このレンダリング ステートが設定されていて
三角形または点が描画されると、その動作は不定である
アンチエイリアシングは、隣接ピクセルの値の平均値を取ることによって実行される
アンチエイリアシングを行うのは最良のやり方ではないが、効率的ではある
この種の処理をサポートするハードウェアは普及しつつある
エッジ アンチエイリアシングは、D3DPRASTERCAPS_ANTIALIASEDGES 能力を
公開するデバイスに限り、有効にすることができる
D3DRS_ZBIAS 物理的には同一面上にあるポリゴンが別々に存在するように見せる
0 から 16 までの範囲の整数値
Z バイアス値が高いポリゴンは、Z バイアス値が低いポリゴンより手前に表示されるため
描画順序を決めるソートを実行する必要はない
たとえば、値 1 を持つポリゴンは、値 0 のポリゴンよりも手前に見える
デフォルト値は 0 である。
D3DRS_RANGEFOGENABLE 範囲ベースの頂点フォグを有効にするには、TRUE を設定する
デフォルト値は FALSE である
この場合は深度ベースのフォグが使用される
範囲ベースのフォグでは、シーンにおけるオブジェクトの深さ (z 座標) ではなく
ビューアからのオブジェクトの距離でフォグ エフェクトを計算する
範囲ベースのフォグでは、通常のすべてのフォグ メソッドが機能するが
深度ではなく範囲を使って計算を行う点だけが異なる
範囲はフォグ計算に使用する適切な要素であるが、範囲は計算の点で負荷が高く
深度は通常計算済みなので、代わりに深度が広く使われる
深度を使用してフォグを計算すると、周辺オブジェクトのフォグ エフェクトが
視点の移動と共に変化するという好ましくない影響が出る
この場合、深度は変化するが範囲は一定のままである

現在のところ、ピクセル単位の範囲ベースの
フォグをサポートするハードウェアは存在しないため
範囲補正は頂点フォグでのみ行われる
D3DRS_STENCILENABLE ステンシル処理を可能にするには TRUE、無効にするには FALSE を設定する
デフォルト値は FALSE である
D3DRS_STENCILFAIL ステンシル テストに失敗したときに実行するステンシル処理
これには D3DSTENCILOP 列挙型のメンバのいずれかを設定できる
デフォルト値は D3DSTENCILOP_KEEP である
D3DRS_STENCILZFAIL ステンシル テストにパスし、深度テスト (Z テスト) に
失敗した場合に実行するステンシル処理
これには D3DSTENCILOP 列挙型のメンバのいずれかを設定できる
デフォルト値は D3DSTENCILOP_KEEP である
D3DRS_STENCILPASS ステンシル テストおよび深度 (Z) テストの両方にパスした場合に実行するステンシル処理
これには D3DSTENCILOP 列挙型のメンバのいずれかを設定できる
デフォルト値は D3DSTENCILOP_KEEP である
D3DRS_STENCILFUNC ステンシル テストのための比較関数
これには D3DCMPFUNC 列挙型のメンバのいずれかを設定できる
デフォルト値は、D3DCMP_ALWAYS である
比較関数は、ステンシル バッファの要素を基準値と比較するために使われる
この比較は、基準値のビットと、ステンシル マスク
(D3DRS_STENCILMASK レンダリング ステートにより設定)
に設定されたステンシル バッファ要素にのみ適用される
TRUE の場合は、ステンシル テストにパスする
D3DRS_STENCILREF ステンシル テストのための整数基準値。デフォルト値は 0 である
D3DRS_STENCILMASK 基準値と、それぞれのステンシル バッファ要素に適用し
ステンシル テストのための有効ビットを決定するマスク
デフォルト マスクは 0xFFFFFFFF である
D3DRS_STENCILWRITEMASK ステンシル バッファに書き込む値に適用する書き込みマスク
デフォルト マスクは 0xFFFFFFFF である
D3DRS_TEXTUREFACTOR D3DTA_TFACTOR テクスチャ ブレンディング引数
または D3DTOP_BLENDFACTORALPHA テクスチャ ブレンディング処理を用いた
マルチテクスチャ ブレンディングで使用される色
関連する値は D3DCOLOR 変数である
D3DRS_WRAP0 〜 D3DRS_WRAP7 複数のテクスチャ座標セットに対するテクスチャラッピング動作
これらのレンダリング ステートの有効値として
D3DWRAPCOORD_0 (または D3DWRAP_U)、D3DWRAPCOORD_1 (または D3DWRAP_V)
D3DWRAPCOORD_2 (または D3DWRAP_W)、および
D3DWRAPCOORD_3 の各フラグを組み合わせることができる
これらの値を設定すると、指定されたテクスチャに対して 1、2、3、および 4 次元方向
(s、t、r、および q 方向と呼ばれる場合もある) のラッピングが実行される
これらのレンダリング ステートのデフォルト値は 0 である (全方向のラッピングが無効)
D3DRS_CLIPPING MicrosoftR Direct3DR によるプリミティブのクリッピングを有効にするには TRUE
無効にするには FALSE を設定する
デフォルト値は TRUE である
D3DRS_LIGHTING Direct3D のライティングを有効にするには TRUE、無効にするには FALSE を設定する
デフォルト値は TRUE である
頂点法線を含む頂点のみ正しくライトが当てられ
法線を含まない頂点ではすべてのライティング計算で 0 の内積が使用される
D3DRS_AMBIENT アンビエント ライトの色。この値は D3DCOLOR 型である
デフォルト値は 0 である
D3DRS_FOGVERTEXMODE 頂点フォグ用のフォグ式
D3DFOGMODE 列挙型のメンバのいずれかを設定する
デフォルト値は D3DFOG_NONE である
D3DRS_COLORVERTEX 頂点単位のカラーを有効にするには TRUE、無効にするには FALSE を設定する
デフォルト値は TRUE である
頂点単位のカラーを有効にすると
個々の頂点に定義された色がライティング計算に含まれるようになる
D3DRS_LOCALVIEWER カメラとの相対角度に依存した
スペキュラ ハイライトを有効にするには、TRUE を設定する
直行スペキュラ ハイライトを使用するには、FALSE を設定する
デフォルト値は TRUE である
正射影を使用するアプリケーションでは FALSE を設定すること
D3DRS_NORMALIZENORMALS 頂点法線の自動正規化を有効にするには TRUE
無効にするには FALSE を設定する
デフォルト値は FALSE である
この機能を有効にすると、頂点がカメラ空間に
トランスフォームされた後で頂点法線が正規化されるが、計算負荷が大きい
D3DRS_DIFFUSEMATERIALSOURCE ライティング計算に使用されるディフューズ色のソース
有効値は、D3DMATERIALCOLORSOURCE 列挙型のメンバである
デフォルト値は D3DMCS_COLOR1 である
このレンダリング ステートの値が使用されるのは
D3DRS_COLORVERTEX レンダリング ステートが TRUE に設定されている場合だけである
D3DRS_SPECULARMATERIALSOURCE ライティング計算に使用されるスペキュラ色のソース
有効値は、D3DMATERIALCOLORSOURCE 列挙型のメンバである
デフォルト値は D3DMCS_COLOR2 である
D3DRS_AMBIENTMATERIALSOURCE ライティング計算に使用されるアンビエント色のソース
有効値は、D3DMATERIALCOLORSOURCE 列挙型のメンバである
デフォルト値は D3DMCS_COLOR2 である
D3DRS_EMISSIVEMATERIALSOURCE ライティング計算に使用されるエミッション色のソース
有効値は、D3DMATERIALCOLORSOURCE 列挙型のメンバである
デフォルト値は D3DMCS_MATERIAL である
D3DRS_VERTEXBLEND ジオメトリがある場合
ジオメトリ ブレンディングを実行するために使用する行列の個数
有効値は D3DVERTEXBLENDFLAGS 列挙型のメンバである
デフォルト値は D3DVBF_DISABLE である
D3DRS_CLIPPLANEENABLE ユーザー定義クリップ面を有効または無効にする
有効値は、各ビットのステータス (設定の有無) によって
対応するユーザー定義クリップ面を
アクティブにするステートを切り替える、任意の DWORD である
最下位ビット (ビット 0) はインデックス 0 の最初のクリップ面を制御し
後続ビットはそれより上位のインデックスのクリップ面をアクティブにすることを制御する
ビットが設定されている場合、シーンのレンダリング中に適切なクリップ面が適用される
デフォルト値は 0 である

クリップ面を簡単に有効にできるよう、D3DCLIPPLANEn マクロが定義されている
D3DRS_SOFTWAREVERTEXPROCESSING ハードウェア頂点処理またはソフトウェア頂点処理の設定を
アプリケーションから照会および選択できるようにする BOOL 値
デバイスの種類が D3DDEVTYPE_SW の場合、この値は TRUE の固定である
デバイスの種類が D3DDEVTYPE_REF の場合は
アプリケーションでこの値を設定できる
デバイスの種類が D3DDEVTYPE_HAL の場合は
D3DDEVCAPS_HWTRANSFORMANDLIGHT が設定されているときに限り
アプリケーションでこの値を設定できる
それ以外の場合、このフラグは TRUE に固定される
アプリケーションでの設定が可能な場合、デフォルト値は FALSE である

両方の頂点処理が可能なモードにおいて、頂点処理のレンダリング ステートを変更すると
カレント ストリーム、インデックス、および頂点シェーダは
デフォルト値の NULL または 0 にリセットされる
D3DRS_POINTSIZE 各頂点に対してポイント サイズが指定されていない場合に
ポイント サイズ計算で使用するサイズを指定する浮動小数点値
頂点がポイント サイズを含んでいる場合、この値は使用されない
この値は、D3DRS_POINTSCALEENABLE が FALSE の場合はスクリーン空間単位で指定する
それ以外の場合は、ワールド空間単位で指定する
デフォルト値は 1.0f である。この値の範囲は、0.0f 以上である
IDirect3DDevice8::SetRenderState メソッドは DWORD 値を使用するため
次のコードに示すように、アプリケーションでは
値を格納した変数をキャストしなければならない

pd3dDevice8->SetRenderState(D3DRS_PATCHSEGMENTS, *((DWORD*)&PointSize));
D3DRS_POINTSIZE_MIN ポイント プリミティブの最小サイズを指定する浮動小数点値
レンダリングの間、ポイント プリミティブはこのサイズに固定される
1.0 より小さい値を設定すると、ポイントにピクセルの中心が含まれずに
アンチエイリアシングが無効になっている場合はポイントが表示されなくなり
アンチエイリアシングが有効になっている場合は低い輝度でレンダリングされる
デフォルト値は 1.0f である
この値の範囲は、0.0f 以上である
IDirect3DDevice8::SetRenderState メソッドは DWORD 値を使用するため
次のコードに示すように、アプリケーションでは
値を格納した変数をキャストしなければならない

pd3dDevice8->SetRenderState(D3DRS_PATCHSEGMENTS, *((DWORD*)&PointSizeMin));
D3DRS_POINTSPRITEENABLE BOOL 値
TRUE を設定すると、テクスチャ全体が各ポイントにマッピングされるように
ポイント プリミティブのテクスチャ座標が設定される
FALSE を設定すると、頂点のテクスチャ座標がポイント全体に対して使用される
デフォルト値は FALSE である
D3DRS_POINTSCALEENABLE に FALSE (デフォルト値) を設定し
D3DRS_POINTSIZE に 1.0 (デフォルト値) を設定することで
DirectX 7 スタイルのシングルピクセル ポイントを実現できる
D3DRS_POINTSCALEENABLE ポイント プリミティブに対するサイズの計算を制御する BOOL 値
TRUE を設定すると、ポイント サイズは、カメラ空間の値として解釈され
距離関数および視錐台によってビューポートの Y 軸のスケーリングになるように調整されて
最終的なスクリーン空間のポイント サイズが計算される
FALSE を設定すると、ポイント サイズは
スクリーン空間の値と解釈されて、そのまま使われる
デフォルト値は FALSE である
D3DRS_POINTSCALE_A ポイント プリミティブに対する距離ベースのサイズの減衰を制御する浮動小数点値
D3DRS_POINTSCALEENABLE が TRUE の場合にのみ有効である
デフォルト値は 1.0f である。この値の範囲は、0.0f 以上である
IDirect3DDevice8::SetRenderState メソッドは DWORD 値を使用するため
次のコードに示すように、アプリケーションでは
値を格納した変数をキャストしなければならない

pd3dDevice8->SetRenderState(D3DRS_PATCHSEGMENTS, *((DWORD*)&PointScaleA));
D3DRS_POINTSCALE_B ポイント プリミティブに対する距離ベースのサイズの減衰を制御する浮動小数点値
D3DRS_POINTSCALEENABLE が TRUE の場合のみ有効である
デフォルト値は 0.0f である。この値の範囲は、0.0f 以上である
IDirect3DDevice8::SetRenderState メソッドは DWORD 値を使用するため
次のコードに示すように、アプリケーションでは
値を格納した変数をキャストしなければならない

pd3dDevice8->SetRenderState(D3DRS_PATCHSEGMENTS, *((DWORD*)&PointScaleB));
D3DRS_POINTSCALE_C ポイント プリミティブに対する距離ベースのサイズの減衰を制御する浮動小数点値
D3DRS_POINTSCALEENABLE が TRUE の場合のみ有効である
デフォルト値は 0.0f である。この値の範囲は、0.0f 以上である
IDirect3DDevice8::SetRenderState メソッドは DWORD 値を使用するため
次のコードに示すように、アプリケーションでは
値を格納した変数をキャストしなければならない

pd3dDevice8->SetRenderState(D3DRS_PATCHSEGMENTS, *((DWORD*)&PointScaleC));
D3DRS_MULTISAMPLEANTIALIAS マルチサンプル レンダリングのターゲット バッファを使うときの
個々のサンプルの計算方法を決定する BOOL 値
TRUE を設定すると、複数サンプルのそれぞれについて
異なるサンプリング位置でサンプリングすることで
フルシーン アンチエイリアシングを実行するように複数のサンプルが計算される
FALSE を設定すると、複数のサンプルはすべて同じサンプリング値
(ピクセルの中心でサンプリングされた値) で記述される
これにより、マルチサンプル バッファに対する
非アンチエイリアシング レンダリングが可能になる
シングル サンプル バッファへのレンダリング時
このレンダリング ステートはエフェクトを持たない
デフォルト値は TRUE である
D3DRS_MULTISAMPLEMASK LSB を先頭とするこのマスクの各ビットは
マルチサンプル レンダリング ターゲットの
サンプルの 1 つについての変更を制御する
したがって、8 サンプルのレンダリング ターゲットの場合は
8 つのサンプルのそれぞれに対する 8 つの書き込み許可情報が
下位バイトに格納されている
シングル サンプル バッファへのレンダリング時
このレンダリング ステートはエフェクトを持たない
デフォルト値は 0xFFFFFFFF である
このレンダリング ステートを使うと
マルチサンプル バッファを蓄積バッファとして使い
各パスがサンプルのサブセットを更新する
ジオメトリのマルチパス レンダリングを行うことができる
D3DRS_PATCHEDGESTYLE パッチ エッジが浮動小数点数スタイルの
テッセレーションを使用するかどうかを設定する
設定可能な値は、D3DPATCHEDGESTYLE 列挙型で定義される
D3DRS_PATCHSEGMENTS パッチを描画するときに、セグメント数を
各エッジの浮動小数点数値として設定する
IDirect3DDevice8::SetRenderState メソッドは DWORD 値を使用するため
次のコードに示すように、アプリケーションでは
値を格納した変数をキャストしなければならない

pd3dDevice8->SetRenderState(D3DRS_PATCHSEGMENTS, *((DWORD*)&NumSegments));
D3DRS_DEBUGMONITORTOKEN モニタをデバッグする場合にのみ設定する
設定可能な値は、D3DDEBUGMONITORTOKENS 列挙型で定義される
D3DRS_DEBUGMONITORTOKEN が設定されている場合
この呼び出しはデバッグ モニタへの
トークンの受け渡しとして処理されることに注意すること
たとえば、D3DDMT_ENABLE または D3DDMT_DISABLE を
D3DRS_DEBUGMONITORTOKEN に渡した後
ほかのトークンの値が渡された場合
デバッグ モニタのステート (有効または無効) は変化しない

このステートは、デバッグ ビルドでのみ有用である
デバッグ モニタのデフォルトは D3DDMT_ENABLE である
D3DRS_POINTSIZE_MAX ポイント スプライトが制限される最大サイズを指定する浮動小数点数値
値は、D3DCAPS8 の MaxPointSize メンバ以下で
D3DRS_POINTSIZE_MIN 以上でなければならない
IDirect3DDevice8::SetRenderState メソッドは DWORD 値を使用するため
次のコードに示すように、アプリケーションでは
値を格納した変数をキャストしなければならない

pd3dDevice8->SetRenderState(D3DRS_PONTSIZE_MAX, *((DWORD*)&PointSizeMax));
D3DRS_INDEXEDVERTEXBLENDENABLE インデックス付きの頂点ブレンディングを有効または無効にする BOOL 値
TRUE を設定すると、インデックス付き頂点ブレンディングが有効になる
FALSE を設定すると、インデックス付き頂点ブレンディングは無効になる
このレンダリング ステートを有効にする場合は
頂点ごとにパック DWORD で行列インデックスを渡さなければならない
このレンダリング ステートを無効にし
D3DRS_VERTEXBLEND ステートを使って頂点ブレンディングを有効にするのは
すべての頂点について行列インデックス 0, 1, 2, 3 を指定することと等しい
D3DRS_COLORWRITEENABLE レンダリング ターゲットのカラー バッファに対する
チャネルごとの書き込みを有効にする UINT 値
ビットを設定すると、3D レンダリングの間にカラー チャネルが更新される
ビットをクリアすると、カラー チャネルは更新されない
デバイスに対する D3DCAPS8 構造体の PrimitiveMiscCaps メンバで
D3DPMISCCAPS_COLORWRITEENABLE 能力ビットを設定すると
この機能を利用できるようになる
このレンダリング ステートは、クリア処理には影響を与えない
デフォルト値は 0x0000000F である

このレンダリング ステートに対する有効な値は、D3DCOLORWRITEENABLE_ALPHA
D3DCOLORWRITEENABLE_BLUE、D3DCOLORWRITEENABLE_GREEN
および D3DCOLORWRITEENABLE_RED の各フラグの任意の組み合わせである
D3DRS_TWEENFACTOR トゥイーン係数を制御する浮動小数点数値
IDirect3DDevice8::SetRenderState メソッドは DWORD 値を使用するため
次のコードに示すように、アプリケーションでは
値を格納した変数をキャストしなければならない

pd3dDevice8->SetRenderState(D3DRS_PATCHSEGMENTS, *((DWORD*)&TweenFactor));
D3DRS_BLENDOP アルファ ブレンディング レンダリング ステート D3DRS_ALPHABLENDENABLE が
TRUE に設定されているときに適用される算術演算を選択するために使用する値
D3DBLENDOP 列挙型で定義される。デフォルト値は D3DBLENDOP_ADD である

D3DPMISCCAPS_BLENDOP デバイス能力がサポートされていない場合は
D3DBLENDOP_ADD が実行される
D3DRS_FORCE_DWORD この列挙型を強制的に 32 ビット サイズにコンパイルする
この値は使用されていない

今回は、まずカリングモードを最初に設定します
通常、面の背面は見えても、見えなくても計算され描画されます
背面が見えないのであれば、これは無駄な計算となるため
これを省略し、背面の処理を排除するのがカリングモードです

今回はカメラを回転させて背面を写すため、カリングモードをオフにする必要があります
そのため SetRenderState() メソッドに D3DRS_CULLMODE メンバを指定して設定を変えます
このとき、第2引数は D3DCULL 列挙型のメンバを指定します
typedef enum _D3DCULL {
    D3DCULL_NONE                = 1,
    D3DCULL_CW                  = 2,
    D3DCULL_CCW                 = 3,

    D3DCULL_FORCE_DWORD         = 0x7fffffff
} D3DCULL;
この列挙型のメンバの意味は、次のように定義されています

メンバ解説
D3DCULL_NONE 背面のカリングはしない
D3DCULL_CW 背面を右回りにカリングする
D3DCULL_CCW 背面を左回りにカリングする
D3DCULL_FORCE_DWORD この列挙型を強制的に 32 ビット サイズにコンパイルする
この値は使用されていない

D3DCULL_NONE を指定すれば、カリングは行われません
また、SetRenderState() メソッドで D3DRS_LIGHTING を設定する必要があります
これは、後ほど紹介するライティングと呼ばれる光の反射を表現する処理ですが
今は使用しないため 第2引数に FALSE を指定することで無効にしてください

この設定が終われば、とりあえずこれまでの方法で描画を行うことができます
次に、カメラの位置を設定して3次元空間をカメラ座標に変換します
空間をカメラ視点に変換する処理をビュー変換と呼びます
様々な変換に関する設定は IDirect3DDevice8::SetTransform() を使います
HRESULT SetTransform(
	D3DTRANSFORMSTATETYPE State , CONST D3DMATRIX* pMatrix
);
State には D3DTRANSFORMSTATETYPE 列挙型のメンバのいずれかを指定し
デバイスの設定変更の対象となる値を指定します
pMatrix には、設定する変換行列を表す D3DMATRIX 構造体へのポインタを指定します
成功した場合は D3D_OK を、そうでなければ D3DERR_INVALIDCALL を返します

State には D3DTS_WORLDMATRIX() マクロを指定することもできます
これは、ワールド座標系の変換行列を設定するものです

D3DTS_WORLDMATRIX(index) (D3DTRANSFORMSTATETYPE)(index + 256)

index には、0〜255 までの値で設定する行列を指定することができます
これは、最大で256個の行列を保存できることを意味しています
ワールド座標系の変換行列の設定に D3DTS_WORLD() も指定できます

D3DTS_WORLD D3DTS_WORLDMATRIX(0)

見てわかるように、単純に先ほどのマクロのインデックス0番を省略したものです
D3DTRANSFORMSTATETYPE 列挙型は次のように定義されています
typedef enum _D3DTRANSFORMSTATETYPE {
    D3DTS_VIEW            =  2,
    D3DTS_PROJECTION      =  3,
    D3DTS_TEXTURE0        = 16,
    D3DTS_TEXTURE1        = 17,
    D3DTS_TEXTURE2        = 18,
    D3DTS_TEXTURE3        = 19,
    D3DTS_TEXTURE4        = 20,
    D3DTS_TEXTURE5        = 21,
    D3DTS_TEXTURE6        = 22,
    D3DTS_TEXTURE7        = 23,

    D3DTS_FORCE_DWORD     = 0x7fffffff
} D3DTRANSFORMSTATETYPE;
この列挙型の各メンバの意味は、次のように定義されています

メンバ解説
D3DTS_VIEW ビュー トランスフォーム行列として設定されるトランスフォーム行列を識別する
デフォルト値は NULL である (単位行列)
D3DTS_PROJECTION 射影トランスフォーム行列として設定されるトランスフォーム行列を識別する
デフォルト値は NULL である (単位行列)
D3DTS_TEXTURE0 〜
D3DTS_TEXTURE7
指定されたテクスチャ ステージに設定されるトランスフォーム行列を識別する
D3DTS_FORCE_DWORD この列挙型を強制的に 32 ビット サイズにコンパイルする
この値は使用されていない

D3DTS_VIEW メンバを SetTransform() に指定することで
第2引数で指定した行列を元に、DirectX Graphics がカメラ座標に変換してくれます

問題は、どうやってカメラ座標用の行列を作成するかですが
これは D3DXMatrixLookAtLH() 関数が左手座標で生成してくれます
右手座標用の D3DXMatrixLookAtRH() も存在します
D3DXMATRIX* D3DXMatrixLookAtLH(
	D3DXMATRIX* pOut,
	CONST D3DXVECTOR3* pEye , CONST D3DXVECTOR3* pAt ,
	CONST D3DXVECTOR3* pUp
);
D3DXMATRIX* D3DXMatrixLookAtRH(
	D3DXMATRIX* pOut,
	CONST D3DXVECTOR3* pEye , CONST D3DXVECTOR3* pAt ,
	CONST D3DXVECTOR3* pUp
);
pOut には、作成する行列へのポインタを指定します
pEye はカメラの位置を、pAt にはカメラが注目する焦点を指定します
pUp はワールド座標形のカメラの上部を表す位置を指定します
戻り値は作成した行列のポインタで、pOut と同じ値です

ワールド座標を指定するには D3DVECTOR3 構造体を使います
この構造体は、単純に x、y、z という座標を意味するメンバを保有しています
typedef struct _D3DVECTOR {
    float x;
    float y;
    float z;
} D3DVECTOR;
C++ 言語を用いている場合、これに様々な機能をカプセル化した
D3DXVERCTOR3 構造体を使うことができます
この構造体は D3DVECTOR 構造体を継承して、オブジェクト指向の恩恵を受けています
typedef struct D3DXVECTOR3 : public D3DVECTOR {
public:
    D3DXVECTOR3() {};
    D3DXVECTOR3( CONST FLOAT * );
    D3DXVECTOR3( CONST D3DVECTOR& );
    D3DXVECTOR3( FLOAT x, FLOAT y, FLOAT z );

    // キャスティング
    operator FLOAT* ();
    operator CONST FLOAT* () const;

    // 代入演算子
    D3DXVECTOR3& operator += ( CONST D3DXVECTOR3& );
    D3DXVECTOR3& operator -= ( CONST D3DXVECTOR3& );
    D3DXVECTOR3& operator *= ( FLOAT );
    D3DXVECTOR3& operator /= ( FLOAT );

    // 単項演算子
    D3DXVECTOR3 operator + () const;
    D3DXVECTOR3 operator - () const;

    // 2 項演算子
    D3DXVECTOR3 operator + ( CONST D3DXVECTOR3& ) const;
    D3DXVECTOR3 operator - ( CONST D3DXVECTOR3& ) const;
    D3DXVECTOR3 operator * ( FLOAT ) const;
    D3DXVECTOR3 operator / ( FLOAT ) const;

    friend D3DXVECTOR3 operator * ( FLOAT, CONST struct D3DXVECTOR3& );

    BOOL operator == ( CONST D3DXVECTOR3& ) const;
    BOOL operator != ( CONST D3DXVECTOR3& ) const;

} D3DXVECTOR3, *LPD3DXVECTOR3;
宣言を見てわかるように、座標同士の演算がカプセル化されています
C++ 言語を使う場合は、この構造体を使ってプログラムすることができます

D3DXMatrixLookAtLH() 関数のカメラの位置と焦点については理解できるでしょう
pUp 引数に指定するカメラの上とは、カメラの向きを表します
例えば、飛行機ゲームの視点モードは、飛行機が傾くとカメラも傾きます
このような処理の変換を行うための値として、カメラの上部を指定するのです
通常は (0 , 1 , 0) で正常に構えますが、例えば (0 , -1 , 0) とすると世界が逆転します

カメラの位置を決定したら、次はカメラの視野を設定します
3次元空間では、カメラには見えない空間というのも存在するわけですから
視野角と最前面、最後面の Z 座標を指定することで、カメラの可視空間を設定するのです
これは SetTransform() メソッドで射影変換を行うことで実現できます

射影変換用の行列は D3DXMatrixPerspectiveFovLH() 関数で生成します
Fov (フォブ)とは、Field Of Vision の略で、日本語で視野と訳せます
右手座標用の D3DXMatrixPerspectiveFovRH() 関数も存在します
D3DXMATRIX* D3DXMatrixPerspectiveFovLH(
	D3DXMATRIX* pOut,
	FLOAT fovy , FLOAT Aspect ,
	FLOAT zn , FLOAT zf
);
D3DXMATRIX* D3DXMatrixPerspectiveFovRH(
	D3DXMATRIX* pOut,
	FLOAT fovy , FLOAT Aspect ,
	FLOAT zn , FLOAT zf
);
pOut には、D3DXMATRIX 構造体へのポインタを指定します
fovy には、視野角をラジアン単位で指定します

Aspect は、長方形のアスペクト比を指定するパラメータです
アスペクト比は「横幅/高さ」で求められ、視野はこの比で伸縮されます
zn は最も近い面の Z 座標、zf は最も遠い面の Z 座標を指定します
戻り値は作成した行列のポインタで、pOut と同じ値です

視野角はラジアンと呼ばれる角度の単位で指定しなければなりません
おそらく、多くの人は度数で角度を考えるため、ラジアンには馴染みがないでしょう
ラジアンとは、コンピュータグラフィックスでよく使われる 2π÷360 を 1 とする単位です
360 度は 2π と表され、n × π ÷ 180 を求めることで度数 n をラジアンに変換できます

DirectX Graphics の数学関数では D3DXToRadian() マクロを提供しています
このマクロは、指定した度をラジアンに変換します

D3DXToRadian( degree ) ((degree) * (D3DX_PI / 180.0f))

n × π ÷ 180 という計算を行い、ラジアンに変換しているのがわかります
degree にはラジアンに変換する度数を指定します

ビュー変換と射影変換の設定が終われば、これで視点が切り替わります
クライアント領域の中央がカメラの注目する焦点となり
従来の、2次元的な空間概念ではなくなることに注意してください
とくに、Y 座標はスクリーン座標と反転し、上に行くほど値が増えることに気をつけましょう

今回は、カメラのワールド座標値を Y 軸を基点に回転させるプログラムを作ります
軸に対する回転とは、すなわち以下のような回転の軌跡を描くということです



この軸に対する特定の角度の位置を表す行列を取得するには
D3DXMatrixRotationX()、D3DXMatrixRotationY()
D3DXMatrixRotationZ() 関数らを用いて生成することができます
それぞれ、順に X 軸、Y 軸、Z 軸回りの回転行列を作成する数学関数です
D3DXMATRIX* D3DXMatrixRotationX(
	D3DXMATRIX* pOut , FLOAT Angle
);

D3DXMATRIX* D3DXMatrixRotationY(
	D3DXMATRIX* pOut , FLOAT Angle
);

D3DXMATRIX* D3DXMatrixRotationZ(
	D3DXMATRIX* pOut , FLOAT Angle
);
pOut は演算結果を格納する D3DXMATRIX 構造体へのポインタを
Angle は角度をラジアン単位で指定します
この時、角度は回転軸から原点向かって時計回りに計測されます
戻り値は作成した行列のポインタで、pOut と同じ値です

ビュー変換の焦点と上を固定し、カメラの位置だけを回転させることによって
焦点を基点として3次元空間を回転移動させることができます
#include <windows.h>
#include <d3d8.h>
#include <d3dx8.h>

#define TITLE TEXT("Kitty on your lap")

IDirect3D8 * pDirect3D;
IDirect3DDevice8 * pD3Device;
D3DPRESENT_PARAMETERS d3dpp;
D3DXVECTOR3 view(0 , 0 , 600);

typedef struct {
	float x,y,z;
	DWORD color;
} D3DVERTEX;

void SetRender(HWND hWnd) {
	RECT rect;
	D3DXMATRIX d3dm;

	GetClientRect(hWnd,&rect);
	D3DXMatrixLookAtLH(&d3dm , &view ,
		&D3DXVECTOR3(10 , -200 , 0) , &D3DXVECTOR3(0 , 1 , 0));
	pD3Device->SetTransform(D3DTS_VIEW , &d3dm);
	
	D3DXMatrixPerspectiveFovLH(&d3dm , D3DXToRadian(45.0) ,
		(float)rect.right / (float)rect.bottom , 0 , 1000);
	pD3Device->SetTransform(D3DTS_PROJECTION , &d3dm);

	pD3Device->SetRenderState(D3DRS_CULLMODE , D3DCULL_NONE);
	pD3Device->SetRenderState(D3DRS_LIGHTING , FALSE);
}

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	static D3DVERTEX pt[3] = {
		{200 , -10 , 0 , 0xFFFF0000} ,
		{400 , -200 , 0 , 0xFF00FF00} ,
		{10 , -200 , 0 , 0xFF0000FF}
	};
	D3DXMATRIX d3dm;
	D3DXVECTOR3 d3dv;

	switch(msg){
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		case WM_CREATE:
			SetTimer(hWnd , 1 , 10 , NULL);
			return 0;
		case WM_PAINT:
			pD3Device->Clear(0 , NULL , D3DCLEAR_TARGET ,
				D3DCOLOR_XRGB(0xFF , 0xFF , 0xFF) , 1.0 , 0);
			pD3Device->BeginScene();
			pD3Device->SetVertexShader(D3DFVF_XYZ | D3DFVF_DIFFUSE);
			pD3Device->DrawPrimitiveUP(
				D3DPT_TRIANGLELIST , 1 , pt , sizeof (D3DVERTEX));
			pD3Device->EndScene();

			pD3Device->Present(NULL,NULL,NULL,NULL);
			ValidateRect(hWnd,NULL);
			break;
		case WM_SIZE:
			if(pD3Device && (wp == SIZE_RESTORED || wp == SIZE_MAXIMIZED)) {
				d3dpp.BackBufferWidth = LOWORD(lp);
				d3dpp.BackBufferHeight = HIWORD(lp);
				pD3Device->Reset(&d3dpp);

				SetRender(hWnd);
				InvalidateRect(hWnd,NULL,TRUE);
			}
			return 0;
		case WM_TIMER:
			D3DXMatrixRotationY(&d3dm , D3DXToRadian(2));
			d3dv = view;
			view.x = d3dv.x * d3dm.m[0][0] +
				d3dv.y * d3dm.m[1][0] + d3dv.z * d3dm.m[2][0];
			view.y = d3dv.x * d3dm.m[0][1] +
				d3dv.y * d3dm.m[1][1] + d3dv.z * d3dm.m[2][1];
			view.z = d3dv.x * d3dm.m[0][2] +
				d3dv.y * d3dm.m[1][2] + d3dv.z * d3dm.m[2][2];
	
			D3DXMatrixLookAtLH(&d3dm , &view ,
				&D3DXVECTOR3(10 , -200 , 0) , &D3DXVECTOR3(0 , 1 , 0));
			pD3Device->SetTransform(D3DTS_VIEW , &d3dm);
			InvalidateRect(hWnd , NULL , TRUE);
			return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance ,HINSTANCE hPrevInstance ,
				   LPSTR lpCmdLine,int nCmdShow) {
	MSG msg;
	HWND hWnd;
	WNDCLASS winc;
	D3DDISPLAYMODE dmode;

	pDirect3D = Direct3DCreate8(D3D_SDK_VERSION);
	pDirect3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&dmode);

	ZeroMemory(&d3dpp , sizeof (d3dpp));
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = dmode.Format;
	d3dpp.BackBufferCount = 1;

	winc.style			= CS_CLASSDC;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra		= winc.cbWndExtra		= 0;
	winc.hInstance		= hInstance;
	winc.hIcon			= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL,IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if(!RegisterClass(&winc)) return 0;

	hWnd = CreateWindow(TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE , 
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			(HWND)NULL,(HMENU)NULL , hInstance , (LPSTR)NULL
	);

	pDirect3D->CreateDevice(
		D3DADAPTER_DEFAULT , D3DDEVTYPE_HAL , hWnd ,
		D3DCREATE_SOFTWARE_VERTEXPROCESSING , &d3dpp , &pD3Device
	);

	SetRender(hWnd);

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	pDirect3D->Release();
	pD3Device->Release();
	return msg.wParam;
}


このプログラムは、前回までの単純な2次元三角形を描画し
カメラ視点に切り替えて、三角形の左下の頂点を基点にその周囲を回ります

座標系が異なるため、三角形の Y 座標は反転して負数にしていますが
三角形のワールド座標値は、前回と同じであることに注目してください
ウィンドウが生成されるとタイマーを設定し、WM_TIMER が定期的に送られるようにし
カメラを Y 軸を基軸に 2 度ずつその周囲を回転させています

この結果、三角形の青い頂点を中心として、三角形が回転しているように見えます
このような処理は、まさに3次元プログラミングの基本的な変換処理といえます
この時、座標系はクライアント領域の左上が原点ではないことにも注意してください
カメラ座標では、描画領域の中心がカメラの焦点の座標となっています
2次元プログラミングに慣れすぎている人は、この座標系の変換に戸惑うかもしれません

ビュー変換と回転、平行移動などの処理を上手く利用すれば
例えば、マウスのドラッグで視点が変わるというプログラムを作ることができます
こうすれば、ユーザーは自由に3次元ワールド空間を移動することができるようになります
#include <windows.h>
#include <d3d8.h>
#include <d3dx8.h>

#define TITLE TEXT("Kitty on your lap")

IDirect3D8 * pDirect3D;
IDirect3DDevice8 * pD3Device;
D3DPRESENT_PARAMETERS d3dpp;
D3DXVECTOR3 view(0 , 0 , 600);

typedef struct {
	float x,y,z;
	DWORD color;
} D3DVERTEX;

void SetRender(HWND hWnd) {
	RECT rect;
	D3DXMATRIX d3dm;

	GetClientRect(hWnd,&rect);
	D3DXMatrixLookAtLH(&d3dm , &view ,
		&D3DXVECTOR3(10 , -200 , 0) , &D3DXVECTOR3(0 , 1 , 0));
	pD3Device->SetTransform(D3DTS_VIEW , &d3dm);
	
	D3DXMatrixPerspectiveFovLH(&d3dm , D3DXToRadian(45.0) ,
		(float)rect.right / (float)rect.bottom , 0 , 1000);
	pD3Device->SetTransform(D3DTS_PROJECTION , &d3dm);

	pD3Device->SetRenderState(D3DRS_CULLMODE , D3DCULL_NONE);
	pD3Device->SetRenderState(D3DRS_LIGHTING , FALSE);
}

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	static D3DVERTEX pt[3] = {
		{200 , -10 , 0 , 0xFFFF0000} ,
		{400 , -200 , 0 , 0xFF00FF00} ,
		{10 , -200 , 0 , 0xFF0000FF}
	};
	static POINT from;
	static BOOL blDown = FALSE;
	D3DXMATRIX d3dm;
	D3DXVECTOR3 d3dv;

	switch(msg){
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		case WM_PAINT:
			pD3Device->Clear(0 , NULL , D3DCLEAR_TARGET ,
				D3DCOLOR_XRGB(0xFF , 0xFF , 0xFF) , 1.0 , 0);
			pD3Device->BeginScene();
			pD3Device->SetVertexShader(D3DFVF_XYZ | D3DFVF_DIFFUSE);
			pD3Device->DrawPrimitiveUP(
				D3DPT_TRIANGLELIST , 1 , pt , sizeof (D3DVERTEX));
			pD3Device->EndScene();

			pD3Device->Present(NULL,NULL,NULL,NULL);
			ValidateRect(hWnd,NULL);
			break;
		case WM_SIZE:
			if(pD3Device && (wp == SIZE_RESTORED || wp == SIZE_MAXIMIZED)) {
				d3dpp.BackBufferWidth = LOWORD(lp);
				d3dpp.BackBufferHeight = HIWORD(lp);
				pD3Device->Reset(&d3dpp);

				SetRender(hWnd);
				InvalidateRect(hWnd,NULL,TRUE);
			}
			return 0;
		case WM_LBUTTONDOWN:
			from.x = LOWORD(lp);
			from.y = HIWORD(lp);
			blDown = TRUE;
			SetCapture(hWnd);
			return 0;
		case WM_LBUTTONUP:
			blDown = FALSE;
			ReleaseCapture();
			return 0;
		case WM_MOUSEMOVE:
			if (!blDown) return 0;
			D3DXMatrixRotationY(&d3dm , D3DXToRadian(
				from.x - LOWORD(lp) == 0 ? 0 : from.x - LOWORD(lp) > 0 ? 4 : -4));
			d3dv = view;
			view.x = d3dv.x * d3dm.m[0][0] +
				d3dv.y * d3dm.m[1][0] + d3dv.z * d3dm.m[2][0];
			view.y = d3dv.x * d3dm.m[0][1] + 
				d3dv.y * d3dm.m[1][1] + d3dv.z * d3dm.m[2][1];
			view.z = d3dv.x * d3dm.m[0][2] +
				d3dv.y * d3dm.m[1][2] + d3dv.z * d3dm.m[2][2];
			view.y += from.y - (lp >> 16);

			D3DXMatrixLookAtLH(&d3dm , &view ,
				&D3DXVECTOR3(10 , -200 , 0) , &D3DXVECTOR3(0 , 1 , 0));
			pD3Device->SetTransform(D3DTS_VIEW , &d3dm);
			InvalidateRect(hWnd , NULL , TRUE);

			from.x = LOWORD(lp);
			from.y = lp >> 16;
			return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance ,HINSTANCE hPrevInstance ,
				   LPSTR lpCmdLine,int nCmdShow) {
	MSG msg;
	HWND hWnd;
	WNDCLASS winc;
	D3DDISPLAYMODE dmode;

	pDirect3D = Direct3DCreate8(D3D_SDK_VERSION);
	pDirect3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&dmode);

	ZeroMemory(&d3dpp , sizeof (d3dpp));
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = dmode.Format;
	d3dpp.BackBufferCount = 1;

	winc.style			= CS_CLASSDC;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra		= winc.cbWndExtra		= 0;
	winc.hInstance		= hInstance;
	winc.hIcon			= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL,IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if(!RegisterClass(&winc)) return 0;

	hWnd = CreateWindow(TEXT("KITTY") , TITLE ,
					WS_OVERLAPPEDWINDOW | WS_VISIBLE , 
					CW_USEDEFAULT , CW_USEDEFAULT ,
					CW_USEDEFAULT , CW_USEDEFAULT ,
					(HWND)NULL,(HMENU)NULL , hInstance , (LPSTR)NULL);

	pDirect3D->CreateDevice(
		D3DADAPTER_DEFAULT , D3DDEVTYPE_HAL , hWnd ,
		D3DCREATE_SOFTWARE_VERTEXPROCESSING , &d3dpp , &pD3Device
	);

	SetRender(hWnd);

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	pDirect3D->Release();
	pD3Device->Release();
	return msg.wParam;
}
これは、マウスによる簡単な回転処理を導入した3次元プログラムです
ユーザーは、マウスをドラッグすることによって視点を自由に替えることができます
X 軸に対する回転は、マウスの縦の移動距離を Y 座標に加算して実現しています

WM_MOUSEMOVE での、マウスの Y 座標の取得はシフトで行っていますが
これは、HIWORD() マクロで行った場合 0 以下の値が取得できなくなるためです
そうでなければ、クライアント領域よりも上にドラッグされると、座標が狂ってしまいます


IDirect3DDevice8::SetRenderState()

HRESULT SetRenderState(D3DRENDERSTATETYPE State , DWORD Value);

デバイスのレンタリングステート変数を設定します

State - D3DRENDERSTATETYPE 列挙型で設定するステートを指定します
Value - 設定する値を指定します。その意味は State によって異なります

戻り値 - 成功した場合は D3D_OK、そうでなければ D3DERR_INVALIDCALL

IDirect3DDevice8::SetTransform()

HRESULT SetTransform(
	D3DTRANSFORMSTATETYPE State , CONST D3DMATRIX* pMatrix
);
デバイスの変換ステート変数を設定します

State - 変換対象のステート変数、または D3DTS_WORLDMATRIX() マクロを指定します
pMatrix - 設定する変換行列を表す D3DMATRIX 構造体へのポインタを指定します

戻り値 - 成功した場合は D3D_OK、そうでなければ D3DERR_INVALIDCALL

D3DTS_WORLDMATRIX()

D3DTS_WORLDMATRIX(index) (D3DTRANSFORMSTATETYPE)(index + 256)

ワールド座標系の変換行列を指定するマクロです

index - 0〜255 までの値で設定する行列を指定します

D3DTS_WORLD()

D3DTS_WORLD D3DTS_WORLDMATRIX(0)

ワールド座標系の変換行列を指定するマクロです
単純にインデックス 0 番を指定しています

D3DXMatrixLookAtLH()

D3DXMATRIX* D3DXMatrixLookAtLH(
	D3DXMATRIX* pOut,
	CONST D3DXVECTOR3* pEye , CONST D3DXVECTOR3* pAt ,
	CONST D3DXVECTOR3* pUp
);
ビュートランスフォーム用の左手座標系変換行列を生成します

pOut - 作成する行列へのポインタを指定します
pEye - カメラの位置をワールド座標系で指定します
pAt - カメラが注目する焦点をワールド座標系で指定します
pUp - ワールド座標形のカメラの上部を表す位置を指定します

戻り値 - 作成した行列のポインタで、pOut と同じ値です

D3DXMatrixLookAtRH()

D3DXMATRIX* D3DXMatrixLookAtRH(
	D3DXMATRIX* pOut,
	CONST D3DXVECTOR3* pEye , CONST D3DXVECTOR3* pAt ,
	CONST D3DXVECTOR3* pUp
);
ビュートランスフォーム用の右手座標系変換行列を生成します

pOut - 作成する行列へのポインタを指定します
pEye - カメラの位置をワールド座標系で指定します
pAt - カメラが注目する焦点をワールド座標系で指定します
pUp - ワールド座標形のカメラの上部を表す位置を指定します

戻り値 - 作成した行列のポインタで、pOut と同じ値です

D3DXMatrixPrespectiveFovLH()

D3DXMATRIX* D3DXMatrixPerspectiveFovLH(
	D3DXMATRIX* pOut,
	FLOAT fovy , FLOAT Aspect ,
	FLOAT zn , FLOAT zf
);
視野 (FOV) に基づいて、右手座標系パースペクティブ射影行列を作成する

pOut - D3DXMATRIX 構造体へのポインタを指定します
fovy - 視野角をラジアン単位で指定します
Aspect - 長方形のアスペクト比を指定するパラメータです
zn - 最も近い面の Z 座標を指定します
zf - 最も遠い面の Z 座標を指定します

戻り値 - 作成した行列のポインタで、pOut と同じ値です

D3DXMatrixPrespectiveFovRH()

D3DXMATRIX* D3DXMatrixPerspectiveFovRH(
	D3DXMATRIX* pOut,
	FLOAT fovy , FLOAT Aspect ,
	FLOAT zn , FLOAT zf
);
視野 (FOV) に基づいて、右手座標系パースペクティブ射影行列を作成する

pOut - D3DXMATRIX 構造体へのポインタを指定します
fovy - 視野角をラジアン単位で指定します
Aspect - 長方形のアスペクト比を指定するパラメータです
zn - 最も近い面の Z 座標を指定します
zf - 最も遠い面の Z 座標を指定します

戻り値 - 作成した行列のポインタで、pOut と同じ値です

D3DXToRadian()

D3DXToRadian( degree ) ((degree) * (D3DX_PI / 180.0f))

度数をラジアンに変換します

degree - ラジアンに変換する度数を指定します

D3DXMatrixRotationX()

D3DXMATRIX* D3DXMatrixRotationZ(
	D3DXMATRIX* pOut , FLOAT Angle
);
X 軸を回転軸として回転した変換行列を作成します

pOut - 演算結果を格納する D3DXMATRIX 構造体へのポインタを指定します
Angle - 角度をラジアン単位で指定します

戻り値 - 作成した行列のポインタで、pOut と同じ値です

D3DXMatrixRotationY()

D3DXMATRIX* D3DXMatrixRotationZ(
	D3DXMATRIX* pOut , FLOAT Angle
);
Y 軸を回転軸として回転した変換行列を作成します

pOut - 演算結果を格納する D3DXMATRIX 構造体へのポインタを指定します
Angle - 角度をラジアン単位で指定します

戻り値 - 作成した行列のポインタで、pOut と同じ値です

D3DXMatrixRotationZ()

D3DXMATRIX* D3DXMatrixRotationZ(
	D3DXMATRIX* pOut , FLOAT Angle
);
Z 軸を回転軸として回転した変換行列を作成します

pOut - 演算結果を格納する D3DXMATRIX 構造体へのポインタを指定します
Angle - 角度をラジアン単位で指定します

戻り値 - 作成した行列のポインタで、pOut と同じ値です



前のページへ戻る次のページへ