C++言語でDirectX 11を使ったアプリケーションを開発してみよう。
今回はライティングの手法の一つ鏡面反射についてまとめてみようと思います。
金属などツルツルした物体に光が当たると鏡のように光を反射します。
正確に表現しようとすると電灯など光源の形が写り込む感じになりますが、そこまでやろうとすると計算量が膨大になってしまいます。そこで光源を点と考えハイライトを付ける事で似たような表現を再現します。
物体に光が当たると光が反射します。反射する方向は光の当たった面に対して反対方向になります。
この反射した光がどのくらいカメラに向かっているかを計算してハイライトを加えていきます。
鏡面反射 (Specular reflection)
鏡面反射はツルツルした表面で鏡のように反射する光の反射を表現します。
物体に当たった光は反射して反対方向に進みます。
完全な鏡では正反射方向のみですが、ツルツルぐあいによって正反射を中心にした範囲に広がっていきます。
見ている人の位置が正反射方向にある場合光がよく見え、それ以外の場合光はあまり見えません。つまり鏡面反射の計算にはカメラの座標が必要になります。
反射ベクトルを使った鏡面反射
まず正反射ベクトルRを計算します。
正規化した正反射ベクトルRと正規化視線ベクトルVが作る角度θによって求める事ができます。
右の図を見ると正反射ベクトルRは光源ベクトルの逆向き-LにDの2倍を足す事で求められそうです。
Dは法線ベクトルNにDの距離を掛けることで求まります。
Dの距離は法線ベクトルNと光源ベクトルLの内積(cosθ)となるので D = N *(N・L)で計算できます。
以上から正反射ベクトルRは R = 2 * N *(N・L)- L で求める事ができます。
最終的な計算式は以下のようになります。
I =((R・V)^ α)* KS * IS
正規化正反射ベクトルRと正規化視線ベクトルVの内積を取り、更に光沢度という係数αでべき乗します。この係数を変化させる事で物体の質感を調整する事が出来るようになっています。
最後に面の色KSと入射光の輝度ISを掛け合わせています。
※内積の結果がマイナスの場合は鏡面反射は起こらない状態。マイナスのままではなく0にして計算する必要がある
ピクセルシェーダー
上記の計算をHLSLで書くとこんな感じになります。
struct PS_IN { float4 pos : SV_POSITION; float4 posw : POSITION0; //ワールド座標系の座標 float4 norw : NORMAL0; //ワールド座標系の法線 }; cbuffer ConstantBuffer { float4 eyePos; //視点座標 float4 pntlightPos; //点光源座標 float4 pntlightCol; //点光源の色 float4 materialSpecular; //物体の色(r,g,b,光沢度係数) } float4 ps_main( PS_IN input ) : SV_Target { float3 l; float3 n; float3 r; float3 v; float i; l = normalize(pntlightPos.xyz - input.posw.xyz); n = normalize(input.norw.xyz); r = 2.0 * n * dot(n, l) - l; v = normalize(eyePos.xyz - input.posw.xyz); i = pow(saturate(dot(r, v)), materialSpecular.w); return float4(i * materialSpecular.xyz * pntlightCol.xyz, 1.0); }
ハーフベクトルを使った鏡面反射
反射ベクトルを求める計算式は少し複雑です。これよりも計算コストを抑えたハーフベクトルというものを使う方法もあります。
ハーフベクトルとは、光源ベクトルLと視線ベクトルVのちょうど間のベクトルです。このハーフベクトルHと法線ベクトルNの作る角度θを用いる方法です。
ハーフベクトルHは H=L+V で求める事が出来ます。
※LとVは正規化ベクトルである事
最終的な計算式は以下のようになります。
I =((H・N)^ α)* KS * IS
正規化ハーフベクトルHと正規化法線ベクトルNの内積を取り、あとは反射ベクトルの時と同じです。
光沢度係数αでべき乗し、面の色KSと入射光の輝度ISを掛け合わせています。
ピクセルシェーダー
上記の計算をHLSLで書くとこんな感じになります。
struct PS_IN { float4 pos : SV_POSITION; float4 posw : POSITION0; //ワールド座標系の座標 float4 norw : NORMAL0; //ワールド座標系の法線 }; cbuffer ConstantBuffer { float4 eyePos; //視点座標 float4 pntlightPos; //点光源座標 float4 pntlightCol; //点光源の色 float4 materialSpecular; //物体の色(r,g,b,光沢度係数) } float4 ps_main( PS_IN input ) : SV_Target { float3 l; float3 v; float3 h; float3 n; float i; l = normalize(pntlightPos.xyz - input.posw.xyz); v = normalize(eyePos.xyz - input.posw.xyz); h = normalize(l + v); n = normalize(input.norw.xyz); i = pow(saturate(dot(h, n)), materialSpecular.w); return float4(i * materialSpecular.xyz * pntlightCol.xyz, 1.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でテクスチャマッピング
コメントをお書きください