2010年4月15日木曜日

OpenGL+GLSLでポイントライトと距離減衰

Check
距離によって強さが変わるライトの実装に成功した。

ある地点における光の強さは、光源までの距離を d 、一定減衰率を kc 、1時減衰率を k1 、2時減衰率を k2 とすると以下の式で表すことができます。
減衰率kc, k1, k2はそれぞれGL側(ホストプログラム)からシェーダ側に送ることができる。

以下はホスト側のプログラム
//距離減衰
//kc: 一定減衰率 = 0
//k1: 1次減衰率 = 0
//k2: 2次減衰率 = 1.0 / (lightPos.y * lightPos.y)
glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, kc);
glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, k1);
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, k2);
んで、フラグメントシェーダ側の記述は以下のとおり
varying vec3 P;
varying vec3 N;

void main()
{
    //法線やライティング情報を受け取る
    vec3 L = gl_LightSource[0].position.xyz - P;

    //ライトまでの距離
    float d = length(L);

    //ライトへの向きベクトルを正規化する
    L = normalize(L);

    //減衰係数
    float attenuation = 1.0 / (gl_LightSource[0].constantAttenuation
                       + gl_LightSource[0].linearAttenuation * d
                       + gl_LightSource[0].quadraticAttenuation * d * d);

    vec3 N = normalize(N);

    //アンビエント確定
    vec4 ambient = gl_FrontLightProduct[0].ambient;
    float dotNL = dot(N, L);

    //ディフューズ確定
    vec4 diffuse = gl_FrontLightProduct[0].diffuse * max(0.0, dotNL);

    vec3 V = normalize(-P);
    vec3 H = normalize(L + V);
    float powNH = pow(max(dot(N, H), 0.0), gl_FrontMaterial.shininess);

    if(dotNL <= 0.0)
    {
        powNH = 0.0;
    }
    //スペキュラ確定
    vec4 specular = gl_FrontLightProduct[0].specular * powNH;

    //カラー値統合
    gl_FragColor = ambient + diffuse + specular;

    //減衰係数を乗じる
    gl_FragColor *= attenuation;
}
まあほとんど教本のまんまなんですが・・・。
gl_LightSource[0].constantAttenuation
gl_LightSource[0].linearAttenuation
gl_LightSource[0].quadraticAttenuation
がそれぞれホスト側で設定した減衰係数です。

画像の式に当たるのがコメントで減衰係数と書いてあるところ。んで、最後にカラー値を統合したgl_FragColorに減衰係数をかけて明るさを確定してるわけですね。このあたりはレイトレースでやったことがかなり活きてるかんじ。

0 件のコメント:

コメントを投稿