"Mental Ray 用のシェーダ mia_material" (SIGGRAPH 2013)について

概要

  • この記事では SIGGRAPH 2013 Course の「物理ベースなシェーディングの理論と実践」で話があった 物理的にそれっぽく見える Mental Ray のシェーダである mia_material に関する講演の説明を簡単にします.
  • 以前の記事で Unreal Engine4 と Pixar の講演の説明についてあまりにも長く書きすぎてしまったので, 気になったところを先にピックアップして書くようにします.

参考文献

mia_material とは ?

  • 物理的にもっともらしく見える(必ずしも正しくはない) mental ray のシェーダです.

縦伸びのスペキュラが起こる原因

  • まず, 縦伸びのスペキュラが起こる原因についての説明が個人的に良かったです.
  • 現実世界では下図のように太陽(スフィアライト)のスペキュラハイライトが海(平面)に反射して縦に伸びる現象が起きます.


  • これが起きる理由については海の法線が均一ではなく, いろんな方向を向いて散っているからです.
  • つまり, ラフネスが粗い(完全に滑らかな面ではない)ということになります.
  • このように面の法線の向きにばらつきがあると, そこを反射する反射ベクトルにもばらつきが出ることになります.
  • しかし, 面の法線のばらつきの種類によって反射ベクトルのばらつき方が変化します.
  • 例えば, 下図の左側のように法線が手前〜奥方向にばらついているときには, 反射ベクトルが上下に大きくばらつきます.
  • 一方で, 下図の右側のように法線が横方向にばらついているときには反射ベクトルも横方向にばらつくのですが, 変化は前者ほど大きくないです.


  • つまり, 法線が全方向(厳密には半球状)に均一にばらついているときでも, 反射ベクトルは上下方向の方にばらつきやすくなります.
  • ということは平面上にある点については, 横方向よりも手前〜奥方向の方が反射ベクトルが光源である太陽にヒットする点が多くなります.
  • 従って, 太陽(スフィアライト)のスペキュラハイライトが海(平面)に反射する場合は平面の手前〜奥方向の方が反射ベクトルが光源にヒットしやすいので, スペキュラハイライトが縦に伸びます.

「レイヤー」と「エネルギー保存」

  • この「レイヤー」と「エネルギー保存」の箇所も個人的に気になったところです.
  • まず、「レイヤー」についてです.
  • 光がレイヤーに対して進入しようとしたときに, 光の反射や透過が起こります.
  • このときの光の振る舞いの種類としては下図のようにリフレクション(Reflections), ディフューズ(Diffuse), 屈折(Refractions), 透過(Translucency)の 4 種類としています.


  • 透過に関しては上図の右下のように, 光が入射する際に物質の中でディフューズランバート反射のように拡散するようにして計算しています.
  • また上のレイヤーに関連して, 光がエネルギー保存則を満たすようにしています.
  • まず光がレイヤーに当たると, まずリフレクションによってエネルギーが奪われます.
  • そして, 透過度(Transparency)の逆(ここでは 1.0 - Transparency)に応じてディフューズ(反射)がエネルギーを奪います.
  • 最後に, 透過光が残りのエネルギーを持って透過します.


  • 下に擬似コードを貼っておきます.
  • この計算については減算の仕方が常にグレースケールになるように ( RGB の輝度の重みを考慮して) 計算しているので, 物理的には正しくないです.
  • しかし, 見た目的に不自然に見えることが少ないのでこの方法を使っているとのことです.


その他の要素

  • ディフューズは Oren-Nayer モデルです.


  • ただ右下図のように 0.0f 付近で cos カーブが丸みを帯びていない(2次微分が不連続)ので, 滑らかに補間する処理を入れて左下のようにしています.
  • こうすると, ディフューズの境界部分のエッジが目立ちにくくなります.

  • フレネルは物質の屈折率 あるいはカーブで制御します.

  • スペキュラ直接光であるハイライトについてはハイライトが広がりを持つように, 3 種類の Ward ハイライトを混ぜています.


  • 式については下のようになっていると思います.
ward_base_exponent = 2.0^( glossiness * 8.0 );
ward_specular = calc_ward_specular( ward_exponent );

specular  = ( 1.0f / 6.0f ) * calc_ward_specular( ward_base_exponent ); 
specular += ( 1.0f / 3.0f ) * calc_ward_specular( ward_base_exponent  / 2.0f );
specular += ( 1.0f / 2.0f ) * calc_ward_specular( ward_base_exponent / 4.0f );

  • スペキュラ間接光であるリフレクションには, 伸び具合というパラメータがあります.
  • 左下は反射ベクトルを球状にジッタリング(ずらす)した場合のもので, Phong スペキュラの計算に近いです.
  • 右下は表面のでこぼこによって法線ベクトルをジッタリング(ずらす)した結果, 反射ベクトルが縦方向にだけジッタリングします. こちらは Blinn-Phong スペキュラの計算に近いです.

  • ただこの方法だと, ハイライトとリフレクションが一致しないという問題が起こります.
  • この資料には解決方法が特に書いていない ? ような気がします.(間違っていたらすみません.)