Tech Sketch Bucket of Technical Chips by TIS Inc.

3Dプログラミング基礎知識(6)

Pocket

3Dプログラミング基礎知識(5)の続きです。

画面に描画されるまでの流れ(おさらい)

Pipeline.png

前回までで画面上の「どこ」に描画されるかは求まりました。今回はライティング、テクスチャによって「どのように」描画されるかを見ていきたいと思います。

ライティング

光の影響を考慮して最終的にレンダリングされる色を決定する処理をライティングと呼びます。
現実の世界にある物体は太陽や蛍光灯など光源からの光があたり、それを反射することで、我々の目に見えています。これを3D空間上でシミュレートすることによって、3D空間上に配置されたオブジェクトの見た目を現実世界に近づけることができます。
しかし、実際には光源の性質として、光源の形状、物体からの距離、方向特性、光の強さ、減衰率など様々な要素が考えられ、これら全てをシミュレートすることはCPU/GPU負荷の面から見ても現実的ではありません。
そこで続きに示すように、代表的な光源や近似出来るような式を用いて光源処理を行うことになります。

直接光(平行光源)

太陽などの光源から直接物体にあたる光を直接光と呼びます。太陽を例に取ると全方向に光が向けられますが、十分に距離が遠い場合、その光は特定の方向から浴びせられる平行光源として近似することができます。

Lighting1.png

Lighting2.png

均一に光があたるものとしてシミュレートすると、光の影響を求めるのに必要となるのは光の角度、光の強さになります。

法線ベクトルと光の影響

同じ光が当たった場合、面(ポリゴン)がどれだけ明るく照らされるかは面の向きによって大きく異なります。
下図における面Aの明るさを考えた場合、左に示すように光線方向と向き合っている場合がもっとも明るくなり、角度が大きくなるにつれて光の影響は少なくなります。
そして、光線方向と面Aの向いている方向がなす角度が90度を超えた場合、光が当たらなくなるために光の影響はなくなります。

Lighting3.png

この図で面Aの向いている方向として赤い矢印が描かれています。この「面が向いている方向」を示す単位ベクトルを法線ベクトルと呼びます。
基本的な光源処理においては頂点毎に求められた法線ベクトルを用いますが、この場合は面を構成する2辺に対する外積として法線ベクトルが表されます。

Normals1.png

光の影響の計算

Lighting4.png

法線ベクトルと光源方向のなす角度によって明るさが変化することがわかりましたので、これを数式に落としていきます。
図に示すように法線ベクトルNと光源方向を示すベクトルLを考えます。角θが0度のときがもっとも明るく、角θが90度以上で光の影響がゼロになるような式を考えると、光の強さfを用いてlight=f_linear(theta).pngといった線形で影響がでる式や、light=f_cos(theta).pngのように三角関数を使った式が例として挙げられます。

Lighting5.png

それぞれの式で角θと明るさの関係をプロットするとこのようになりますが、実際に光源の元で面の角度を変えてみると線形よりもcosの式に近いことが分かります。
その為、light=f_cos(theta).pngの式を用いることにします。

正規化されたベクトルN, Lからcos(theta).pngを求める為、以前にも使用したNdotL=cos(theta).pngを用いてベクトルの内積からcos(theta).pngを求めます。

正規化されたベクトルN, Lの長さは1になる為、光の影響は以下の式で表されます。
この係数を元々の色に乗算することで直接光による影響をシミュレートすることができます。
light=case_fNdotL.png

間接光(環境光)

前項では直接光が当たる場合のシミュレーションの一例を示しました。しかし、この場合光の当たっていない面は真っ黒になってしまいます。

現実の世界においては物に当たった光が反射して更に他の物を照らす為、光が直接当たらない場所でもある程度の明るさが維持されます。しかし、こういった間接光を正確にシミュレートするには、光線毎にどのオブジェクトに当たるか、どのように反射するか、反射した光線はどのオブジェクトに当たるかなどを計算する必要がある為、計算量が爆発しやすく、オブジェクトの数やシミュレートする空間の広さによっては現実的な時間で終わらせることができません。

3Dを扱うプログラムにおいてCPU/GPU負荷を抑えつつ間接光を表現する方法は色々と検討されていますが、今回は一番単純な環境光(Ambient)を用いる方法を解説します。
環境光ではオブジェクトに対して全方向から均一な光が当たっているものとして扱います。

Lighting6.png

全方向から光が当たっているものとする為、直接光が当たっていない影の部分に関してもある程度の明るさが保障されます。
以下に環境光を使用しない場合と、使用した場合のレンダリングの例を示します。

Lighting7.png

テクスチャマッピング

例えば現実世界のコーヒー缶を3Dのモデルとする場合、ものすごく細かいポリゴンで円柱を作り、各頂点の色を指定することによって缶の表面に描かれていた文字や図柄を再現することも可能は可能です。
しかし、これには膨大なポリゴン数が必要となり、現実的な時間でレンダリングすることが難しいですし、同一平面を表すポリゴンを細かくしていっても演算負荷が高まるだけで利点が薄いです。

その為、形状は頂点情報で表し、色などの見た目は画像を貼り付けるという、テクスチャマッピングの考え方が一般的に使われています。
これは少ない負荷で綺麗な見た目を作ることができますし、多少の凹凸であればモデルは平面にしてしまい、影を付けた画像を貼り付けることでポリゴン数を削減しつつ綺麗な見た目を維持することも出来ます。

例えばサイコロのモデルを作る場合、目の凹み部分などの形状を以下のような画像で表現し、立方体のモデルに貼り付けます。

Texture1.png

この面は4頂点で構成された単純な平面なので、実際に凹みがあるわけではありません。しかし、テクスチャを貼り付けることによって、以下のようにサイコロの見た目を表現することができます。

Texture2.png

UVマッピング

テクスチャという2Dの画像を3Dのモデルに貼り付ける為、この2つの対応関係を定義する必要があります。
これをUVマッピングといい、頂点毎に2D画像のどこに対応するのかをUV座標という形式で指定します。
指定方法はテクスチャサイズを1.0とし、Uが水平方向の位置、Vが垂直方向の位置を示します。

Texture3.png

あとがき

第一回から第六回まで大分かかりましたが、これで3Dの頂点情報、光源情報などから画面上への描画までがひと通り出来るようになりました。
全てを説明できたわけでは無いですが、これらの処理がOpenGL ES 1.0(固定機能パイプライン)における描画処理になります。

次回はOpenGL ES 2.0。プログラマブルシェーダの季節です。

参考情報 / 使用ツール

エンジニア採用中!私たちと一緒に働いてみませんか?