GLSLで異方性反射もどきを作って見たよ

やあ子どもたち。GLSLの練習課題として今日はGLSLで異方性反射もどきを作って見たので紹介しておくぞ。こんなやつだよ。

まるでキューブの各面の中心を中心として何かこう円形に磨き上げた金属のような光沢の感じがでていないか。iPadなんかでもこんなのを各ピクセルあたりに計算してるのかと思うともうこれは本当にすごい時代になってきたなと思ってしまうわけなんだけども。
頂点シェーダとフラグメントシェーダは以下のようになってるよ。まずは頂点シェーダだ。

attribute vec4 position;
attribute vec3 normal;
varying vec4 tako_pos;
varying vec3 tako_nor;
varying vec4 transfd_pos;
uniform mat4 modelViewProjMatrix;
uniform mat3 normalMatrix;

void main()
{
 // フラグメントシェーダへの出力
    transfd_pos = modelViewProjMatrix * position;
    tako_pos = position;
    tako_nor = normal;
 // 頂点シェーダとしての出力
    gl_Position = transfd_pos;
}

そしてフラグメントシェーダは以下のようだよ。今回はモデルの形状に特異的なコードなので美しくないし汎用性もないけどな。まAxisAlignedなキューブだけには使えるってわけよ。

varying lowp vec4 tako_pos;
varying lowp vec3 tako_nor;
varying lowp vec4 transfd_pos;
uniform highp mat3 normalMatrix;

void main()
{
    lowp float x = tako_pos.x;
    lowp float y = tako_pos.y;
    lowp float z = tako_pos.z;
    // 光源方向の計算
    lowp vec3 light_dir;
    {
      lowp vec3 light_position = vec3(0.0, 2.0, 2.0);
      light_dir = normalize( transfd_pos.xyz - light_position );
    }
 // 異方性反射もどきの計算
    gl_FragColor.rgb = vec3( .5, .4, .0 );
 // yz面の場合
    if( tako_nor.x != 0.0 )
    {
        lowp vec3 tangent = normalize( normalMatrix * vec3( .0, y, z ) );
        lowp float nDotVP = abs( dot( tangent, light_dir ) );
        nDotVP = nDotVP * nDotVP * nDotVP;
        gl_FragColor.rg += vec2( nDotVP, nDotVP );
    }
 // xz面の場合
    else if( tako_nor.y != 0.0 )
    {
        lowp vec3 tangent = normalize( normalMatrix * vec3( x, .0, z ) );
        lowp float nDotVP = abs( dot( tangent, light_dir ) );
        nDotVP = nDotVP * nDotVP * nDotVP;
        gl_FragColor.rg += vec2( nDotVP, nDotVP );
    }
 // xy面の場合
    else if( tako_nor.z != 0.0 )
    {
        lowp vec3 tangent = normalize( normalMatrix * vec3( x, y, .0 ) );
        lowp float nDotVP = abs( dot( tangent, light_dir ) );
        nDotVP = nDotVP * nDotVP * nDotVP;
        gl_FragColor.rg += vec2( nDotVP, nDotVP );
    }
    return;
}

今回は各面の方向別に処理を分けて書いてみた。どの方向の面なのかは変換前のモデル定義空間での法線の方向で判定させているよ。面上の点の、その面の中心に対する接線方向(もちろんこっちはモデル系ではなくてワールド系に変換済のもの)と、光源の方向とで色強度を決めています。ただの内積だよ簡単だろう?コードの中では実際には接線方向ではなく、中心に向かう向きにしているけど、光沢模様の位相が変わるだけで物体回転への応答は同じなのでそうなってるよ。本当に接線方向にしたいのなら、tangentを、今は( x, y, 0 ) としているところを( -y, x, 0 ) とでもすればいいだけだ。
異方性反射というくらいだから反射なんだね本当は。でも今回は別に光源からの反射を計算してるわけじゃないので(レイトレじゃないんだからな)、上記で求めた色強度の指向性を上げる、つまり特定の方向だけ極端に明るしたいので、何回かべき乗計算させているよ。これは手早くスペキュラーを出したい時によく使う手だね。このべき乗計算をしないとせっかく異方性計算した色強度がのっぺりとのっかるだけで全然光沢って感じがしなくなってしまうのでとても大事なスパイスなんだ。覚えておくといいぞ。
次回かどうかはわからんけども任意のテクスチャ模様を使って同じようなことをやりたいな。それはもう画像処理なんかも入ってくるテーマだから楽しそうだぞ。エッジ抽出してその方向をいろ情報に落としたテクスチャを用意したりしてなっ!なんてなっ!じゃなっ!