GLSLを使い環境マップ(キューブマップ)と反射・屈折効果を実装.



キューブマップとフレネル効果(Fresnel reflection)について

環境マップ

鏡面反射表面上への写り込みや透過物体の表現はレンダリング結果のリアルさを向上させるのにとても重要である. しかし,これらの効果を正確にシミュレートする場合,レイトレーシングなどの時間のかかる方法が必要となる. そこで写り込みや屈折に影響するのは遠景だけであるとし, 環境に配置した遠景を表す画像を使ってこれらの効果を表現する. この方法は環境マップと呼ばれ,画像を球形状に貼り付けるスフィアマップ, 立方体形状に貼り付けるキューブマップの2つが一般的である. キューブマップに用いる画像の例を以下に示す.

cube_map.jpg

OpenGLにはキューブマップを取り扱うためのテクスチャタイプ

GL_TEXTURE_CUBE_MAP

があり,以下のように設定することでキューブマップ用のテクスチャ座標を自動生成することができる.

glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);

これらのテクスチャ座標は法線ベクトルに基づいて計算された反射ベクトルから生成される.

屈折の効果を入れたいので以下ではGLSLを使って反射,屈折ベクトルを計算, キューブマップテクスチャを参照することで反射・屈折効果を再現する方法を述べる. ただし,一次反射,屈折のみで複数回の反射などは考慮しない.

反射方向ベクトルの算出

GLSLによるフォンシェーディングで説明したように,反射ベクトルは以下のように求められる.

fresnel.eq1.gif

反射ベクトルを計算するシェーダコードを以下に示す.

  1
  2
  3
  4
  5
  6
vec3 Reflection(vec3 I, vec3 N)
{
    float cos_theta = dot(-I, N);
    vec3 R = 2*N*cos_theta+I;
    return (cos_theta > 0 ? normalize(R) : vec3(0.0));
}

また,GLSLには反射ベクトルを計算するビルトイン関数 reflect() があるのでこれを使って,

vec3 R = reflect(I, N);

としても良い.

透過屈折方向ベクトルの算出

スネルの法則(snell's law)より,入射角fresnel.eq2.gifと屈折角fresnel.eq3.gifは,

fresnel.eq4.gif

の関係がある. ここでfresnel.eq5.gifは境界面をはさむ両物体の屈折率である.

refraction_model.jpg

上の図において,入射ベクトルfresnel.eq6.gif(fresnel.eq7.gif), 屈折ベクトルfresnel.eq8.gif, 法線ベクトルfresnel.eq9.gif は単位ベクトルとする. fresnel.eq10.gif, fresnel.eq11.gifの長さを持つベクトルfresnel.eq12.gif,fresnel.eq13.gifは以下となる.

fresnel.eq14.gif

よって,

fresnel.eq15.gif
fresnel.eq16.gif

ここでfresnel.eq12.giffresnel.eq13.gifの両ベクトルの方向が同じであることから,

fresnel.eq17.gif

よって,

fresnel.eq18.gif

また,スネルの法則より,

fresnel.eq19.gif

この式の両辺にfresnel.eq12.gifをかけて,上式を代入すると,

fresnel.eq20.gif

fresnel.eq8.gifについて解くと,

fresnel.eq21.gif

ここで,

fresnel.eq22.gif

fresnel.eq23.giffresnel.eq24.gifで求められる.

屈折ベクトルを計算するシェーダコードを以下に示す.

  1
  2
  3
  4
  5
  6
  7
vec3 Refraction(vec3 I, vec3 N, float e)
{
    float cos_theta = dot(-I, N);
    float cos_phi2 = 1.0-e*e*(1.0-cos_theta*cos_theta);
    vec3 T = e*(I+N*cos_theta)-N*sqrt(abs(cos_phi2));
    return (cos_phi2 > 0 ? normalize(T) : vec3(0.0));
}

ここで,引数eには屈折率比を指定する. また,GLSLには屈折ベクトルを計算するビルトイン関数 refract() があるのでこれを使って,

vec3 T = refract(I, N, e);

としても良い.

フレネル効果

フレネル効果とは,透明な物体表面においてみる角度によって反射や屈折が起こる現象のことである. 入射光強度fresnel.eq6.gifのうち,fresnel.eq25.gifが反射,fresnel.eq26.gifが透過屈折するとする. ここで,fresnel.eq27.gif,fresnel.eq28.gifは鏡面反射係数,透過係数である. 表面での損失がなければfresnel.eq29.gifである.

視点に届く光に関して考えると, 表面上の点に反射方向から届く光の強度をfresnel.eq30.gif, 屈折方向からの強度をfresnel.eq31.gifとすると, 視点に届く色はfresnel.eq32.gifとなる. このときの鏡面反射係数はフレネルの公式より,

fresnel.eq33.gif

計算を簡単にするためとユーザによる制御を容易にするために (1)で提案されている近似式を用いる.

fresnel.eq34.gif

ここでbiasは値が小さいと反射成分が弱くなりほとんど屈折し,1に近いとほぼ反射するようになる. 上記の反射・屈折ベクトルの算出方法とフレネル効果の近似式をGLSLで行うコードを以下に示す. まず,頂点シェーダは,

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
// フラグメントシェーダに値を渡すための変数
varying vec4 vPos;
varying vec3 vNrm;
 
void main(void)
{
    // フラグメントシェーダでの計算用(モデルビュー変換のみ)
    vPos = gl_Vertex;            // 頂点位置
    vNrm = normalize(gl_NormalMatrix*gl_Normal);    // 頂点法線
 
    // 描画頂点位置
    gl_Position = ftransform();
}

フラグメントシェーダにおける視点位置eyePositionがローカル座標で与えられることを想定して, vPosにはローカル座標の値をそのまま用いている. 頂点シェーダはGLSLによるフォンシェーディングのものとおなじである.

次にフラグメントシェーダを以下に示す.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
// バーテックスシェーダから受け取る変数
varying vec4 vPos;
varying vec3 vNrm;
 
// GLから設定される定数(uniform)
uniform float fresnelBias;
uniform float fresnelScale; 
uniform float fresnelPower; 
uniform float etaRatio;
uniform vec3 eyePosition;
uniform samplerCube envmap;
 
vec4 lerp(vec4 a, vec4 b, float s)
{
    return vec4(a+(b-a)*s);       
}
 
/*!
 * Fresnel反射モデルによるシェーディング
 * @return 表面反射色
 */
vec4 FresnelShading(void)
{
    // 入射,反射,屈折ベクトルの計算
    vec3 N = normalize(vNrm);            // 法線ベクトル
    vec3 I = normalize(vPos.xyz-eyePosition);    // 入射ベクトル
    //vec3 R = reflect(I, N);            // 反射ベクトル
    vec3 R = Reflection(I, N);            // 反射ベクトル
    //vec3 T = refract(I, N, etaRatio);    // 屈折ベクトル
    vec3 T = Refraction(I, N, etaRatio);    // 屈折ベクトル
 
    // 反射因数の計算
    float fresnel = fresnelBias+fresnelScale*pow(min(0.0, 1.0-dot(I, N)), fresnelPower);
 
    // 反射環境色の取得
    vec4 reflecColor = textureCube(envmap, R);
    reflecColor.a = 1.0;
 
    // 屈折環境色の計算
    vec4 refracColor;
    refracColor.rgb = textureCube(envmap, T).rgb;
    refracColor.a = 1.0;
 
    // 色を統合
    vec4 cout = lerp(refracColor, reflecColor, fresnel);
    cout.a = fresnel*0.5+0.5;
 
    return cout;
}

OpenGL側から各パラメータと視点位置, GL_TEXTURE_CUBE_MAPとして生成したキューブマップテクスチャを指定する.

実行結果

実行結果のスクリーンショットを以下に示す(クリックで拡大).

fresnel_result1.jpgfresnel_result2.jpg
etaRatio=0.8,bias=0.3etaRatio=0.8,bias=1.0

ソースコード

Visual Studio 2010用のソースコードを以下に置く(要GLUT,GLEW,libpng,libjpeg,zlib).

  • version1.1 : 視点位置と頂点位置の整合性がとれていなかったのを修正

(1)  R. Fernando and M. J. Kilgard, The Cg Tutorial: The Definitive Guide to Programmable Real-Time Graphics, Addison-Wesley Professional , 2003.

添付ファイル: filerefraction_model.jpg 1354件 [詳細] fileglsl_fresnel_v1.1.zip 1224件 [詳細] filecube_map.jpg 2042件 [詳細] filefresnel.eq12.gif 557件 [詳細] filefresnel.eq27.gif 595件 [詳細] filefresnel.eq15.gif 563件 [詳細] filefresnel.eq7.gif 548件 [詳細] filefresnel.eq2.gif 620件 [詳細] filefresnel.eq18.gif 579件 [詳細] filefresnel.eq25.gif 513件 [詳細] filefresnel.eq32.gif 574件 [詳細] filefresnel.eq19.gif 533件 [詳細] filefresnel.eq29.gif 496件 [詳細] filefresnel.eq3.gif 585件 [詳細] filefresnel.eq6.gif 560件 [詳細] filefresnel.eq9.gif 534件 [詳細] filefresnel.eq31.gif 587件 [詳細] filefresnel.eq26.gif 565件 [詳細] filefresnel.eq10.gif 588件 [詳細] filefresnel.eq21.gif 571件 [詳細] filefresnel.eq5.gif 570件 [詳細] filefresnel.eq8.gif 506件 [詳細] filefresnel.eq11.gif 586件 [詳細] filefresnel.eq33.gif 531件 [詳細] filefresnel.eq24.gif 566件 [詳細] filefresnel.eq23.gif 592件 [詳細] filefresnel.eq28.gif 590件 [詳細] filefresnel.eq4.gif 532件 [詳細] filefresnel.eq34.gif 489件 [詳細] filefresnel.eq17.gif 583件 [詳細] filefresnel.eq1.gif 568件 [詳細] filefresnel.eq30.gif 517件 [詳細] filefresnel.eq14.gif 589件 [詳細] filefresnel.eq20.gif 540件 [詳細] filefresnel.eq16.gif 530件 [詳細] filefresnel.eq22.gif 581件 [詳細] filefresnel.eq13.gif 569件 [詳細] fileglsl_fresnel.zip 636件 [詳細] filefresnel_result2.jpg 1583件 [詳細] filefresnel_result1.jpg 1531件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2014-05-22 (木) 11:44:38 (2270d)