Vertex Shader:フォグ


~霧(直訳してどうする)~




■はじめに

さて、フォグをやっていなかったのでやっておきます。
遠くの景色ほど、白くかすんでいきます。これがフォグ(霧)です。
霧として使うだけでなく、空気感を演出するために使います。


今回のソースは、次のものです(DirectX8.1用です)。

まぁ、いつものように適当にファイルが入っています。
せっかくなんで、頂点シェーダーで組んでみました。

vs.vsh頂点シェーダープログラム。久しぶりに、ここに新しい要素が入る。
draw.cppメインの描画部分。
draw.h描画の各関数の定義。特に意味無いので出番無し。
main.h基本的な定数など。今回も出番無し。
main.cpp描画に関係しないシステム的な部分。変更が無いので、出番無し。
load.hファイルの読み込み。
load.cppファイルの読み込み。
bg.cpp地面+天円柱の描画。今回説明無し。
tile.bmp (床デカール) sky.bmp (空デカール)

あと、いつもの様に、モデルと、実行ファイル及び、プロジェクトファイルが入っています。

■フォグとは

あなたの周りは汚れています!!!
いや、掃除してないとかじゃなくて…
普段は、まったく気にしていませんが、身の周りの空気中には塵が漂っていたり、水が飛んでたりします(あなたがクリーンルームにいたらごめんなさい)。
空気中にごみがあるので、あなたの目には、途中のごみで散乱された光も飛び込んできます。
もちろん、本来入ってくる筈の光も散乱されます。


塵が多いほど、この散乱の影響は大きくなります。
塵が多いというのは、光が届くまでの距離が長いということです。 つまり、遠くにある物体ほど塵の影響を受け、霧が濃くかかります。 下の図で、使用前、使用後を見比べていただければ、 遠くに行くほど霞がかかることが確認できると思います。

使用前 使用後

■ソース解説

それでは、ソースコードにとっとと入ります。
今回は、初期化部分とシェーダープログラムにしか新しいことは入りません。
初期化部分は、下の黄色い部分が追加されます。

0174: //-----------------------------------------------------------------------------
0175: // Name: InitRender()
0176: // Desc: いろいろと初期化
0177: //-----------------------------------------------------------------------------
0178: HRESULT InitRender(LPDIRECT3DDEVICE8 lpD3DDev)
0179: {
0180:     HRESULT hr;
0181: 
0182:     // モデルの読み込み
0183:     if ( FAILED(hr = LoadXFile("nsx.x", lpD3DDev)) ) return hr;
0184:     
0185:     // バーテックスシェーダーを作成する
0186:     if ( FAILED(hr = LoadVertexShader("vs.vsh", lpD3DDev, &hVertexShader, dwDecl)) ) return hr;
0187: 
0188:     InitBg(lpD3DDev);
0189: 
0190:     lpD3DDev->SetRenderState(D3DRS_LIGHTING, FALSE);
0191: 
0192:     lpD3DDev->SetTextureStageState(0,D3DTSS_COLOROP,    D3DTOP_MODULATE);
0193:     lpD3DDev->SetTextureStageState(0,D3DTSS_COLORARG1,  D3DTA_TEXTURE);
0194:     lpD3DDev->SetTextureStageState(0,D3DTSS_COLORARG2,  D3DTA_DIFFUSE);
0195: 
0196:     lpD3DDev->SetVertexShader(hVertexShader);
0197:     lpD3DDev->SetVertexShaderConstant(12, &D3DXVECTOR4(0.0f, 0.5f, 1.0f, 2.0f), 1);
0198: 
0199:     // フォグの設定
0200:     float start = 0.0f;
0201:     float end   = 1.0f;
0202:     lpD3DDev->SetRenderState(D3DRS_FOGENABLE, TRUE);                // フォグ ブレンディングを有効にする。
0203:     lpD3DDev->SetRenderState(D3DRS_FOGCOLOR, 0x00ffffff);           // フォグの色を設定する。
0204:     lpD3DDev->SetRenderState(D3DRS_FOGSTART, *((DWORD *)(&start))); // 0 からかかりはじめる
0205:     lpD3DDev->SetRenderState(D3DRS_FOGEND,   *((DWORD *)(&end)) );  // 1で完全に見えなくなる
0206:     // 頂点シェーダーのフォグに関するパラメータ 
0207:     float Near =   0.0f;
0208:     float Far  = 100.0f;
0209:     lpD3DDev->SetVertexShaderConstant(20, &D3DXVECTOR4( -1.0f/(Far-Near), Far/(Far-Near), 0.0f, 0.0f), 1);
0210: 
0211:     return S_OK;
0212: }

D3DRS_FOGENABLE で、フォグを有効にして、D3DRS_FOGCOLOR で、色を設定します。 この色を変更すると、遠くにかすむときの色が変わります。
D3DRS_FOGSTART と、D3DRS_FOGEND は、フォグがかかり始める深度です。 Vertex shader で詳細に設定するので、とりあえず0から1にしました。
後は、Vertex shader で使う定数の設定です。次に説明します。

Vertex shader プログラムです。今まで使っていなかった oFog を使います。
追加するのは、2行だけです。

0001: ; c0-3   -- world + ビュー + 透視変換行列
0001: ; c4-7   -- world + ビュー行列
0004: ; c13    -- ライトのベクトル (w成分は環境光の強さ)
0005: ; c14    -- ライトの色(メッシュの色)
0007: ; c20    -- (-1/(Far-Near), Far/(Far-Near), 0,0) 
0009: ; v0    頂点の座標値
0010: ; v3    法線ベクトル (w成分は1.0f)
0011: ; v7    テクスチャ座標
0012: 
0013: vs.1.0
0014: 
0015: ;座標変換
0016: dp4 oPos.x, v0, c0
0017: dp4 oPos.y, v0, c1
0018: dp4 oPos.z, v0, c2
0019: dp4 oPos.w, v0, c3
0020: 
0021: ; ランバート diffuse
0022: dp4 r0.w,   v3,  c13            ; l・n
0023: mul oD0,    c14,  r0.w          ; ライトの色(メッシュの色付き)をつける
0024: 
0025: ; テクスチャーを張る
0026: mov oT0, v7
0027: 
0028: ; フォグの計算
0029: dp4 r0.x,   v0, c6
0030: mad oFog.x, r0.x, c20.x, c20.y  ; fog = (Far-z)/(Far-Near)

dp4 命令を使って、カメラの向きがz軸を向いているビュー空間に変換し、カメラ深度を求めます。
次に、線形変換をして、フォグの値を調整します。
フォグの値の調整は、D3DRS_FOGSTART、D3DRS_FOGEND で調整した値。 すなわち、フォグがかかり始める状態の時に0,フォグが完全にかかった状態の時に1になるように設定します。

Near がワールド座標で、フォグがかかり始めるz値、Far が完全にフォグがかかるz値です。
ちなみに、値がそれらの範囲を超えた場合には、ハードウェアが自動的にかからない状態、完全にかかった状態にクリップしてくれます。

あとは、ハードウェアが画面に書き込むときに、ピクセルごと

 = oFog.x  + (1-oFog.x) 

の加工をして、フォグをかけます。

■最後に

さて、久しぶりに簡単なプログラムでした。
次は、このフォグを少し変化させてみたいと思います。




もどる

imagire@gmail.com