環境マッピング

Microsoft Corporation

DirectX Developer Center Home Page に戻る

cpp.jpg envcube.cpp DirectX 7.0 SDK のキューブ環境マッピング サンプルコード(日本語コメント)

CPP.JPG spheremap.cpp DirectX 7.0 SDK の球状環境マッピング サンプルコード(日本語コメント)

環境マッピング

このセクションでは、Direct3D 直接モードで使用される 2 つの一般的なタイプの環境マッピングを行う方法について説明する。グラフィックスの業界では、タイプの環境マッピングが使われているが、次のトピックでは、もっとも一般的な2つの形式である、キューブ環境マッピングと球状環境マッピングを主に取り上げる。

  • 環境マッピングとは? 

  • キューブ環境マッピング 

  • 球状環境マップ

環境マッピングとは?

環境マッピングは、レイトレーシングを使わずに反射率の高いサーフェスをシミュレートするテクニックである。実際には、環境マッピングはオブジェクトを囲むシーンの画像を含む特殊なテクスチャを、オブジェクト自体に適用している。その結果は反射面の特徴をよく表しており、膨大な計算を必要とするレイトレーシングを使わずに、十分実用的なものである。

次のイメージは Spheremap サンプル( C++ )のものである。サンプル名でも分かるように、"球状環境マッピング" というタイプの環境マッピングを使用している。詳細については「球状環境マッピング」を参照すること。

wpe82.jpg

上の図のティーポットには周囲の状況が映り込んでいるように見えるが、実際にはテクスチャがオブジェクトに適用されている。環境マッピングはテクスチャを使うため(特別に計算されたテクスチャ座標で貼り付けられる)リアルタイムに実行することが可能である。

キューブ環境マッピング

キューブ環境マッピングは、立方体の 6 つの内壁に映ったオブジェクトの周囲の空間のイメージやライティング効果をテクスチャとしてオブジェクトにマップする。このセクションの内容は、次の6つのトピックに分けられる。

  • キューブ環境マップとは?  

  • キューブ環境マップサーフェスの作成  

  • キューブ環境マップサーフェイスへのアクセス  

  • ミップマップされたキューブ環境マップ  

  • キューブ環境マップへのレンダリング ** **

  • キューブ環境マップ用のテクスチャ座標  

キューブ環境マップとは?

キューブ環境マップは(くだけた言い方で "キューブ マップ" と呼ばれることも多い)オブジェクトを囲むシーンを表すイメージデータを含んだテクスチャであり、オブジェクトはあたかもその立方体の中央に位置しているかのように見ることができる。キューブ環境マップには 6 つのサーフェイスがあり、各サーフェイスは、垂直および水平方向に視野の 90 度をカバーする。各サーフェイスの配置は、次の図のとおりである。

envmap1.jpg

立方体の各サーフェイスはワールド空間内で x/y、y/z、x/z の各平サーフェイスに対して垂直に位置する。次の図は、各サーフェイスと立方体の各面の対応を示したものである。

envmap2.jpg
キューブ環境マップはアタッチされた一連のテクスチャ サーフェスとして実装され、単一の呼び出しで作成することができ、他のアタッチされたサーフェスと同じようにアクセスすることができる。アプリケーションは、キューブ環境マッピングに、静的なイメージを使用できるが、そればかりではなく、キューブ マップのサーフェイスにレンダリングを行って動的な環境マッピングを行うこともできる(このテクニックでは、キューブ マップのサーフェイスが DDSCAPS_3DDEVICE 機能で作られたレンダリング対象として有効なサーフェスでなければならない)。

キューブマップのサーフェイスは、それほど詳細な周囲のシーンのレンダリングを含んでいる必要はない。ほとんどの場合、環境マップは湾曲したサーフェスに適用される。ほとんどのアプリケーションで使われる曲率では、反射の映像が歪むため、非常に精度の高い環境マップを作っても意味が無く、メモリやレンダリングのオーバーヘッドが無駄になる。

キューブ環境マップサーフェスの作成

C++ または DirectDraw7 では、IDirectDraw7::CreateSurface メソッドを使ってキューブ環境マップを作成する。Visual BasicではCreateSurface を使う。キューブ マップは複合サーフェスである。これらは単一のコールで DirectDraw によって作られた 1 群のアタッチされたサーフェスでなければならない。個々に作られたサーフェスを相互にアタッチしてキューブ環境マップを作ることはできない。キューブ環境マップのテクスチャは正方形で、2 のべき乗のサイズでなければならない。

次のコードは C++ アプリケーションで簡単なキューブ環境マップを作る方法を示している。

// この例で、pDD 変数はIDirectDraw7インターフェイスへの
// 有効なポインタ
DDSURFACEDESC2 ddsd;
ZeroMemory((LPVOID)&ddsd, sizeof(DDSURFACEDESC2));
ddsd.dwSize         = sizeof(DDSURFACEDESC2);
ddsd.dwFlags        = DDSD_CAPS | DDSD_WIDTH | 
                      DDSD_HEIGHT | DDSD_PIXELFORMAT;
ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE;
// ピクセル フォーマットを、ここで有効なテクスチャフォーマットに設定.
// サイズは任意であるが、2の累乗でなければならない
ddsd.dwWidth  = 64;  
ddsd.dwHeight = 64; 
// キューブ マップ テクスチャの能力を設定
// このテクスチャは有効なレンダリング対象
ddsd.ddsCaps.dwCaps  = DDSCAPS_COMPLEX | DDSCAPS_3DDEVICE | 
                       DDSCAPS_TEXTURE;
ddsd.ddsCaps.dwCaps2 = DDSCAPS2_CUBEMAP|DDSCAPS2_CUBEMAP_ALLFACES;
LPDIRECTDRAWSURFACE7 pddsCubeMap;
// キューブ マップの作成.
if( FAILED( pDD->CreateSurface( &ddsd, pddsCubeMap, NULL ) ) )
{
    // ここにエラー処理のコード
}                                     

注 キューブマップのサーフェイスにレンダリングを行う場合は、サーフェスを作成する際に与えるサーフェス記述に DDSCAPS_3DDEVICE 能力が含まれていなければならない。

キューブ マップは管理されるテクスチャになることもできる。サーフェスを作成する時に DDSCAPS2_TEXTUREMANAGE または DDSCAPS2_TEXTUREMANAGE のどちらかの能力フラグを含めた場合、結果のテクスチャは管理される。

キューブ環境マップサーフェイスへのアクセス

他の複合サーフェスと同様に、IDirectDrawSurface7::GetAttachedSurface メソッドを使って、キューブ環境マップのサーフェイスの間を移動することができる。GetAttachedSurface メソッドは取得すべきアタッチされたサーフェスを記述する DDSCAPS2 構造体へのポインタを受け入れる。対応するサーフェイスを取得するには、dwCaps2 メンバを "DDSCAPS2_CUBEMAP_" で始まるいずれかのフラグ値に設定し、DDSCAPS2_CUBEMAP フラグと結合する。

次のコードは、正の y サーフェイス( face2 ) で使われるキューブマップのサーフェスを取得する。

// この例で、pddsMainFace 変数はキューブ マップが最初に作られたときに
// CreateSurface によって返された、サーフェイスへの有効なポインタを保持する。
LPDIRECTDRAWSURFACE7 pddsFace2;
DDSCAPS2 ddsCaps;
ddsCaps.dwCaps2 = DDSCAPS2_CUBEMAP_POSITIVEY | DDSCAPS2_CUBEMAP;
HRESULT hr;
hr = pddsMainFace->GetAttachedSurface( &ddsCaps,
                                       &pddsFace2);
if( FAILED(hr) )
{
    // ここにエラー処理のコード.
}                         

ミップマップされたキューブ環境マップ

キューブ マップはミップマップすることも可能である。DDSCAPS_MIPMAP 能力を含めた場合、システムは各サーフェイスをアタッチされたミップマップ サーフェスで作成する。これらのサーフェスは次のような図に表すことができる。 

envmap3.gif

ミップマップされたキューブ環境マップを作成するアプリケーションは、IDirectDrawSurface7::GetAttachedSurface メソッドを呼ぶことによって各サーフェイスにアクセスできる。、"DDSCAPS2_CUBEMAP" で始まるフラグのうちの1つを渡すことによって、対応するサーフェイスにアクセスする(「キューブ環境マップのサーフェイスへのアクセス」で説明)。そして、下位のミップマップ レベルはそのサーフェイスに対して IDirectDrawSurface7::GetAttachedSurface を呼び、DDSCAPS_MIPMAP フラグを渡すことによって取得できる。

キューブ環境マップへのレンダリング

DDSCAPS_3DDEVICE 能力を使ってキューブ環境マップを作成した場合、他のレンダリング対象のサーフェスとまったく同じように、キューブ マップの個々のサーフェイスに対してレンダリングを行う。サーフェイスに対してレンダリングを行う前にすべきもっとも重要なことは、それぞれのサーフェイスに対して、カメラが正しい位置に置かれ、正しい方向を向くようにトランスフォーム座標をセットすることである。それぞれのサーフェイスとは、前 (+z), 後(-z), 左(-x), 右(+x), 上(+y), 下(-y)である。

次の C++ コードでは、レンダリングされるサーフェイスに従ってビュー行列の準備と設定を行う。

//
// ppddsFaces 変数は、IDirectDrawSurface7 インターフェイス ポインタの配列のアドレス 
// 各サーフェイスに1つ
//
void RenderFaces(LPDIRECTDRAWSURFACE7* ppddsFaces)
{
    // キューブ マップの6つのサーフェイスに対して同じ処理を行うためのループ
    for( DWORD i=0; i<6; i++ )
    {
        DDSCAPS2 ddsc;
        ZeroMemory( (LPVOID)&ddsc, sizeof(DDSCAPS2) );
        // サーフェイスの能力を取得(これがどのサーフェイスかを調べるため)
        ppddsFaces[i]->GetCaps( &ddsc );
        // 下の方でオーバーライドされる標準のビュー
        D3DVECTOR vEnvEyePt = D3DVECTOR( 0.0f, 0.0f, 0.0f );
        D3DVECTOR vLookatPt, vUpVec;
        switch( ddsc.dwCaps2 & DDSCAPS2_CUBEMAP_ALLFACES )
        {
            case DDSCAPS2_CUBEMAP_POSITIVEX:
                vLookatPt = D3DVECTOR( 1.0f, 0.0f, 0.0f );
                vUpVec    = D3DVECTOR( 0.0f, 1.0f, 0.0f );
                break;
            case DDSCAPS2_CUBEMAP_NEGATIVEX:
                vLookatPt = D3DVECTOR(-1.0f, 0.0f, 0.0f );
                vUpVec    = D3DVECTOR( 0.0f, 1.0f, 0.0f );
                break;
            case DDSCAPS2_CUBEMAP_POSITIVEY:
                vLookatPt = D3DVECTOR( 0.0f, 1.0f, 0.0f );
                vUpVec    = D3DVECTOR( 0.0f, 0.0f,-1.0f );
                break;
            case DDSCAPS2_CUBEMAP_NEGATIVEY:
                vLookatPt = D3DVECTOR( 0.0f,-1.0f, 0.0f );
                vUpVec    = D3DVECTOR( 0.0f, 0.0f, 1.0f );
                break;
            case DDSCAPS2_CUBEMAP_POSITIVEZ:
                vLookatPt = D3DVECTOR( 0.0f, 0.0f, 1.0f );
                vUpVec    = D3DVECTOR( 0.0f, 1.0f, 0.0f );
                break;
            case DDSCAPS2_CUBEMAP_NEGATIVEZ:
                vLookatPt = D3DVECTOR( 0.0f, 0.0f,-1.0f );
                vUpVec    = D3DVECTOR( 0.0f, 1.0f, 0.0f );
                break;
        }
        D3DMATRIX matView;
        D3DUtil_SetViewMatrix( matView, vEnvEyePt, vLookatPt, vUpVec );
        g_pd3dDevice->SetTransform( D3DTRANSFORMSTATE_VIEW, &matView );   

キューブ環境マップの各サーフェイスは視野の 90 度を表しているということを思い出して欲しい。アプリケーションが(特殊効果などのために)異なる視野角を必要としない限り、これに従って射影行列を設定すること。

このコードは、ほとんどの一般的なケースで使われる射影行列を作成し、設定する。

        // 射影に90度の視野角を使用
        D3DMATRIX matProj;
        D3DUtil_SetProjectionMatrix( matProj, g_PI/2, 1.0f, 0.5f, 1000.0f );
        g_pd3dDevice->SetTransform( D3DTRANSFORMSTATE_PROJECTION, &matProj );

カメラの位置を決め、射影行列を設定したら、シーンをレンダリングできる。シーン内の各オブジェクトは、通常と同じように置くことができる。次のコードは、この作業の最初から最後までを示したものである。

//
        // シーンのレンダリング.
        // 
        // 深度バッファをサーフェイスにスワップし、レンダリング対象として設定
        // この関数は後で詳しく説明
        ChangeRenderTarget( ppddsFaces[i] );
        // zバッファを作成
        g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_ZBUFFER, 0x000000ff, 1.0f, 0L );
        // 環境マップの見栄えを良くするために、アンチエイリアシングを有効にする。
        g_pd3dDevice->SetRenderState( D3DRENDERSTATE_ANTIALIAS, 
                                      D3DANTIALIAS_TRANSLUCENTSORTINDEPENDENT );
        // シーン開始
        if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
        {
            // レンダリングされる各オブジェクトに対してワールド行列を設定することにより、
            // シーンのジオメトリを通常と同様に配置
            g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST,
                                         D3DFVF_XYZ |
                                         D3DFVF_NORMAL |
                                         D3DFVF_TEX1 |
                                         D3DFVF_TEXCOORDSIZE3(0),
                                         lpVertices, VERTEX_COUNT, 0);
            // シーン終了.
            g_pd3dDevice->EndScene();
        }
    }
}

注 ほとんどのアプリケーションで、キューブ環境マップのサーフェイスにレンダリングする際にはアンチエイリアシングを有効にすべきである。各サーフェイスは、比較的小さいので、レンダリング時にアンチエイリアシングを有効にすることによって、結果の環境マップの見栄えを大きく向上させることができる。

ChangeRenderTarget 関数への呼び出しに留意されたい。キューブ マップのサーフェイスにレンダリングする際に、当然ながら、サーフェイスをカレントのレンダリング対象サーフェイスとして割り当てる必要がある。深度バッファを使うアプリケーションは、レンダリング対象のための深度バッファを明示的に作るか、または、既存の深度バッファをレンダリング対象に再度割り当てることができる。次のコードは後者の方法を使っている。

HRESULT ChangeRenderTarget( LPDIRECTDRAWSURFACE7 pddsNewTarget)
{
    LPDIRECTDRAWSURFACE7 pddsOldRenderTarget = NULL;
    g_pd3dDevice->GetRenderTarget( &pddsOldRenderTarget );
    if( pddsOldRenderTarget )
    {
        LPDIRECTDRAWSURFACE7 pddsZBuffer = NULL;
        DDSCAPS2 ddscaps = { DDSCAPS_ZBUFFER, 0, 0, 0 };
        pddsOldRenderTarget->GetAttachedSurface( &ddscaps, &pddsZBuffer );
        if( pddsZBuffer )
        {
            pddsOldRenderTarget->DeleteAttachedSurface( 0, pddsZBuffer );
            pddsNewRenderTarget->AddAttachedSurface( pddsZBuffer );
            pddsZBuffer->Release();
        }
        pddsOldRenderTarget->Release();
    }
    g_pd3dDevice->SetRenderTarget( pddsNewRenderTarget, 0 );
    return S_OK;
}

キューブ環境マップ用のテクスチャ座標

キューブ環境マップをインデックスするテクスチャ座標は標準のテクスチャを適用する際に使う単純な u、 v スタイルの座標ではない。実際には、環境マップは、テクスチャ座標をまったく使用しない。テクスチャ座標の代わりに、キューブ環境マップでは 3-D ベクトルを使用する。頂点のフォーマットを正しく設定するように注意する必要がある。アプリケーションが何セットめのテクスチャ座標を使うかをシステムに伝えるだけでなく、各セットにいくつの要素があるかも伝える必要がある。Direct3D はこの目的のために、マクロセット D3DFVF_TEXCOORDSIZEn を提供している。これらのマクロは、サイズを記述しようとしているテクスチャ座標のインデックスを識別する単一のパラメータを受け入れる。3-D ベクトルの場合は、D3DFVF_TEXCOORDSIZE3 マクロで作られたビット パターンを含める。次のコードは、このマクロの使い方を示したものである。

//位置、法線、1組の3-Dテクスチャ座標を格納する頂点のための
//フレキシブル頂点フォーマット記述子を作成
DWORD dwFVF = D3DFVF_XYZ | D3DFVF_NORMAL | 
              D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0);

場合によっては、ディフューズ ライト マッピングなどのように、カメラ空間の頂点法線ベクトルを使用することもある。また別のケースでは、反射環境マッピングなどのように、反射ベクトルを使う(トランスフォームされた頂点法線は広く理解されいるので、ここでの情報は反射ベクトルの計算に重点を置いている)。

反射ベクトルを自分で計算するには、各頂点の位置と視点からその頂点へのベクトルを理解していることが必要である。Direct3Dはジオメトリの反射ベクトルを自動的に計算する。

この機能を使うことによって、メモリを節約でき(環境マップにテクスチャ座標を含める必要がない)、バンド幅を低減し、TnLHALデバイスの場合にはアプリケーションが独自に行う計算に比べて非常に高速になる。この機能を活用するには、キューブ環境マップを含むテクスチャ ステージで、単にD3DTSS_TEXCOORDINDEXテクスチャ ステージ状態を D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR フラグとテクスチャ座標セットのインデックスを組み合わせたものにセットすればよい(ディフューズ ライト マッピングなどの場合は、システムがテクスチャのベクトルのアドレッシングとして、トランスフォームされたカメラ空間の頂点法線を使うように、D3DTSS_TCI_CAMERASPACENORMAL フラグを使ってもよい)。インデックスはシステムがそのテクスチャのラッピング モードを調べるためだけに使われる。

次のコードは、この値の使い方を示したものである。

// pd3dDevice 変数は、IDirect3DDevice7インターフェイスへの 
// 有効なポインタ
// ステージ2のテクスチャ座標を自動的に生成する。
// これは、ステージ2がキューブマップに割り当てられていることを前提としている。
// (インデックス1にセットされたテクスチャ座標のラップ モードを使用)
pd3dDevice->SetTextureStageState( 2, D3DTSS_TEXCOORDINDEX, 
                                  D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR | 1);  

自動テクスチャ座標生成を有効にした場合、システムは2つの公式のうちの 1 つを使って各頂点の反射ベクトルを計算する。D3DRENDERSTATE_LOCALVIEWER レンダリング状態がTRUEにセットされている場合、次の公式が使われる。

GR8262.jpg

上の公式で、R は計算される反射ベクトル、E は頂点座標から視点への正規化されたベクトル、N はカメラ空間の頂点法線である。

D3DRENDERSTATE_LOCALVIEWER レンダリング状態がFALSEに設定されている場合、システムは次の公式を使用する。

GR8263.gif

この公式で R と N は、前の公式と同じである。Nz は、頂点法線のワールド空間 z、I は無限遠視点のベクトル( 0,0,1 )である。システムが選択したどちらかの公式からの反射ベクトルを使ってキューブマップの適切なサーフェイスをアドレスする。

注 ほとんどの場合、アプリケーションは頂点法線の自動正規化をオンにすべきである。そうするには、D3DRENDERSTATE_NORMALIZENORMALS を TRUE に設定すればよい。このレンダリング状態を有効にしなければ、環境マップの見栄えは期待したものとはかけ離れたものになってしまう。

球状環境マップ

球状環境マップ(スフィア マップと呼ばれることも多い)は、特別な形式のテクスチャ マップにエンコードされた、オブジェクトの周囲の空間の画像やライティング効果をテクスチャとしてオブジェクトに適用する。次のトピックは、スフィア マッピングのコンセプトについて説明し、Direct3D での使用方法に関する情報を提供する。

  • 球状環境マップとは?

  • 球状環境マップのテクスチャ座標

  • 球状環境マップの適用

球状環境マップとは?

球状環境マップ(スフィア マップとも言う)はオブジェクトの周囲のシーンの画像またはオブジェクトの周囲のライティング効果を含む特殊なテクスチャである。キューブ環境マップとは異なり、スフィア マップはオブジェクトの周囲を直接表すものではない。「環境マッピングとは?」のセクションのティーポットの画像はスフィア マッピングで実現できる反射効果を示している。

スフィア マップはオブジェクト周囲のシーン、360 度の視野を 2-D 表現したもので、魚眼レンズを使って撮影したものと同じである。次に Spheremap サンプル( C++ )で使われている球状マップを示す。

wpeC4.gif

球状環境マップのテクスチャ座標

環境マッピングを受ける各頂点に指定したテクスチャ座標は、サーフェスの曲面によって作り出された反射の歪みの関数としてテクスチャをアドレスする。アプリケーションは、各頂点に対応するこれらのテクスチャ座標を計算して必要な効果を実現しなければならない。テクスチャ座標を生成する、ある非常に単純で効果的な方法では、頂点法線を入力として使う。いくつかの方法があるが、次の公式が球状マップで環境マッピングを行うアプリケーションで一般に使われている。

GR8256.gif

上の公式で、u と v は計算されるテクスチャ座標、Nz と Ny はカメラ空間の頂点法線の x と y コンポーネントである。上の公式は、簡単ではあるが、効果的である。法線が正の x コンポーネントを持つ場合、法線は右を指し、u 座標は正しくテクスチャをアドレスできるように修正される。同様に、v 座標では正の y は法線が上を指すことを示す。当然ながら、それぞれのコンポーネントについて、負の値は逆の方向を指す。

法線が直接カメラを指している場合、結果となる座標はなんらの歪みも受けない。両座標への +0.5 のバイアスは、スフィア マップにゼロ点歪みを配置し、( 0, 0, z )の頂点法線はこのポイントをアドレスする。この公式は法線のzコンポーネントを考慮に入れていないことに注意されたい。公式を使用するアプリケーションは z 要素が正の法線の頂点をスキップすることによって計算を最適化しているのである。法線がカメラから遠ざかる方向を向いている(正の z )場合、オブジェクトのレンダリング時に頂点が間引かれるため、正しい結果が得られる。

球状環境マップの適用

環境マップは他のテクスチャと同じ方法でオブジェクトに適用できる。テクスチャを IDirect3DDevice7::SetTexture メソッドで適切なテクスチャ ステージに設定すればよい。最初のパラメータに目的のテクスチャ ステージのインデックスを指定し、2 番目のパラメータに環境マップのためのテクスチャを作成したときに返される IDirectDrawSurface7 インターフェイスのアドレスを設定する。必要に応じてカラーおよびアルファ ブレンディング操作や引数をセットし、目的のテクスチャ ブレンディング効果を得ることができる。