その7 レイヤー要素を取得する:UV
前章の法線に続き、本章ではレイヤー要素の中のUV座標を取得してみます。取得の方法は基本的には法線の時のそれと変わりませんが、UVの種類が沢山出てきますので、それをどうまとめるかがちょっとポイントになりそうです。
@ UVの種類
UVはテクスチャの張り付く座標です。テクスチャ座標(テクセル)は通常XY共に0.0〜1.0の範囲で定義されていて、頂点のUVに合わせるように貼り付けられます。UV座標がもれなく定義されているお陰でモデルは綺麗に色付くわけです。
レイヤー(KfbxLayer)からUV座標を取得するには各種取得メソッドを用います。とりあえずマニュアルにあるUV取得用のメソッドを列挙すると次のようになります:
・ GetAmbientUV ()
・ GetAmbientFactorUV ()
・ GetDiffuseUV ()
・ GetDiffuseFactorUV ()
・ GetSpecularUV ()
・ GetSpecularFactorUV ()
・ GetEmissiveUV ()
・ GetEmissiveFactorUV ()
・ GetShininessUV ()
・ GetNormalMapUV ()
・ GetBumpUV ()
・ GetTransparentUV ()
・ GetTransparencyFactorUV ()
・ GetReflectionUV ()
・ GetReflectionFactorUV ()
・ GetUVs (KFbxLayerElement::ELayerElementType pTypeIdentifier =
KFbxLayerElement::eDIFFUSE_TEXTURES)
・ iGetUVSetCount ()
凄いあります!意味が良く分からないのもあるかもしれませんので一通りさらってみます。
○ Ambient(アンビエント)、Ambient Factor
アンビエント(環境光)はモデル対して全方向から当たる特殊な光です。光が当たってる部分は均等に明るくなります。Ambient Factorというのはその強度(Weight)の事で、普通0〜1の間を取ります。あるピクセルに対してアンビエントカラーは次のように作用します:
Color += Global_Ambient + Ambient * Ambient_Factor * Light_Ambient
Global_Ambientというのは全宇宙を照らす明かりの事で、目に見えるすべてのものを照らします。Light_Ambientはライトが持つアンビエント色で、アンビエントカラーの色味に掛け算されます。光が当たっているか否かで色味が二極化する特殊なライトなんです。
○ Diffuse(ディフューズ)、Diffuse Factor
モデルが本来持っている色味を表すのがディフューズ(拡散反射光)です。この色は入力されてきた光の角度によってその強度が変わる性質を持っています。ある点に対して光がど真正面(0度)で入ってきた場合は最大の跳ね返りを見せるので「ディフューズ色×入力された光の色」でその点が表現されます。しかし角度が変わると跳ね返る光の量が減ってしまうため暗く見えるようになります。一般にそれを加味した計算式は次のようになります:
Color += ( Diffuse * Input_Color ) * dot( Normal, -Input_Vector) * Diffuse_Factor
dotとは「内積」です。上では法線と入力光の逆方向の内積を取っています。DiffuseとInput_Colorの掛け算で色味が決まり、内積の部分でその強度が決まっているわけです。Diffse Factorは強度を微調整します。
○ Specular(スペキュラ)、Specular Factor
スペキュラ(鏡面反射光)はモデルの艶を表現する時に使用します。スペキュラが無いとプラスチックのようなのっぺりとした面になりますが、スペキュラが高いと磨き上げた金属のような表面になります。スペキュラの計算式はかなり面倒なのですが、こんな感じです:
HalfWay_Vector = normalize( normalize( Vertex_Pos - Camera_Pos ) + Light_Pos - Vertex_Pos
)
Color += Specular * Light_Specular * { dot( HalfWay_Vector, Normal
)^Power } * Specular_Factor
ハーフウェイベクトルというのはカメラと光の方向を決めるベクトルのようなもので、下段の式でそれと法線の内積が取られています。さらにそれをPowerでべき乗する事で角度が出てきた場合の減衰量を大きくします。このPowerが大きい程スペキュラがきつくなるので、モデルは鏡面のようになっていきます。
○ Emissive(エミッシブ)、Emissive Factor
エミッシブ(放射光)はモデル自身が放つ光を表します。太陽や電球などは自分で光っているため真っ暗闇でも明るく見えます。この光は問答無用の光です。世界が真っ暗でもモデルは光るので、ある点の色は単にエミッシブの色そのものとなります:
Color += Emmisive
○Shininess(シャイネス)
シャイネス(光沢)はスペキュラと似ています。色々と調べたのですがちょっと不明です(^-^;;。計算式を含め知っている方はご教授下さい。
○ NormalMap(法線マップ)
法線マップとはモデルの表面に貼り付けるテクスチャの1つで、各テクセルにモデル表面の法線の向きが刻印されています。これによって1テクセル単位で反射する光の強さを制御する事ができるようになります。
○ BumpMap(バンプマップ)
バンプマップは法線マップと同様にモデルの表面の凹凸をテクセル単位で表すテクスチャです。法線マップがテクセル一つ一つに向きを定義するのに対し、バンプマップは一般にグレースケールでモデルの凹凸強度を表します。これはいわゆるハイトマップ(高さマップ)と呼ばれるものです。つまり、バンプマップには向きの概念がありません。バンプマップの向きを決めるのは法線マップです。この違い、微妙ですが重要です。
○ Transparent(透過度)、Transparent Factor
トランスペアレント(透過度)はその名の通りモデルの透明度を決めます。アルファ成分にのみ作用する値です:
Alpha *= Transparent * Transparent Factor
○ Reflection(反射)、Reflection Factor
リフレクション(反射)は鏡のように物体を映し出す効果の事です。これは一般には「環境マップ」と呼ばれるモデルの周りの風景を投影したテクスチャに対して効果を発揮する値です。リフレクションが強ければ周りの風景をより強力に反射し、低ければ反射せずに吸収してしまいます。計算式は簡単に言えば入力する光の色を反射するだけです:
Color += Input_Environment_Color * Reflection * Reflection_Factor
このように光については様々な要素があります。ゲームなどではこれらに該当するテクスチャを作り、それを1つのモデルに重ね貼りすることで非常に複雑で現実味のある表面の質を再現しています。この章の着眼であるUVは、その貼り付ける位置を決める座標です。各要素に対して独立したUVが必要なのはうなずけますよね。
A UVを取得する
UVは頂点に対して定義される座標です。これを取得するには冒頭に挙げた取得メソッドを用います。示し忘れていましたが、上のGetメソッド系はKFbxLayerElementUVクラスのポインタを返します。このクラスは内部的にはKFbxVector2という2次元ベクトルの配列になっています。アンビエントもディフューズもすべて同じです。ですから欲しいUVを取得するコードは凄く似通ったものになります。そこで汎用関数を以下のように作ってみました:
汎用要素取得テンプレートメソッド template< class Elem, class Out >
void GetElement( int VertexIndex, Elem *pElem, Out *pOut )
{
KFbxLayerElement::EMappingMode mode = pElem->GetMappingMode();
if ( mode == KFbxLayerElement::eBY_CONTROL_POINT ||
mode == KFbxLayerElement::eBY_POLYGON_VERTEX )
{
KFbxLayerElement::EReferenceMode ref = pElem->GetReferenceMode();
if ( ref == KFbxLayerElement::eDIRECT )
{
// 参照モードは直接
*pOut = pElem->GetDirectArray().GetAt(VertexIndex);
}
else if ( ref == KFbxLayerElement::eINDEX_TO_DIRECT )
{
// インデックスを通して取得
int index = pElem->GetIndexArray().GetAt(VertexIndex);
*pOut = pElem->GetDirectArray().GetAt(index);
}
}
}
この関数を用いて例えばアンビエントテクスチャのUV座標を取得したい時は次のようにします:
アンビエントUVを取得 KFbxLayerElementUV* pUV;
KFbxVector2 UVPos;
pUV = pMesh->GetLayer(l)->GetAmbientUV();
GetElement( v, pUV, &UVPos );
こうすると非常に楽ですよね。この汎用テンプレート関数は先の章で取得した法線などでも使えます。
取得したUVはレイヤーごとにまとめ、描画時にプログラム側でシェーダに渡す事になります。この機構については多分ず〜〜っと先のお話になりますが、たどり着いた時には記事にしたいと思います。