Ashikhminモデル★ 1.はじめに
異方性ライティングやってみたかったので,やってみたつもりなんですが…
★ 2.Ashikhmin Model
Ashikhminモデルとは,Michael Ashikhminさん達に,よって提唱された異方性の照明モデルだそうです。サテンやベルベットの生地に適したライティングみたいです。
プログラム作成にあたり,こちらの資料(http://www.cs.utah.edu/~shirley/papers/jgtbrdf.pdf)を参考にしましたが,全部きっちり読んでいないので間違っているところがあるかもしれません。 モデルは以下の式のようにdiffuse項とspecular項の足し算になっているそうです。 ![]() BRDFのspecular項のρsは以下のような式になるそうです。 ![]() 資料を見る限り,ベクトルkについての説明はないようですが,k1とk2のどちらでもいいから説明をしていないという風に解釈しておきます。あと上の式で,(n・h)の右に書いてあるごちゃごちゃした所はわかりづらいかもしれませんが(n・h)の指数になっています。Fはフレネル項で資料ではSchlickの近似を使用しているそうで,それを使うとフレネル項Fは次のようになるそうです。 ![]() つづいて,diffuse項のρdですが下のような式になります。 ![]() あとは上記のムズカシイ式をごりごり計算していけばよいようです。 ★ 3.実装
シェーダの方で上記のムズカシイ式をごりごりと計算していきます。
まず,モデルを用意します。接ベクトルと従法線ベクトルをシェーダに渡す必要があるので,きちんとしたUVマッピングが行われているモデルを用意しましょう。これは,テクスチャ座標をもとにして接ベクトルと従法線ベクトルをが計算されるためです。 接ベクトルと従法線ベクトルの生成はソリューションエクスプローラーでモデルファイルを右クリックし,でてきたポップアップメニューの中からプロパティを選択します。 すると,プロパティウィンドウがでるのでContent Processorと書いてる所の左に"+"マークが出ているはずなので,"+"マークを押して隠れている項目を展開します。すると上から4つ目辺りに"Generate Tangent"という項目があるので,デフォルトだとFalseになっているので,これをTrueにして接ベクトルが生成されるようにしておきます。 ![]() あとはシェーダファイルの方にコードを記述していきます。 まずはspecular項ですが,下のSpecularTerm()関数内で計算を行うようにしました。
00071: //----------------------------------------------------------------------------------------------------
00072: // Name : SpecularTerm()
00073: // Desc : スペキュラー項の計算
00074: //----------------------------------------------------------------------------------------------------
00075: float SpecularTerm(
00076: float nu, float nv,
00077: float3 n, float3 h,
00078: float3 k1, float3 k2,
00079: float3 u, float3 v, float3 spec )
00080: {
00081: float hu = dot( h, u );
00082: float hv = dot( h, v );
00083: float hn = dot( h, n );
00084: float3 k = k1;
00085: float hk = dot(h, k);
00086: float nk1 = dot(n, k1);
00087: float nk2 = dot(n, k2);
00088: float exponent = ( ( nu * hu * hu ) + ( nv * hv * hv ) ) / ( 1 - hn * hn );
00089:
00090: float term1 = sqrt( (nu + 1) * (nv + 1) )/ ( 8*Pi );
00091: float term2 = pow(hn, exponent)/ ( hk * max( nk1, nk2 ) );
00092:
00093: return term1 * term2 * FresnelTerm( spec, hk );
00094: }
00095:
93行目にあるFresnelTerm()関数の中身は次のようになっています。
00064: //---------------------------------------------------------------------------------------------------
00065: // Name : FresnelTerm()
00066: // Desc : フレネル項の計算
00067: //---------------------------------------------------------------------------------------------------
00068: float3 FresnelTerm( float3 Rs, float LH )
00069: { return ( Rs + (1-Rs) *( 1- pow5(LH) ) ); }
00070:
そのまんまですね。続いてdiffuse項ですが,DiffuserTerm()関数内で計算を行っています。
00096: //----------------------------------------------------------------------------------------------------
00097: // Name : DiffuseTerm()
00098: // Desc : ディフューズ項の計算
00099: //----------------------------------------------------------------------------------------------------
00100: float3 DiffuseTerm( float3 Rd, float3 Rs, float3 n, float3 k1, float3 k2 )
00101: {
00102: float3 term1 = ( (28*Rd) /(23*Pi) ) * ( 1 - Rs );
00103: float term2 = ( 1 - pow5( 1 - dot(n, k1)*0.5 ) );
00104: float term3 = ( 1 - pow5( 1 - dot(n, k2)*0.5 ) );
00105:
00106: return term1 * term2 * term3;
00107: }
00108:
上述したSpecularTerm()とDiffuseTerm()の呼び出しはピクセルシェーダで行うようにしてみました。頂点シェーダの方は,普通に透視投影して,法線ベクトルや接ベクトル,従法線ベクトルのデータをピクセルシェーダ側に渡すだけの処理になっています。ピクセルシェーダでは頂点シェーダから送られたきたデータの正規化などを行い,ライティングをします。
00130: //----------------------------------------------------------------------------------------------------
00131: // Name : PixelShaderFunction()
00132: // Desc : ピクセルシェーダ
00133: //----------------------------------------------------------------------------------------------------
00134: float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
00135: {
00136: // 正規化しておく
00137: float3 N = normalize(input.Normal);
00138: float3 U = normalize(input.Tangent);
00139: float3 V = normalize(input.Binormal);
00140: float3 L = normalize(g_LightPosition - input.ObjPosition);
00141: float3 E = normalize(g_EyePosition - input.ObjPosition);
00142: float3 H = normalize( L + E );
00143:
00144: // TODO: add your pixel shader code here.
00145: float3 diffuse = ModelDiffuseColor * DiffuseTerm( ModelDiffuseColor, g_Rs, N, L, E );
00146: float3 specular = SpecularTerm( g_nu, g_nv, N, H, L, E, U, V, g_Rs );
00147: float4 color = float4( diffuse + specular, 1.0 );
00148:
00149: return color;
00150: }
00151:
とりあえず,こんな感じで一番上のような画像は作れるのですが,ちゃんと合っているかどうかちょっと自信ありません。★ Download
本ソースコードおよびプログラムを使用したことによる如何なる損害も製作者は責任を負いません。
本ソースコードおよびプログラムは自己責任でご使用ください。 プログラムの作成にはMicrosoft Visual Studio 2005 SP1 Professional, XNA Game Studio 2.0を用いています。 |