ライティングとシェーディング — 明るさは「向き」で決まる
3Dの物体が立体的に見えるのは、面の向きによって明るさが変わるから。面の向きを表す法線と光の方向、この2本のベクトルのなす角から画素の色を計算するしくみ — ランバートの拡散反射とフォンの鏡面反射 — を、動かしながら理解します。
1. 明るさを決める2本のベクトル — 法線と光
面の向きは、面に垂直な単位ベクトル法線 N で表します。光源の方向を指す単位ベクトルを L とすると、面が受け取る光の量は2本のなす角 θ で決まります。幾何的な理由は単純で、同じ幅の光の束が、斜めの面では広い面積に薄く分配されるからです。
下のデモで光の角度を変えてみてください。受光面の幅が 1/cos θ 倍に広がり、単位面積あたりの明るさが cos θ に比例して落ちる様子が見えます。この cos θ は、単位ベクトルどうしの内積 N・L そのものです。
法線と光のなす角 — 内積 N・L が明るさになる
オレンジの点=光のエネルギー。角度 θ を大きくすると、同じ量の光が広い範囲に薄く分配されて面は暗くなります。右のバーが cos θ = 明るさ。青が法線 N、水色が光源方向 L。
POINT — ランバートの余弦則
完全につや消しの面(理想拡散面)の明るさは cos θ に比例し、しかもどの方向から見ても同じ明るさに見える。だから拡散反射の計算式に視線の方向は登場しない。
I拡散 = kd · I光 · max(0, N·L)
N=法線、L=光源方向(どちらも単位ベクトル)。内積 N·L = cos θ。max(0, ·) は裏を向いた面(cos θ < 0)を暗くするため
2. つやを足す — フォンの鏡面反射
拡散反射だけでは、プラスチックや金属の「てかり」が出ません。つやのある面では、光が正反射方向 R(鏡が反射する方向)の近くに集中して跳ね返ります。視線 V が R に近いほど強く光って見える — これがハイライトです。
近さを cos の累乗 (R·V)α で表したのがフォンの鏡面反射です。光沢度 α を大きくするほどハイライトは小さく鋭くなり、つるつるした質感になります。
球を画素ごとにシェーディング — 光源と光沢度を動かす
各画素で球面の法線を求め、拡散+鏡面を計算して塗っています。α を上げるとハイライトが小さく鋭く(つるつるに)、下げると広くぼんやり(マットに)。ks=0 にすると純粋なランバート面です。
I鏡面 = ks · I光 · max(0, R·V)α R = 2(N·L)N − L
R=正反射方向、V=視線方向。α(光沢度)が大きいほどハイライトは小さく鋭くなる
注意 — フォンのモデルは物理法則ではない
この式は「それらしく見える」ように作られた経験則で、物理的なエネルギー保存は保証しない。それでも計算が軽く見た目が良いため、リアルタイムCGの標準として何十年も使われてきた。物理的に正しい表現は最後の「一歩先へ」で。
3. どこで照明を計算するか — フラット・グーロー・フォン
曲面は実際には小さなポリゴンの集まりです。上の照明計算を「面ごと」「頂点ごと」「画素ごと」のどこで行うかによって、見た目の滑らかさと計算量が大きく変わります。
- フラットシェーディング:面ごとに1回計算し、面全体を1色で塗る。カクカクの多面体に見える。
- グーローシェーディング:頂点で照明を計算し、面の内部は色を補間。滑らかだが、頂点の間に落ちたハイライトが消えたり、にじんだりする。
- フォンシェーディング:面の内部で法線を補間し、画素ごとに照明を計算。ハイライトが正確に出る。いちばん高価。
同じローポリ球・同じ照明 — 「計算する場所」だけが違う
分割数を下げるほど3つの差が際立ちます。グーローのハイライトが「にじむ・消える」様子と、光源を回したときフラットの面が段階的にパッと切り替わる様子に注目。フォンは少ない分割数でも滑らかなハイライトを保ちます。
POINT — 現代のGPUは「画素ごと」が当たり前
今のGPUではフラグメントシェーダが画素ごとに照明を計算するのが標準 — 実質すべてフォンシェーディングだ。グーローは「頂点シェーダで計算した色をそのまま補間で受け取る」書き方に相当し、軽量表現として今も選択肢に残っている。
4. 3つの光を足し合わせる — フォンの反射モデル
実際のシーンでは、光源から直接届く光のほかに、壁や床で跳ね回った間接光がどこにでも回り込んでいます。これを定数で代用したのが環境光(アンビエント)です。環境光・拡散・鏡面の3成分を足し合わせたものがフォンの反射モデルで、RGBの各チャンネルごとに計算します。
環境光+拡散反射+鏡面反射 — 成分の足し算で「立体感」ができる
チェックを外して確かめてください:環境光だけだと完全な円盤(立体感ゼロ)、拡散が立体感を、鏡面がつや(質感)を担当しています。全部OFFにすると真っ黒 — 光がなければ何も見えません。
I = ka Ia + kd IL max(0, N·L) + ks IL max(0, R·V)α
フォンの反射モデル(1975)。ka, kd, ks は材質の反射係数。RGBの各チャンネルごとに計算する
注意 — 名前の混同に注意
「フォンの反射モデル」(この式)と「フォンシェーディング」(3節の法線補間)は、同じ Bui Tuong Phong の提案だが別の概念。「フォンシェーディングで描く」は照明を画素ごとに計算することを指し、そこで使う反射の式は別のモデル(PBRなど)でも構わない。
5. まとめ
- 拡散反射:法線 N と光の方向 L の内積 cos θ が明るさ。ランバートの余弦則。視線によらない。
- 鏡面反射:正反射方向 R と視線 V の近さの α 乗。α が大きいほど鋭いハイライト。
- 照明計算の場所:面ごと(フラット)→ 頂点ごと(グーロー)→ 画素ごと(フォン)の順に滑らかで高価。
- フォンの反射モデル:I = 環境光 + 拡散 + 鏡面。長年のリアルタイムCGの標準式。
一歩先へ — Blinn-Phong と物理ベースレンダリング
R·V の代わりにハーフベクトル H =(L+V を正規化)と法線の内積 N·H を使う Blinn-Phong は計算が軽く、長らくGPUの定番だった。現在の主流は物理ベースレンダリング(PBR):エネルギー保存・フレネル反射・マイクロファセット理論(Cook-Torrance、GGX分布)に基づき、「粗さ」と「金属度」の2パラメータで現実の材質を直感的に再現する。ゲームエンジンの標準マテリアルはほぼこれだ。