DXライブラリで鏡面反射光(スペキュラー)と環境光(アンビエントライト)を実装する方法について説明しています。使用言語はHLSL、DirectX11版。
※スペキュラ計算で余計な処理が入っていたので修正しました。
鏡面反射光と環境光について
鏡面反射光(スペキュラー)はマテリアルの光沢を表す要素。金属の反射などを表現するときに必要になる。Blenderなどの3DモデリングソフトやUnityではメタリックといった専用の値があるがDXライブラリでは用意されていないのでこの鏡面反射光を実装して光の反射を表現する。
一方の環境光(アンビエントライト)は他の物体や空気中で乱反射した光を指す。間接光とも。シェーダー上では影の真っ暗な部分を緩和するのに使われる。
3Dモデルの各オブジェクトは鏡面反射光の情報(反射の強さ、色)および環境光の情報(環境光の強さ、色)を持っているのでこれを使って反射光や環境光を求めていく。Blenderなどの3Dモデリングソフトで設定してもいいがDxLibModelViewerからでも設定可能。
鏡面反射光についてはスペキュラマップという専用のテクスチャを利用して求めることもできる。オブジェクト内に金属の部分とそうでない部分が混合している場合に使う。
サンプルコード
それでは実装。
前回のランバートシェーダーで作成したピクセルシェーダーに以下の処理を追加・変更する。
// main関数
PS_OUTPUT main(PS_INPUT PSInput)
{
//略
//スペキュラー関連の変数を追加
float3 SpecularColor;
float3 TotalSpecular;
float3 TempF3;
float Temp;
float3 refDir; //反射ベクトル
float3 Surface_to_Eye; //頂点から視点へのベクトル
// 略
// ディフューズ角度減衰率計算後の処理を以下のように変更
// ディフューズカラー蓄積値 += ライトのディフューズカラー * マテリアルのディフューズカラー * ディフューズカラー角度減衰率 + ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
TotalDiffuse += g_Common.Light[0].Diffuse * g_Common.Material.Diffuse.xyz * DiffuseAngleGen + g_Common.Light[0].Ambient.xyz;
// スペキュラカラー計算
// スペキュラカラーの蓄積値を初期化
TotalSpecular = float3(0.0f, 0.0f, 0.0f);
// 反射ベクトルを求める
refDir = normalize(reflect(lLightDir, Normal));
// 頂点から視点へのベクトルを求める
Surface_to_Eye = normalize(-PSInput.VPosition);
// スペキュラの強さを求める
Temp = dot(Surface_to_Eye, refDir);
Temp = pow(max(0.0f, Temp), g_Common.Material.Power);
// スペキュラカラー蓄積値 += Temp * ライトのスペキュラカラー
TotalSpecular += Temp * g_Common.Light[0].Specular;
// ディレクショナルライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// SpecularColor = ライトのスペキュラカラー蓄積値 * マテリアルのスペキュラカラー
SpecularColor = TotalSpecular * g_Common.Material.Specular.xyz;
// 出力カラー = TotalDiffuse * テクスチャカラー + SpecularColor
TextureDiffuseColor = g_DiffuseMapTexture.Sample(g_DiffuseMapSampler, PSInput.TexCoords0);
PSOutput.Color0.rgb = TextureDiffuseColor.rgb * TotalDiffuse + SpecularColor;
// アルファ値 = テクスチャアルファ * マテリアルのディフューズアルファ * 不透明度
PSOutput.Color0.a = TextureDiffuseColor.a * g_Common.Material.Diffuse.a * g_Base.FactorColor.a;
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return PSOutput;
}
鏡面反射(スペキュラー)の求め方だが以下の順で行う。
- 物体の面に当たって反射したベクトルを求める。
- 反射した点から視点へのベクトルを求める
- 1と2で求めたベクトルを使って鏡面反射の強さを求める

反射ベクトルはライトの向きと面の法線から求める。HLSLでは反射ベクトルを計算する関数 reflect() が用意されているのでそれを使用する。
鏡面反射の強さの計算だがここでも内積を用いて計算している。最終的にはマテリアルに設定されている鏡面反射の強さと乗算する。
ちなみに上の計算式だとマテリアルに設定されている鏡面反射の強さが値が小さいほど反射光が強くなることに注意。
環境光は鏡面反射とは違いディフューズカラーにマテリアルとライトのアンビエントカラーを乗算したものを足すというシンプルな処理で実装している。
HLSLでのシェーダーについてきちんと学びたい人はこちらの本で学ぶのがオススメ。

コメント