glScalefのバカヤローヽ(`Д´)ノ
メタセコモデルのローダーを改良中。なんだけれど妙な問題にぶつかった。どうしてもモデルのシェーディングがおかしい。異常にハイライトがきつかったり暗すぎたり。法線の計算ルーチンがバグっているのかと思い見直してみるも問題ない様子。材質やライティングの設定をあれこれ弄ってみてもだめぽ。数時間悩む。
思い立って何気なくglEnable(GL_NORMALIZE)を有効にしてみたところ一気に解決。どうやら犯人はモデルの大きさを合わせるために使っていたglScalef。こいつで拡大縮小するとおせっかいなことに法線まで拡大縮小されてしまうらしい。OpenGLでは法線は単位ベクトル長でなければならないので、シェーディングがおかしくなったようだ。
glScalefを使う場合はGL_NORMALIZE有効にするか法線の再正規化をしましょうってことで。
法線は拡大縮小されないと勝手に思い込んでいた漏れが悪いのか。変換行列のこともよくわからないし、ちゃんと数学勉強しないと駄目かな。
というわけで法線の計算方法のおさらい
1.面単位の法線を求める
たとえば頂点v1、v2、v3を含む面の法線を求めるには
Vector3 work1, work2; work1 = v2 - v1; work2 = v3 - v1; normal = crossProduct(work1, work2); // 外積を求める。 normal.normalize(); // ちゃんと正規化しておく。
注意点は、v1、v2、v3が法線の方向に対して反時計回りになるようにするくらいか。ベクトル同士の減算とか、crossProductやらnormalizeは脳内関数に置き換えてプリーズ。
2.頂点単位の法線を決定する
その頂点を含む全ての面(隣り合う面)の法線の平均を求める。
スムースシェーディングで鋭角なエッジが潰れてしまうのを防ぐ法線生成テクニック。よくスムースシェーディングとフラットシェーディングの比較の図なんかで、スムースシェーディングをかけられた立方体が、頂点同士の色が補完されて立方体の角が消えちゃってるのを見るけれど、これを防ぐ方法。