投稿者 tel | 2013年3月13日

Cook-Torranceのシェーディングモデル

鏡面反射のシェーディングモデルの1つであるクック・トランス(Cook-Torrance)のモデルを実装してみる。クック・トランスのモデルでは金属の反射に適しているらしい。

調べてみたところクック・トランスのモデルの式がどれも若干違っていた。英語のwikipediaの参考されていたpdfを元にしてみる。(リンクとリンク先がどうも違う論文のようであるが)

クック・トランスのモデルの鏡面反射は以下のようになる。

specular

上記の式でRsは反射率、Fはフレネル項、Dは微小面の分布関数、Gは幾何学的減衰係数、Nは法線、Lは光源へのベクトル、Vは視点へのベクトルである。Dはベックマン分布が用いられる。(DはdistributionのD)

beckmann

αはNとH(ハーフベクトル)のなす角度なのでcosα=N・H。mは面の粗さを調整するパラメータ。

geometry_term

3つのうち最小のものをGにする。フレネル項は

fresnel

cg

nは相対屈折率。nは反射率から求められる。F0はθが0のときの反射率。金属の反射率は光の波長に依存するのでF0をベクトルにしておく。

n

Wikipediaだとフレネル項が近似式になっている。

f_approximation

上記の式はSchlickの近似となっているが詳しいことは不明。(そもそもλが何を表している?)実際Schlickの近似でググると出てくるのは以下の式。

f_approximation2

今回はフレネルの式をそのまま使った。左がCook-Torranceで右側がBlinnのモデルで描画した結果。

cook_torranceblinn

uniform vec4 diffuse;  // 拡散反射係数
uniform vec4 specular; // 鏡面反射係数
uniform vec4 ambient;  // 環境光
uniform float microfacet; // 面の粗さ

varying vec3 vNormal;
varying vec3 vView;
varying vec3 vLightDirection;

// Beckman分布
float BechmannDistribution(float d, float m) {
    float d2 = d * d;
    float m2 = m * m;
    return exp((d2 - 1.0) / (d2 * m2)) / (m2 * d2 * d2);
}

// フレネル項
float Fresnel(float c, float f0) {
    float sf = sqrt(f0);
    float n = (1.0 + sf) / (1.0 - sf);
    float g = sqrt(n * n + c * c - 1.0);
    float ga = (c * (g + c) - 1.0) * (c * (g + c) - 1.0);
    float gb = (c * (g - c) + 1.0) * (c * (g - c) + 1.0);
    return (g - c) * (g - c) / (2.0 * (g + c) + (g + c)) * (1.0 + ga / gb);
}

void main() {
    vec3 l = normalize(vLightDirection);
    vec3 n = normalize(vNormal);

    vec3 v = normalize(vView);
    vec3 h = normalize(l + v);

    float hn = dot(h, n);
    float ln = dot(l, n);
    float lh = dot(l, h);
    float vn = dot(v, n);

    // Cook-Torrance
    vec3 f = vec3(Fresnel(lh, specular.x), Fresnel(lh, specular.y), Fresnel(lh, specular.z));
    float d = BechmannDistribution(hn, microfacet);
    float t = 2.0 * hn / dot(v, h);
    float g = min(1.0, min(t * vn, t * ln));
    float m = 3.14159265 * vn * ln;
    vec3 spe = max(f * d * g / m, 0.0);
    vec3 dif = max(ln, 0.0) * diffuse.xyz;
    vec3 amb = ambient.xyz;

    gl_FragColor = vec4(spe + dif + amb, 1.0);
 }
 

参考にした論文

http://inst.eecs.berkeley.edu/~cs283/sp13/lectures/cookpaper.pdf

式が何パターンか見つかることについての話が以下のところに書いてあるような

http://www.gamedev.net/topic/638197-cook-torrance-brdf-general/


フィードバック

  1. […] Cook-Torranceのシェーディングモデルでフレネルを式を使ったが、フレネルの式の近似としてSchlickの近似というものがある。実際にどのくらい正しいのかグラフを書いてみた。ちなみにCook-Torranceの論文にも似たような近似式が載っていた。 […]


コメントを残す

カテゴリー