音ゲーの出来ない雑魚のブログ

LWJGLとかのことをいうブログ

「moreover lwjgl」 14  Phongなひかりとその最適化

このまえは拡散光だけで、艶も環境光もなかったので、リアル感がありませんでした。そこで、今回はPhongシェーディングというものを使って、実装したいと思います。

Phongシェーディングとは、光による物体の色の変化を「環境光(Ambient)」「拡散光(Diffuse)」「反射光(Specular)」にわけて考え、それらを1ピクセルずつ(つまりフラグメントシェーダで)計算するというものです。
上の3つの光は、




S:物体に向かう光のベクトル N:法線ベクトル R:反射したSのベクトル V:視線方向のベクトル shininess:輝き係数

Ambient = C(定数)

Diffuse = L・N

Specular = (R・V)shininess

そして、それらを…

Color = Ambient + Diffuse + Specular

することで物体が照らされているように見えるということです。

反射式自体は、Lambertモデルというものです。

とりあえず、やってみましょう。



解説

Test9_ShadingWithPhong.java

ないです。この前(Diffuse)からAmbient成分の"rateAmbient"とSpecular成分の"rateSpecular","Shininess"のUniformを足したぐらいで、殆ど変わっていません。

V4.vert+F4.frag

こっちはいろいろ変わっています。

v11,12行目 : フラグメントシェーダにPhongを計算するための頂点位置、法線を渡すための出力変数です。この変数に代入された値は、頂点ごとの値ですが、ピクセルずつに補間されます

v16行目 : フラグメントシェーダに渡す頂点座標は、射影変換を除いたものです。それは、ライティングの処理をワールド座標系で行いたいからです。

v17行目 : 法線行列を先にかけておいた法線を渡します。

f30~43行目 : ライトと物体の材質(マテリアル)に分け、ライティングの要素は、マテリアルが持つことにしました。

f46~49行目 : 法線、物体から光ヘ向かうベクトル、視線方向のベクトルを使って、反射ベクトルRをreflectという組み込み関数で計算します。

vec3 reflect(vec3 I,vec3 N)

reflect関数内は、次の様な計算をしています。

R = I – 2 * dot(N, I) * N

Iは反射させたいベクトル、Nは法線です。基本的には、内積を使って三角形を作っているという感じでしょうか?

f52行目 : 環境光を足しています。

f54行目 : 反射光を足しています。反射光は、先ほどの式と同じです。



このようにしてPhong式を物体に掛けると…

f:id:cakkby:20160712081903p:plain

と、ちょっと大きめですが、ハイライトが出てきました。また、ハイライトの大きさはShininessを上げると小さくなります。例えばShininessが200だと…

f:id:cakkby:20160712082106p:plain

と、よりテカテカした感じになります(?)。ただ、あまり大きくし過ぎるとハイライトが小さすぎてそれっぽく見えなくなるので注意して下さい。


最適化?したい

今やった方法でもいいのですが、もう少し計算を楽にして、結果も似ている方法(ハーフランバート)でやってみたいと思います。

ハーフランバートモデルは次のような計算をします。

H:ハーフベクトル N:法線ベクトル shininess:輝き係数

Specular = (H・N)shininess

ということで、GLSLの方だけちょっといじったバージョン…

解説

f49行目 : 視線ベクトルと光ベクトルを足すことで、その中間のベクトル(ハーフベクトル)を作ります。

f54行目 : ハーフベクトルと法線ベクトルの内積をとることで、ランバートモデルを近似しています。

Lambertモデルと、Half-Lambertモデルは次のような感じになっています。
f:id:cakkby:20160712160358p:plain
反射ベクトル(or ハーフベクトル)が赤ければ赤いほど、照り返しが強いということです。

このようにして、ハーフベクトルを使ってやると、見た感じではほとんど同じなのに、計算量を減らしてやれます