Blenderにおける異方性のパラメーター
anisotropicGGXではという粗さのパラメータが必要ですが実用上ではそれぞれを直接指定することは多分ありません。実際に、BlenderのPrincipled BSDFではRoughnessという粗さのパラメータが1つだけで済まされており、別途で異方性を作るためのパラメーターとして「Anisotropic」と「Anisotropic Rotate」の2つが用意されています。
Principled BSDFの異方性はどんな感じなのかは灰ならしさんという方がとても分かりやすくまとめている記事がございますのでこちらをご覧ください。
それで私の作成したBRDFにこのパラメーターで異方性を制御してみたいと思い、実際のBlenderのソースコードを見てどのようにやっているのかを調べ、実装してみましたので記事にしてみたという次第です。
Anisotropic
「Anisotorpic」は単純に異方性がどれだけ生じているかを示すパラメーターで1にするほど異方性が強くなります。その実装はBlenderのこのソースコードにあり、
以下のような実装となっています。
float aspect = sqrt(1.0 - Anisotropic * 0.9); float r2 = Roughness * Roughness; float alpha_x = r2 / aspect; float alpha_y = r2 * aspect;
かなり単純なもので「Roughness」パラメータの値を「Anisotropic」の値でずらして、それぞれずらされたラフネス値をに渡すという処理をしているだけです。なのでそのままこれを実装すれ「Anisotropic」を作ることができます。
Anisotpic Rotate
anisotropicGGXの制御は「Anisotorpic」というパラメータだけではまだ不十分です。本当ならば接線方向u,従法線方向vの両方向で異方性を作ることができるのですが、このパラメーターだけでは接線方向の1方向の異方性しか扱えません。そこで異方性の回転として「Anisotropic Rotate」というパラメーターが存在します。
「Anisotropic Ratate」は数値を変えると異方性の持つ方向がそのまま回転します。そのため、通常の接線方向、従法線方向だけではなく斜めといったどんな方向でも異方性を持つことができるようになっています。実はを変えることなく、接空間の接線方向と従法線方向を回転させた接空間で計算すればこのようなことができます。
接空間の生成の際、法線と上方向のベクトルtex:T = の外積を取っていますが、このを法線回りで回転させた上で外積を取ると当然それによって生成される接線ベクトルと従法線ベクトルも回転することになります。BlenderではこのTを「Anisotopic Rotate]の値分回転させて、接空間の生成に使用するいう実装を行っています。
実装はいくつかコードをまたいでおり、
https://github.com/blender/blender/blob/master/intern/cycles/kernel/osl/shaders/node_principled_bsdf.osl https://github.com/blender/blender/blob/master/intern/cycles/kernel/closure/bsdf_microfacet_multi.h https://github.com/blender/blender/blob/594f47ecd2d5367ca936cf6fc6ec8168c2b360d0/intern/cycles/kernel/kernel_montecarlo.h
少々変数名や言語が変わっているので流れを見ていきますとまずnode_principleの方でtangent(Priciple BSDFのパラメータの1つ、通常なら(0,1,0)が入る)でを作り、そのを「Anisotropic」で回転させており、
vector T = Tangent; T = rotate(T, AnisotropicRotation * M_2PI, point(0.0, 0.0, 0.0), Normal);
という実装がなされています。これは(多分)ノーマル方向を軸にTを * AnisotropicRotation角度分回転させるという処理です。そして次にBSDFの実装部分bsdf_microfacetではこのTを元に接空間を生成しているようです。
float3 X, Y; make_orthonormals_tangent(N, bsdf->T, &X, &Y);
このmake_orthonomals_tangentという関数は別のkernel_montecalroのところで以下のように定義されており、単純なTを用いた接空間生成であることがわかります。
ccl_device void make_orthonormals_tangent(const float3 N, const float3 T, float3 *a, float3 *b) { *b = normalize(cross(N, T)); *a = cross(*b, N); }
このような形で実装が行われているようです。
私の実装ではBSDFに接空間の処理を任せていないので、さらに接空間内で回転させた基底をBSDF内で作り入射方向ベクトルと出射方向ベクトルを変換した上でGGXの計算を行う形で実装を行いました。
// tangent rotate // (1,0,0)と(0,0,1)の基底を回転させて接空間を回転させる Vec3 b(std::cos(rotateTan), 0, std::sin(rotateTan)); Vec3 t(-std::sin(rotateTan), 0, std::cos(rotateTan)); Vec3 n = Vec3(0.0, 1.0, 0.0); //入射方向ベクトルを回転させた基底での座標に変換する Vec3 rtan = worldtoLocal(wo, b, n, t); Vec3 i = rtan; //入射方向ベクトル Vec3 m = this->samplem(i, sampler->sample(), sampler->sample()); //ハーフベクトル Vec3 o = reflect(i, m); //出射方向ベクトル wi = localToWorld(o, b, n, t); //元の接空間の座標に戻す
終わり
以上のようにすれば異方性GGXの制御がBlenderと同じように行えます。生のパラメータを扱うのは予測がつきづらく、狙った感じにならないことがよくありました。Blenderはちゃんと誰でも扱えるようにshaderのインターフェースを作っているだけあって、こうしたパラメータが直感的に行えるようになっており、そうした面で非常に良い勉強となりました。
Blenderのソースコードを見るととても勉強になるのでまた何かの拍子に見てみるのもいいかもしれない