C++言語でDirectX 11を使ったアプリケーションを開発してみよう。
前回は平行光源でのライティングを行いましたが、今回は点光源(ポイントライト)というものに挑戦してみます。平行光源は太陽光のような一定の方向から当たる光でした。対して点光源は電灯などの光を表現します。
平行光源との違いは、まず光源に座標があるという事です。平行光源の時と同様に面の向きと光の方向を元に当たり具合を計算しますが、頂点の位置によって光源との角度が変わります。もう一つの違いは、面と光源との距離によって明るさが変わる点です。この2点を考えながら点光源を実装してみます。
定数バッファ
定数バッファは以下のように定義しました。
struct ConstantBuffer { XMFLOAT4X4 world; XMFLOAT4X4 view; XMFLOAT4X4 projection; XMFLOAT4 light; XMFLOAT4 attenuation; };
lightは、光の方向ではなく光源の座標を格納します。
attenuationは、距離によってどのように明るさを変化させるかを調整する為の減衰パラメータを格納します。詳細はピクセルシェーダーのところで解説します。
頂点シェーダー
平行光源の時は頂点シェーダーで明るさを計算していました。点光源では距離によって明るさが変わる為よりリアルな表現になるようにピクセルシェーダーで明るさを計算させるようにしています。
この為、頂点シェーダーは主にデータを引き渡すだけになっています。
struct VS_IN { float4 pos : POSITION0; float4 nor : NORMAL0; }; struct VS_OUT { float4 pos : SV_POSITION; float4 posw : POSITION0; float4 norw : NORMAL0; }; cbuffer ConstantBuffer { float4x4 World; //ワールド変換行列 float4x4 View; //ビュー変換行列 float4x4 Projection; //透視射影変換行列 float4 Light; float4 Attenuation; } VS_OUT vs_main( VS_IN input ) { VS_OUT output; float3 nor; float col; output.posw = mul(input.pos, World); output.pos = mul(output.posw, View); output.pos = mul(output.pos, Projection); output.norw = mul(input.nor, World); return output; }
頂点シェーダーから出力されるVS_OUT構造体は、プロジェクション座標系へ変換された座標posの他に、ワールド座標系の座標poswと、同じくワールド座標系に変換された法線norwを出力しています。
ピクセルシェーダー
ピクセルシェーダーで明るさを計算します。
こちらでも定数バッファを参照する必要がある為 cbuffer が定義されています。
光源との角度による明るさと距離による明るさの減衰を掛け合わせて最終的な色を計算しています。
struct PS_IN { float4 pos : SV_POSITION; float4 posw : POSITION0; float4 norw : NORMAL0; }; cbuffer ConstantBuffer { float4x4 World; //ワールド変換行列 float4x4 View; //ビュー変換行列 float4x4 Projection; //透視射影変換行列 float4 Light; //光源座標 float4 Attenuation; //光源減衰パラメータ } float4 ps_main( PS_IN input ) : SV_Target { float3 dir; float len; float colD; float colA; float col; //点光源の方向 dir = Light.xyz - input.posw.xyz; //点光源の距離 len = length(dir); //点光源の方向をnormalize dir = dir / len; //拡散 colD = saturate(dot(normalize(input.norw.xyz), dir)); //減衰 colA = saturate(1.0f / (Attenuation.x + Attenuation.y * len + Attenuation.z * len * len)); col = colD * colA; return float4(col, col, col, 1.0f); }
光源との角度による明るさ計算
光源の方向を毎回計算して求めます。
座標 input.posw と光源の位置 Light から方向 dir を計算します。(26行目)
dir をベクトルの長さで割って正規化します。normalize関数と同じ意味です。(32行目)
dir と法線ベクトル input.norw の内積を計算して明るさを求めます。(35行目)
光源との距離による明るさの減衰を計算
物理現象を正しく再現しようとすると明るさの減衰は距離の2乗に反比例するようですが、その通りに計算すると減衰が早すぎて扱いにくいために、それに近い曲線を描くような別の計算式を利用する事が多いようです。
今回はDirectX 9の時にも利用されていた以下の計算式を使ってみます。
明るさ = 1 / ( att0 + att1 * 距離 + att2 * 距離の2乗 )
att0 ・・・ 一定減衰係数
att1 ・・・ 線形減衰係数
att2 ・・・ 2次減衰係数
att0 ~ att2 は減衰を調整するパラメータで定数バッファ Attenuation の x,y,z 成分を使って引き渡す仕組みです。(37行目)
ピクセルシェーダーにも定数バッファをセット
今回からピクセルシェーダーも定数バッファを利用するようになりました。
ID3D11DeviceContext::PSSetConstantBuffersメソッドで忘れずにセットしましょう。
m_pImmediateContext->VSSetConstantBuffers( 0, 1, &m_pConstantBuffer ); m_pImmediateContext->VSSetShader( m_pVertexShader, NULL, 0 ); m_pImmediateContext->PSSetConstantBuffers( 0, 1, &m_pConstantBuffer ); m_pImmediateContext->PSSetShader( m_pPixelShader, NULL, 0 );
- ~プログラミング~ DirectX 11を始めよう
- ~プログラミング~ DirectX 11はDirectX 9と何が違うか その1
- ~プログラミング~ DirectX 11はDirectX 9と何が違うか その2
- ~プログラミング~ DirectX 11の初期化をしよう その1
- ~プログラミング~ DirectX 11の初期化をしよう その2
- ~プログラミング~ DirectX 11で深度/ステンシルバッファを準備しよう
- ~プログラミング~ DirectX 11でシェーダーをコンパイルしよう
- ~プログラミング~ DirectX 11で三角形を表示しよう
- ~プログラミング~ DirectX 11の頂点バッファとインデックスバッファ
- ~プログラミング~ DirectX 11で立体を表示しよう その1
- ~プログラミング~ DirectX 11で立体を表示しよう その2
- ~プログラミング~ DirectX 11で平行光源ライティング
- ~プログラミング~ DirectX 11で点光源ライティング
- ~プログラミング~ DirectX 11で拡散反射 (Diffuse reflection)
- ~プログラミング~ DirectX 11で鏡面反射 (Specular reflection)
- ~プログラミング~ DirectX 11で環境反射 (Ambient reflection)
- ~プログラミング~ DirectX 11でフォン反射モデル
- ~プログラミング~ DirectXTexライブラリを導入しよう
- ~プログラミング~ DirectX 11でテクスチャマッピング
コメントをお書きください