屈折マッピング
今回のサンプルの実行結果
キューブ環境マッピングの応用
前回はキューブ環境マッピングとバンプマッピングの合わせ技、キューブ環境バンプマッピングを解説しました。
少ない頂点数で複雑な凹凸を表現できるバンプマッピングのテクニックと、世界の映り込みを表現できる環境マッピングとの合わせ技で、非常にリアリティのあるシーンを生成することができましたね。
今回はキューブマッピングを応用したテクニックの一つ、屈折マッピングを解説します。屈折マッピングを用いると、まるでガラスのように少し歪んだ風景の映り込みを表現できます。
屈折マッピングを適用してレンダリングした球体は、まるでビー玉(ガラス玉)のような質感を演出します。また、屈折マッピングでは屈折の度合いを変更することで、ガラスだけでなく透明なプラスチックのような質感を演出することも可能です。
キューブ環境マッピングと比較しても、ほんの少しの変更だけで実現できる屈折マッピング、是非習得していただければと思います。
反射と屈折
キューブマッピングは、モデルの法線と視線ベクトルとを用いてベクトルの反射を計算することで実現できるテクニックです。一方、屈折マッピングはその名が示す通りベクトルを屈折させることで実現できます。
現実世界の物理では、光が様々な効果によって屈折することで景色が歪んで見えます。しかし、3D プログラミングにおける屈折マッピングでは、あくまでもそれを擬似的に再現するだけにしか過ぎません。
キューブ環境マッピングでは、モデルの法線と視線ベクトルをシェーダの組み込み関数である reflect
に渡して反射ベクトルを計算しましたね。屈折マッピングの場合もシェーダのちからを借りることになりますが、通常のキューブ環境マッピングとは異なり refract
というビルトイン関数を使います。
この refract
は、既出の reflect
と非常に似ていて紛らわしいスペルなので間違えないように気をつけましょう。
それでは早速シェーダのソースを見ていきます。まずは頂点シェーダです。
頂点シェーダ
attribute vec3 position;
attribute vec3 normal;
attribute vec4 color;
uniform mat4 mMatrix;
uniform mat4 mvpMatrix;
varying vec3 vPosition;
varying vec3 vNormal;
varying vec4 vColor;
void main(void){
vPosition = (mMatrix * vec4(position, 1.0)).xyz;
vNormal = normalize((mMatrix * vec4(normal, 0.0)).xyz);
vColor = color;
gl_Position = mvpMatrix * vec4(position, 1.0);
}
これはキューブ環境マッピングのときの頂点シェーダとほとんど同じです。違うところがあるとすれば、フラグメントシェーダに渡す varying 変数の vNormal
を正規化しておくことくらいです。
屈折マッピングに用いる refract
には正規化されたベクトルを渡す必要があるためこのようになっています。
続いてはフラグメントシェーダ。
フラグメントシェーダ
precision mediump float;
uniform vec3 eyePosition;
uniform samplerCube cubeTexture;
uniform bool refraction;
uniform float eta;
varying vec3 vPosition;
varying vec3 vNormal;
varying vec4 vColor;
void main(void){
vec3 ref;
if(refraction){
ref = refract(normalize(vPosition - eyePosition), vNormal, eta);
}else{
ref = vNormal;
}
vec4 envColor = textureCube(cubeTexture, ref);
vec4 destColor = vColor * envColor;
gl_FragColor = destColor;
}
フラグメントシェーダも、やはりキューブ環境マッピングのときとほとんど同じです。
違うのは反射ベクトルを算出していた部分、ここが屈折ベクトルを算出する式に変わっています。
屈折ベクトルの算出
ref = refract(vPosition - eyePosition, vNormal, eta);
座標変換後の頂点からカメラの座標位置を引くことで視線ベクトルを算出し、その視線ベクトルと、先ほど頂点シェーダで正規化した法線とを refract
に渡しています。
また、ここで新たに定義した uniform 変数 eta
の値を使って屈折率を変化させています。
この eta
という名前の uniform 変数には屈折率の比を渡します。この値は HTML 内に input タグを設置して、そこからリアルタイムに値を拾うようにプログラムを組みます。今回のサンプルでは、この変数 eta
の値が 0 ~ 1.0 の間で変動するようになっています。
javascript プログラムの修正
先述の通り、今回のサンプルでは HTML 内のエレメントから値を取得できるようにプログラムを修正します。input タグの range タイプを使いますので、id 付きで HTML 内に含めておきます。
メインプログラムの恒常ループの中では、この input タグの値をリアルタイムに参照しシェーダに送る屈折率の比を変えています。当該の箇所が以下のコード。
屈折率の比を HTML から取得する
// エレメントから屈折率を取得
var refractiveIndex = eRange.value / 100;
変数 eRange
には事前に参照を取得しておきます。このエレメントの value
プロパティは 0 ~ 100 の範囲の値を取るようにしてあるので、結果的にシェーダに送られる数値は 0 ~ 1.0 の範囲に収まるわけですね。ちなみに、HTML のほうでは既定値としてエレメントの value
プロパティに 60 を設定していますので、シェーダには 0.6 が送られます。これはちょうどガラスのような質感になります。
それ以外の部分では、ほとんどがキューブ環境マッピングと同じですので注意深くソースを見ていけば大丈夫でしょう。
まとめ
さて、屈折マッピング、いかがでしたでしょうか。
プログラムとしては、本当にキューブ環境マッピングと大差ありませんので、コードを修正すること自体は非常に簡単だと思います。複雑なベクトルの反射や屈折を、シェーダのビルトイン関数が肩代わりしてくれますので非常に簡単に実装できるのがステキです。
サンプルでは屈折率の比をリアルタイムに変更することができますので、ぐりぐりと比率を変えながら、レンダリング結果がどのように変化するのか見てみると楽しいと思います。
サンプルへのリンクはいつものようにこの下にあります。なかなか面白いので、是非実際に動作させてみてください。