ブログトップ 記事一覧 ログイン 無料ブログ開設

OLD hanecci’s blog : 旧 はねっちブログ

2013-05-25 フレネル反射率について

フレネル反射率について

概要

フレネル反射率とは ?

  • 空気から他の物質に入射しようとした光は, その表面を跳ね返る光(反射する光)とその表面の中に進入する光(屈折する光)の 2 つに分かれます.
  • つまり, "入ろうとした光" = "反射した光" + "屈折した光" というように, 2 つに分かれることになります.
  • この "入ろうとした光" を100 % としてそれが面に対して入射の角度 10 °で入ろうとした結果, "反射する光" の割合が 90% で 屈折する光が 10% になったとします.
  • この場合, フレネル反射率 F(10°) = 0.9 となります.

  • フレネル反射率は "物質ごとの屈折率" "光が物質に対して入る角度" の 2 つによって決まります.

フレネル反射率 F(0°)について

  • ここで, 面に対して真上から垂直に入射するときのフレネル反射率を, フレネル反射率 F(0°)と呼びます.


金属のフレネル反射率

  • まず, 金属のフレネル反射率 F(0°) は全体的に高めで, 大雑把に言うと F(0°) = 0.5-0.9 の範囲にあったりします.
  • また "光が物質に対して入る角度" によって, フレネル反射率も少し変化します.
  • 下図のようにほぼ真上から光が入った場合はF(10°) = 0.90 で, 光が面に近い角度で入るときは F(80°) = 0.95 です.
  • 光が入射する角度が 90°になると, 最終的にフレネル反射率は F(90°) = 1.0 となります.

f:id:hanecci:20130526200220j:image:w640

非金属(水やプラスティックなど)のフレネル反射率

  • 次に金属以外(水やプラスティックなど)のフレネル反射率F(0°) については, 金属よりは全体的に低めでF(0°) = 0.02〜0.2 ぐらいの範囲です.
  • しかし, "光が物質に対して入る角度" によってかなり変化します.
  • 例えば, 下図のように水に対してほぼ真上から光が入った場合は F(10°) = 0.02 で, 光が面に近い角度で入るときは F(80°)= 0.90 です.
  • 光が入射する角度が 90°になると, 最終的にフレネル反射率は F(90°) = 1.0 となります.

f:id:hanecci:20130526200223j:image:w640

  • 例えば, 下図のようにプラスティックに対してほぼ真上から光が入った場合は F(10°) = 0.05 で, 光が面に近い角度で入るときは F(80°) = 0.95 です.

f:id:hanecci:20130526200222j:image:w640

光が垂直に入射するときの非金属(水, ガラス, ダイヤモンド)のフレネル反射率 F(0°)

  • 下図は光が垂直に入射するときの非金属(水, ガラス, プラスチック, ダイヤモンド)などのフレネル反射率 F(0°)の表です.

f:id:hanecci:20130515051857j:image:w360

  • 非金属のフレネル反射率については R = G = B で同じ値で大丈夫です.
  • つまり, 非金属のスペキュラ反射においては基本的には光源の色味だけが載ります.
  • 具体的には, 白い光源を当てたときは白いスペキュラのハイライトになります.

光が垂直に入射するときの金属のフレネル反射率 F(0°)

  • 下図は光が垂直に入射するときの金属(金, 銀, 銅, 鉄, アルミニウム)などのフレネル反射率 F(0°)の表です.

f:id:hanecci:20130515051938j:image:w360

  • 金属のフレネル反射率については波長によって異なるので, RGB で別の値になります.
  • つまり, 金属のスペキュラ反射においては金属特有の色がつくことになります.
  • 具体的には, 白い光源を当てたときには金属の色がついたスペキュラのハイライトになります.


フレネル反射率のグラフ

  • 物質の種類に応じたフレネルの反射率をグラフにしたものが下図で, 図の横軸は入射する光線と法線の間の入射角度で, 縦軸はフレネル反射率を示しています.

f:id:hanecci:20130515052143j:image:w360

  • この図を見ると, やはり金属については入射角度が横軸方向に 0〜90 度と変化しても, フレネル反射率の変動が少ないです.
  • 一方で, 水やダイヤモンドは入射角度によってフレネル反射率が変化しています.


  • もう少しこの図を分析すると, フレネル反射率の変化については 下の 3 つの区間に分けることができます.
    • 緑色の区間 F(0°)〜 F(45°) : フレネル反射率がほとんど変化しないです
    • 黄色の区間 F(45°)〜F(75°) : フレネル反射率が少し変化します
    • 赤色の区間 F(75°)〜F(90°) : フレネル反射率が急激に変化して, 1.0 に近づきます.

f:id:hanecci:20130515052102j:image:w360

補足資料 : フレネル反射率の他の資料

  • フレネル反射率の他の資料があったので下に貼り付けておきました.
  • 左下図が金属のフレネル反射率の図で, 右下図が非金属のフレネル反射率の図です.

f:id:hanecci:20130530014340j:image:w480


Schlick によるフレネル反射率の近似

  • 本当は物質ごとに 入射角度0°〜90°に応じて全てのフレネル反射率を持っていればいいのですが, この方法はあまり実用的ではないです.
  • そこでコンピュータグラフィックスではフレネル反射率の計算は, Schlick での近似式を使うことが多いです.

  • Schlick での近似式においては下図のように「光が垂直に入射するときのフレネル反射率 F(0°)」を物質ごとの用意しておきます.

f:id:hanecci:20130515052020j:image:w360

  • そして, 入射角度0°〜90°に応じたフレネル反射率の変化は下図の数式で近似します.
  • ここで Cspec は 「光が垂直に入射するときのフレネル反射率 F(0°)」のことで, 別名でよくスペキュラリフレクタンスと呼びます.
  • またマイクロファセットを使ったシェーディングモデルの場合にはハーフベクトル(h)を使った, 下の方の数式を用います.

f:id:hanecci:20130515051815j:image:w360

  • 下は光が垂直に入射するときのフレネル反射率 F(0°)を使った「Schlick によるフレネル反射率の近似計算」をするシェーダーのコード例です.
  • 下でのフレネル反射率はスカラーで非金属の場合です.
analytic

# variables go here...
# only floats supported right now.
# [type] [name] [min val] [max val] [default val]

::begin parameters

# フレネル反射率 F(0°)がパラメータです
float f0 0 1 0.1

::end parameters


# Then comes the shader. This should be GLSL code
# that defines a function called BRDF (although you can
# add whatever other functions you want too). 

::begin shader

# Schlick によるフレネル反射率の計算です
float Fresnel(float f0, float u)
{
    return f0 + (1-f0) * pow(1-u, 5);
}

vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
    # ハーフベクトルを求めます
    vec3 H = normalize(L+V);

    # フレネル反射率を求めます
    float F = Fresnel(f0, dot(L,H));

    return vec3(F);
}

::end shader

  • そして, 下が金属の場合も考慮したフレネル反射率の Schlick 近似になります.
// "Crafting a Next-Gen Material Pipeline for The Order: 1886" SIGGRAPH 2014
// ===============================================================================
// Calculates the Fresnel factor using Schlick's approximation
// ===============================================================================
float3 Fresnel(in float3 specAlbedo, in float3 h, in float3 l)
{
float lDotH = saturate(dot(l, h));
return specAlbedo + (1.0f - specAlbedo) * pow((1.0f - lDotH), 5.0f);
}

  • 一方で下はフレネル反射率 F(0°)をまず物質の相対屈折率から計算します.
  • そして, その値から「Schlick によるフレネル反射率の近似計算」をするシェーダーのコード例です.
  • (下でのフレネル反射率はスカラーになっているので, 金属の場合は 3 次元のベクトルを使う必要があります.)
analytic

# variables go here...
# only floats supported right now.
# [type] [name] [min val] [max val] [default val]

::begin parameters

# 物質の相対屈折率がパラメータです
float ior 1 3 1.2

::end parameters


# Then comes the shader. This should be GLSL code
# that defines a function called BRDF (although you can
# add whatever other functions you want too). 

::begin shader

# Schlick によるフレネル反射率の計算です
float Fresnel(float f0, float u)
{
    return f0 + (1-f0) * pow(1-u, 5);
}

vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
    # ハーフベクトルを求めます
    vec3 H = normalize(L+V);

    # 物質の相対屈折率の比から, フレネル反射係数 F(0°)を求めます
    float f0 = pow((ior-1)/(ior+1), 2);

    # フレネル反射率を求めます
    float F = Fresnel(f0, dot(L,H));

    return vec3(F);
}

::end shader


ガウシアン球によるフレネル反射率の近似

  • Schlick によるフレネル反射率の近似の式よりも GPU の処理コストを減らすために, 下図の下側の数式のガウシアン球によるフレネル反射率の近似計算がFarCry3 で使われていました.

f:id:hanecci:20130530014341j:image:w640

  • 元ネタは "Specular Showdown in the Wild-West" Stephen Hill です.


参考文献