さて、フォグをやっていなかったのでやっておきます。
遠くの景色ほど、白くかすんでいきます。これがフォグ(霧)です。
霧として使うだけでなく、空気感を演出するために使います。
今回のソースは、次のものです(DirectX8.1用です)。
まぁ、いつものように適当にファイルが入っています。
せっかくなんで、頂点シェーダーで組んでみました。
vs.vsh | 頂点シェーダープログラム。久しぶりに、ここに新しい要素が入る。 |
draw.cpp | メインの描画部分。 |
draw.h | 描画の各関数の定義。特に意味無いので出番無し。 |
main.h | 基本的な定数など。今回も出番無し。 |
main.cpp | 描画に関係しないシステム的な部分。変更が無いので、出番無し。 |
load.h | ファイルの読み込み。 |
load.cpp | ファイルの読み込み。 |
bg.cpp | 地面+天円柱の描画。今回説明無し。 |
あと、いつもの様に、モデルと、実行ファイル及び、プロジェクトファイルが入っています。
あなたの周りは汚れています!!!
それでは、ソースコードにとっとと入ります。
D3DRS_FOGENABLE で、フォグを有効にして、D3DRS_FOGCOLOR で、色を設定します。
この色を変更すると、遠くにかすむときの色が変わります。
Vertex shader プログラムです。今まで使っていなかった oFog を使います。
dp4 命令を使って、カメラの向きがz軸を向いているビュー空間に変換し、カメラ深度を求めます。
Near がワールド座標で、フォグがかかり始めるz値、Far が完全にフォグがかかるz値です。
あとは、ハードウェアが画面に書き込むときに、ピクセルごと
の加工をして、フォグをかけます。
さて、久しぶりに簡単なプログラムでした。
いや、掃除してないとかじゃなくて…
普段は、まったく気にしていませんが、身の周りの空気中には塵が漂っていたり、水が飛んでたりします(あなたがクリーンルームにいたらごめんなさい)。
空気中にごみがあるので、あなたの目には、途中のごみで散乱された光も飛び込んできます。
もちろん、本来入ってくる筈の光も散乱されます。
塵が多いほど、この散乱の影響は大きくなります。
塵が多いというのは、光が届くまでの距離が長いということです。
つまり、遠くにある物体ほど塵の影響を受け、霧が濃くかかります。
下の図で、使用前、使用後を見比べていただければ、
遠くに行くほど霞がかかることが確認できると思います。
■ソース解説
今回は、初期化部分とシェーダープログラムにしか新しいことは入りません。
初期化部分は、下の黄色い部分が追加されます。
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_FOGSTART と、D3DRS_FOGEND は、フォグがかかり始める深度です。
Vertex shader で詳細に設定するので、とりあえず0から1にしました。
後は、Vertex shader で使う定数の設定です。次に説明します。
追加するのは、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)
次に、線形変換をして、フォグの値を調整します。
フォグの値の調整は、D3DRS_FOGSTART、D3DRS_FOGEND で調整した値。
すなわち、フォグがかかり始める状態の時に0,フォグが完全にかかった状態の時に1になるように設定します。
ちなみに、値がそれらの範囲を超えた場合には、ハードウェアが自動的にかからない状態、完全にかかった状態にクリップしてくれます。
■最後に
次は、このフォグを少し変化させてみたいと思います。