サーチ…
フォングライティングモデル
注:この例はWIPです。図、画像、例などが更新されます。
Phongとは何ですか?
Phongは、非常に基本的ですが、周囲、拡散、鏡面の3つの部分を持つサーフェスのライトモデルです。
周囲照明:
アンビエントライティングは、理解して計算する3つの部分の中で最も簡単です。アンビエントライティングは、シーンをフラッディングしてオブジェクトをあらゆる方向に均等に照らすライトです。
アンビエントライティングの2つの変数は、アンビエントの強さとアンビエントの色です。フラグメントシェーダでは、以下のものがアンビエントで動作します:
in vec3 objColor;
out vec3 finalColor;
uniform vec3 lightColor;
void main() {
float ambientStrength = 0.3f;
vec3 ambient = lightColor * ambientStrength;
finalColor = ambient * objColor;
}
拡散照明:
拡散照明は周囲よりやや複雑です。拡散照明は指向性の光です。つまり、光源に向かう面がよりよく照らされ、指し示す面が光がそれらに当たるために暗くなります。
注:拡散照明では各面に法線を使用する必要がありますが、ここでは計算方法を示しません。これを行う方法を知りたい場合は、3D数学のページをご覧ください。
コンピュータグラフィックスにおける光の反射をモデル化するために、双方向反射率分布関数(BRDF)が使用される。 BRDFは、出射方向に沿って反射する光と入射方向に入射する光との間の関係を与える関数である。
完全拡散面には、すべての入射方向と出射方向に対して同じ値を持つBRDFがあります。これは計算を実質的に減少させるので、現実世界に純粋な拡散物質が存在しないにもかかわらず、物理的にもっともらしいので拡散表面をモデル化するために一般的に使用されます。このBRDFはランバートのコサインの法則に従うので、ランバートの反射と呼ばれます。
ランバート反射は、しばしば拡散反射のモデルとして使用される。この手法は、レンダリング時にすべての閉じたポリゴン(3Dメッシュ内の三角形など)がすべての方向に光を均等に反射させるようにします。拡散係数は、法線ベクトルと光ベクトルの間の角度から計算されます。
f_Lambertian = max( 0.0, dot( N, L )
N
は面の法線ベクトルであり、 L
は光源に向かうベクトルである。
使い方
一般に、2つのベクトルのドット積は、両ベクトルの大きさ(な長さ)を乗じ2つのベクトル間の角度の余弦に等しいです。
dot( A, B ) == length( A ) * length( B ) * cos( angle_A_B )
これは、単位ベクトルの長さが1であるため、2つの単位ベクトルの内積は、2つのベクトル間の角度の余弦に等しいとすると、次の。
uA = normalize( A )
uB = normalize( B )
cos( angle_A_B ) == dot( uA, uB )
-90°と90°の間のcos(x)関数を見ると、0°の角度で最大1、90°の角度で0になることがわかります-90°である。
この振る舞いは、リフレクション・モデルに必要なものとまったく同じです。表面の扇形のvetorと光源へのdiretionが同じ方向にあるとき(その間の角度は0°です)、我々は反射の極大を求めます。対照的に、正規直交化されたベクトル(その間の角度が90°である場合)は、最小限の反射が必要であり、0度と90度の2つの境界の間で滑らかで連続的な機能動作が必要です。
光が頂点ごとに計算される場合、反射はプリミティブの各コーナーに対して計算されます。プリミティブの間で、反射はその重心座標に従って補間される。球面上の反射を確認する:
さて、フラグメントシェーダを使い始めるには、4つの入力が必要です。
- 頂点法線(バッファ内にあり、頂点属性ポインタによって指定される)
- フラグメント位置(頂点シェーダからフラグメントシェーダに出力する必要があります)
- 光源の位置(均一)
- 明るい色(均一)
in vec3 normal;
in vec3 fragPos;
out vec3 finalColor;
uniform vec3 lightColor;
uniform vec3 lightPos;
uniform vec3 objColor;
メインの内部は数学をする必要がある場所です。拡散照明の概念全体は、法線方向と光線方向との間の角度に基づいている。角度が大きくなればなるほど、光が全くない90°までの光が少なくなります。
光の量を計算する前に、光の方向ベクトルが必要です。これは、光の位置からフラグメントの位置を指すベクトルを返すフラグメントの位置からライトの位置を減算するだけで検索できます。
vec3 lightDir = lightPos-fragPos;
また、 normal
とlightDir
ベクトルを正規化して、同じ長さになるようにしてください。
normal = normalize(normal);
lightDir = normalize(lightDir);
ベクトルがあるので、ベクトルの差を計算することができます。これを行うには、ドットプロダクト関数を使用します。基本的に、これは2つのベクトルをとり、形成された角度のcos()を返します。これは完璧です。なぜなら、90度では0が得られ、0度では1が得られるからです。その結果、光がオブジェクトを直接指しているときには、完全に照明され、その逆もあります。
float diff = dot(normal, lightDir);
計算された数値にもう一度やらなければならないことがもう一つあります。常に正の値であることを確認する必要があります。それについて考えると、光が顔の後ろにあることを意味するので、負の数は文脈上意味をなさない。 if文を使うか、最大2つの入力を返すmax()
関数を使うことができます。
diff = max(diff, 0.0);
これで、フラグメントの最終出力カラーを計算する準備が整いました。
vec3 diffuse = diff * lightColor;
finalColor = diffuse * objColor;
鏡面照明:
プログレッシヴで作業し、後でチェックしてください。
結合された
作業中ですが、後で確認してください。
以下のコードと画像は、これらの3つの照明概念を組み合わせて示しています。