0から学ぶ環境マップ約2年ぶりのシェーダの記事となりました(^-^;。機会があったためまとめておこうと思った今回のテーマは環境マップです。環境マップとはモデルの表面に周囲の背景等が映り込む様子を再現した物で、その手法その物やそれに必要なテクスチャの両方を指すようです。鏡や磨かれた柱、金属製の時計などなど、私たちの周りには鏡面性が高いために周りの景色を反射している物が沢山存在しています。私たちは知らず知らずそれらを見て「磨かれた金属っぽい」などの質感を感じています。これが画面にあると強烈な現実味を醸しだすようになります:上は今回用意したサンプルの実行結果です。これをどのように実現するのか?実は結構簡単なんです。目→モデル表面反射→背景のどこか今、私たちの目線から物の表面のある一点へ視線を飛ばすとします。その視線は面に入射し、反射します。反射した後の私たちの視線は、きっと遥かかなたに飛んで行って、いずれ何かに衝突します。そこには少なくとも何らかの色があるはずです。今度は逆にその当たった点から物の反射点までレイを飛ばします。レイは表面で反射して、きっと私たちの目に飛び込んできます。これにより私たちは物の表面に「色」を感じるわけです:では、これをシェーダで考えてみます。頂点シェーダにはローカル空間での頂点座標が引数として入ってきます。これにワールド変換行列を掛けるとワールド空間に移動します。今カメラのワールド位置があるとして、その位置からワールド空間頂点に対してレイを飛ばします。このレイの方向が「視線ベクトル」となります。頂点にはもう一つ「法線」の情報もあります。法線は面の向きを表しますね。言う事は、視線ベクトルが頂点に当たった後「どちらに反射するか(反射ベクトル)」を簡単に計算できてしまいます。後はその反射した先にある景色から色をもらえばいいんです。「まてまて、『反射した先にある景色の色』って、シェーダ内で周囲のモデルの情報は取れないでしょ」とお気付きの方はすばらです。その通りでして、シェーダ内ではワールドにある物の情報は普通取れません。シェーダで取得可能な色情報は頂点カラーと「貼り付けるテクスチャ」のみです。そこでこう考えます、「世界は大きな立方体だ!」と。すると、世界(背景)の色情報は6面のテクスチャにすべて表現されます:シェーダ内で立方体を想定すれば、具体的なモデルの情報をシェーダに渡す必要はありません。後は、視線の先にあるだろうでっかいテクスチャのUVを直接指定して、その色を出力すれば、モデルの表面に周囲の色が反射したように見えます。この立方体の内側に貼り付いているテクスチャこそ「環境マップ」です。reflect関数を使えば簡単!上のようなテクスチャは特に「キューブマップ」と呼ばれています。いう6面のテクスチャをIDirect3DTexture9を6つ使って表現することもできますが、DirectXにはキューブマップ専用のIDirect3DCubeTextureというインターフェイスが用意されています。これを使うと非常に簡単に環境マップ(キューブ環境マップ)を実現出来ます。*ppCubeTexture);pDeviceは描画デバイスです。pSrcFileにはキューブマップの画が入った画像ファイル名を指定します。ファイルは.ddsフォーマットである必要があります。ppCubeTextureにキューブマップオブジェクトへのポインタが返ります。この関数で扱えるファイルは.ddsフォーマットファイルです。Tool(Dxtex.exe)で他の画像ファイルから作ることができます。このツールでキューブマップテクスチャも作成可能で、上の図にあるような6面の「べき乗サイズの」背景画を用意しておいてTool内に連続で読み込ませると作れます(詳しくはツールを使ってみて下さい)。して作成したキューブマップテクスチャをシェーダに渡します。vReflect);}サンプルの殆どは変数やサンプラなどの宣言で、実際のシェーダコードは極めて短いです!特にピクセルシェーダを御覧下さい。これで冒頭の環境マップが実現出来てしまいます。これはシェーダ自体がキューブテクスチャからのサンプリングをサポートしてくれているためです。キューブマップはtextureCUBEというテクスチャ型で与えられます。このテクスチャ専用のサンプラがsamplerCUBEです。texCUBE関数を通してsamplerCUBEサンプラに対して「反射ベクトル」を与えると、そのベクトルの先にあるキューブマップの色をサクっと取ってきてくれるんです。これにより、反射ベクトルと立方体の交点を求めて、それをUVに変換して云々...という面倒な計算が全部省略できてしまいます。その反射ベクトルを求めるにはどうするか?実はこれも簡単で「reflect関数」を使います。この関数は、視線ベクトルと面の法線ベクトルから、視線が面に反射した後の進行方向である反射ベクトルをサクっと求めてくれます。言うことで、ピクセルシェーダはとっても簡単なんです。視線ベクトルはカメラの位置とカメラが見ている「点」がわかれば求められます。これは頂点シェーダで求めます。そのため、上の例では頂点シェーダにカメラのワールド位置を与えています。頂点をワールド空間に移動した後、「頂点位置-カメラ位置」で視線ベクトルを作っています。それをTEXCOORD1としてピクセルシェーダに引渡します。ついでに法線もピクセルシェーダに渡します。4);cube->DrawSubset(0);当初色々面倒なのかなと思っていた環境マップは、キューブマップさえ用意できれば驚くほど簡単な実装で実現できることがわかりました。ゲームのリアリティをこんなにサクっと高められるのであれば、積極的に使っていきたいもんです(^-^)。