LIFE LOG(MochiMochi3D)

MochiMochi3D

3D関係の記事を書きます

Blenderでのanisotropic GGXのパラメーターを再現する

Blenderにおける異方性のパラメーター

 anisotropicGGXでは\alpha_x,\alpha_yという粗さのパラメータが必要ですが実用上ではそれぞれを直接指定することは多分ありません。実際に、BlenderのPrincipled BSDFではRoughnessという粗さのパラメータが1つだけで済まされており、別途で異方性を作るためのパラメーターとして「Anisotropic」と「Anisotropic Rotate」の2つが用意されています。

f:id:kinakomoti321:20220129012354j:plain
Principled BSDFの異方性に関わるパラメーター
 Principled BSDFの異方性はどんな感じなのかは灰ならしさんという方がとても分かりやすくまとめている記事がございますのでこちらをご覧ください。

hainarashi.hatenablog.com

 それで私の作成したBRDFにこのパラメーターで異方性を制御してみたいと思い、実際のBlenderソースコードを見てどのようにやっているのかを調べ、実装してみましたので記事にしてみたという次第です。

Anisotropic

 「Anisotorpic」は単純に異方性がどれだけ生じているかを示すパラメーターで1にするほど異方性が強くなります。その実装はBlenderのこのソースコードにあり、

github.com

 以下のような実装となっています。

    float aspect = sqrt(1.0 - Anisotropic * 0.9);
    float r2 = Roughness * Roughness;

    float alpha_x = r2 / aspect;
    float alpha_y = r2 * aspect;

かなり単純なもので「Roughness」パラメータの値を「Anisotropic」の値でずらして、それぞれずらされたラフネス値を\alpha_x,\alpha_yに渡すという処理をしているだけです。なのでそのままこれを実装すれ「Anisotropic」を作ることができます。

Anisotpic Rotate

 anisotropicGGXの制御は「Anisotorpic」というパラメータだけではまだ不十分です。本当ならば接線方向u,従法線方向vの両方向で異方性を作ることができるのですが、このパラメーターだけでは接線方向の1方向の異方性しか扱えません。そこで異方性の回転として「Anisotropic Rotate」というパラメーターが存在します。 

「Anisotropic Ratate」は数値を変えると異方性の持つ方向がそのまま回転します。そのため、通常の接線方向、従法線方向だけではなく斜めといったどんな方向でも異方性を持つことができるようになっています。実は\alpha_x,\alpha_yを変えることなく、接空間の接線方向と従法線方向を回転させた接空間で計算すればこのようなことができます。

 接空間の生成の際、法線nと上方向のベクトルtex:T = 外積を取っていますが、このTを法線n回りで回転させた上で外積を取ると当然それによって生成される接線ベクトルと従法線ベクトルも回転することになります。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)が入る)でTを作り、そのTを「Anisotropic」で回転させており、

  vector T = Tangent;
  T = rotate(T, AnisotropicRotation * M_2PI, point(0.0, 0.0, 0.0), Normal);

という実装がなされています。これは(多分)ノーマル方向を軸にTを2\pi * 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内で作り入射方向ベクトル\omega_iと出射方向ベクトル\omega_oを変換した上で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ソースコードを見るととても勉強になるのでまた何かの拍子に見てみるのもいいかもしれない

参考文献

github.com

hainarashi.hatenablog.com