さて、フォグをやっていなかったのでやっておきます。
遠くの景色ほど、白くかすんでいきます。これがフォグ(霧)です。
霧として使うだけでなく、空気感を演出するために使います。
今回のソースは、次のものです(DirectX8.1用です)。
まぁ、いつものように適当にファイルが入っています。
せっかくなんで、頂点シェーダーで組んでみました。
vs.vsh | 頂点シェーダープログラム。久しぶりに、ここに新しい要素が入る。 |
draw.cpp | メインの描画部分。 |
draw.h | 描画の各関数の定義。特に意味無いので出番無し。 |
main.h | 基本的な定数など。今回も出番無し。 |
main.cpp | 描画に関係しないシステム的な部分。変更が無いので、出番無し。 |
load.h | ファイルの読み込み。 |
load.cpp | ファイルの読み込み。 |
bg.cpp | 地面+天円柱の描画。今回説明無し。 |
あと、いつもの様に、モデルと、実行ファイル及び、プロジェクトファイルが入っています。
あなたの周りは汚れています!!!
いや、掃除してないとかじゃなくて…
普段は、まったく気にしていませんが、身の周りの空気中には塵が漂っていたり、水が飛んでたりします(あなたがクリーンルームにいたらごめんなさい)。
空気中にごみがあるので、あなたの目には、途中のごみで散乱された光も飛び込んできます。
もちろん、本来入ってくる筈の光も散乱されます。
塵が多いほど、この散乱の影響は大きくなります。
塵が多いというのは、光が届くまでの距離が長いということです。
つまり、遠くにある物体ほど塵の影響を受け、霧が濃くかかります。
下の図で、使用前、使用後を見比べていただければ、
遠くに行くほど霞がかかることが確認できると思います。
使用前
使用後
それでは、ソースコードにとっとと入ります。
今回は、初期化部分とシェーダープログラムにしか新しいことは入りません。
初期化部分は、下の黄色い部分が追加されます。
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)
![]()
の加工をして、フォグをかけます。
さて、久しぶりに簡単なプログラムでした。
次は、このフォグを少し変化させてみたいと思います。