ランバート反射の基本


概要

最終更新日:2020/02/02

ランバート反射とはCG上で拡散反射を表現するために使用される反射モデルで、
オブジェクトのポリゴン単位で行われます。
この反射の特徴は実行結果がどの角度から見ても同じ見え方になります。

computer_graphics_0015

サンプル

サンプルはこちらからダウンロードできます。
環境については以下の内容となっています。

開発環境
VSのバージョン VisualStudio 2019
DirectXのバージョン DirectX9

使用する情報

ランバート反射で最低限必要な情報は以下の通りです。
  • 光の方向
  • ポリゴンの色
  • マテリアルを使用する場合はディフューズを使用する
  • ポリゴンの法線
より細かく表現をしたい場合は以下の情報を追加します。
  • 光の色
  • 反射の強さ

ランベルトの余弦則

ランベルトの余弦則とはランバート反射で使用されている法則です。
光学におけるランベルトの余弦則(ランベルトのよげんそく)は、
理想的な拡散反射面や拡散放射体で観測される放射強度あるいは光度が、
入射光と面の法線との間の角度θの余弦と正比例することを示す法則である

フリー百科事典『ウィキペディア(Wikipedia)』

この法則を使用すればポリゴンの面積に当たっている光の量で反射する色を表現できます。
ポリゴンの面積に対しての光の量が大きいほど強く反射して、ポリゴンの色は明るくなり、
逆に光の量が小さいほど弱く反射して、ポリゴンの色は暗くなります。
光の量は光がポリゴンの表面に真正面から当たっている場合が最も大きくなります。

computer_graphics_0016

そして、ポリゴンの表面から横にずれるほど光の量は少なくなっていきます。

computer_graphics_0018

反射計算

ランバート反射を実行する場合に最低限の情報を使用した計算式は以下の通りです。

computer_graphics_0013
次は先ほどの計算式に光の色や反射の強さを追加した式です。

computer_graphics_0014
このどちらかを使用することでランバート反射を実装できます。

実装方法

プログラム上での実装はシェーダで行います。
今回のサンプルでは頂点シェーダで光の方向と法線との内積計算を行っています。

// 移動を計算に反映させない
input.normal.w = 0.0;

// 頂点の法線にワールド行列を掛け合わせて
// ワールド座標上での法線の向きに変換する
float4 vertex_normal = mul(input.normal, World);
vertex_normal = normalize(vertex_normal);

// ディレクショナルライト
// saturate => 引数で指定した値を0~1間での範囲に収める
// dot => 内積計算
/*
	-1.0 を掛けているのは光が最も当たる
	二つのベクトルのなす角は180度の時なので
	-1.0かけて今後の計算がやりやすいように
	0~1の範囲が有効な値として扱うようにしている
*/
output.normal = saturate(dot(vertex_normal, LightVector) * -1.0);

そして反射計算はピクセル(フラグメント)シェーダで行っています。
※計算は二つ用意しています。

// マテリアルのディフューズ * 内積
float4 diffuse_color = MaterialDiffuse * input.dot_normal;

// 光の色 * マテリアルのディフューズ * 内積 * 反射率
//float4 diffuse_color = LightColor * MaterialDiffuse * input.dot_normal * diffuese_power;

マテリアル情報やライトの情報はDirectGraphics.cppのSetUpLightで行っているので、
パラメータを変更して確認したい人はこちらで行ってください。

// ライトの設定
DirectX::XMVECTOR light_vec = DirectX::XMVector3Normalize(DirectX::XMVectorSet(0.0f, 0.0f, -1.0f, 0.0f));
DirectX::XMVECTOR light_pos = DirectX::XMVectorSet(0.0f, 2.0f, 0.0f, 0.0f);
// 光の方向
XMStoreFloat4(&m_ConstantBufferData.LightVector, light_vec);

// ライトの位置(ディレクショナルライトとして扱うので関係なし)
XMStoreFloat4(&m_ConstantBufferData.LightPos, light_pos);

// マテリアル係数の設定(x y z : ambient diffuse specular)
m_ConstantBufferData.MaterialPower = DirectX::XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);

// ライトカラー設定
m_ConstantBufferData.LightColor = DirectX::XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);

ランバート未使用、ランバート使用、光の色を変更を並べてみました。

computer_graphics_0020