Appleんとこ、陰影づけ(シェーディングというのじゃよ)のサンプル無いっすね。
 仕方ないんで

自習用サンプルサイト紹介ページ



NeHe tutorials and more OpenGL codesamples ported to OpenGL ES 1.x

に載ってるLesson 07: Texture Filters, Lighting & Keyboard Control:をパクります。
グラッチェ!

 平面の陰影は面にあたる光の角度で計算します。
 具体的には平面から垂直に飛び出すベクトル(面法線ベクトル)

テン・シー・シー-1

と、光源へのベクトルとのなす角を求めて濃淡を決める。角度が小さいほど光は真正面からあたるわけで、そのぶん明るくなると考える。

テン・シー・シー-2

 この計算をするのに必要な情報としては

光源:光源へのベクトル、光の色
平面:反射するさいの色成分

あたりが必要になってくるわけでlesson07.cのソースをあさると

LightAmbient
LightDiffuse
LightPosition

な~んていう、いかにもな変数名と、それを使う

glLightfv

てAPIが見つかるわけです。
 で、わかったのがOpenGLではGL_LIGHT1~GL_LIGHT7まで7つの光源を個別に設定可能だということ。光源の設定としては

glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);

で、環境光量(GL_AMBIENT)を

glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);

で、拡散光量(GL_DIFFUSE)を

glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);

で、光源の位置(GL_POSITION)を、最後に

glEnable(GL_LIGHT1);

で、GL_LIGHT1光源の利用宣言をしているようです。
 なんのかんの設定しても、最後

glEnable(GL_LIGHTING);

こいつを呼ばないと、シェーディングはしてくれません。サンプルいじって試してみましょう。

 引数のLightAmbient、LightDiffuseはそれぞれR,G,B,αで色を指定していて、LightPositionはx,y,z位置を指定してる事になります。

 環境光量てのはなにかというと、光源から出た光が、壁や床(周辺)のいたるところで反射して平面に降り注ぐ量を考慮したもの。

テン・シー・シー-3

 まじめに反射をシミュレーションしてリアリティを追求する場合(中前&西田先生みたいに)もあるけど、OpenGLの目的は

リアリティ < 高速演算

なので、ここらへんは、影響の度合いを比率で設定する程度で妥協なわけですな。

 でもって、拡散光量は反射の少ない紙のような面が、光源によって明るくなる量。
 これが、先の「面法線ベクトルと光源へのベクトルとの角度を求めて濃淡を決める」量。
 実は、もう一つ加わって鏡面反射光量の3つで、それなりの画像を作り上げるのがOpenGLなんだけど、lesson07.cでも使ってないし、今回は鏡面反射光量はパス。

 最初に書いたように、拡散光量が100%作用するのは、面法線ベクトルと光源への方向ベクトルのなす角が0になる場合、そうでなければ角度が開けば開くほど光量は落ちると考えるわけっす。

テン・シー・シー-4

 光源の位置(GL_POSITION)は、引数にx,y,zの他にwという値を受け取り、w=0の時は平行光源、それ以外は点光源とみなす。平行光源は太陽光のように、光を受ける位置によって光の方向や、光量が変化しない光。点光源は電球のように放射状に光を出して距離によって光量が変わるもの。
 で、平行光源の場合は引数x,y,zは光源の位置ではなく、そこから原点に向かうベクトルで光の進行方向を指定することになる。

 これで光源への方向ベクトルは割り出せる。
 じゃあ、平面の拡散光量の計算に必要な面法線ベクトルはどうやって導きだすかというと、平面の一角を形成する2辺をベクトルとした

外積

テン・シー・シー-5

を使います。こいつで面法線ベクトルを出して、ベクトル同士のなす角の計算には

内積

テン・シー・シー-7
を使うわけです。

ちなみに外積は、掛け合わす順序を逆にすると出てくる面法線ベクトルも逆になる。
テン・シー・シー-6

正直引くわ...
状態になってるとは思うけど、導きだせます。コーラを飲むとゲップが出るくらい確実に導けるんじゃあ。

 ま、なにはともあれ、この法線ベクトルは

glNormalPointer

を使って頂点毎に設定する事になってます。
 それと、平面側の反射するさいの色成分は

glMaterialfv

で、環境光用と拡散光量を別々に設定しています。やっぱ、これもR,G,B,α指定。GL_FRONT_AND_BACKは裏、表という意味。

 ということですが、よく考えたらGL_TRIANGLE_STRIP指定だと、複数の面が同じ頂点を共有するんで、法線ベクトルを面ごとに指定できないんですよ。でもいいや、適当に中間の値でいっちゃえ~。

テン・シー・シー-8
上の外積は使わずに原点と頂点を結んだベクトルを法線として指定しました。
なんか微妙~。

なもんで、GL_TRIANGLE_STRIPやめて、GL_TRIANGLESで各面ごとに描画してみました。

テン・シー・シー-9
グッドす!

 サンプルソースではUSE_TRIANGLE_STRIPを1にするとGL_TRIANGLE_STRIP、0にするとGL_TRIANGLESを使うようにしているんで、いろいろ試してみてください。

 GL_TRIANGLE_STRIPのような隣り合う平面で法線を共有する場合、もうちょっと面が多いと効果がわかるんだけどね~、なのでGL_TRIANGLE_FANを使って以下の描画を試してみました。

テン・シー・シー-10
ね、平面のくせに曲面ぽいっしょ。

以下、次回!

------------
サンプルプロジェクト:es5.zip