2010年4月15日木曜日

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

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

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

以下はホスト側のプログラム
1//距離減衰
2//kc: 一定減衰率 = 0
3//k1: 1次減衰率 = 0
4//k2: 2次減衰率 = 1.0 / (lightPos.y * lightPos.y)
5glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, kc);
6glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, k1);
7glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, k2);
んで、フラグメントシェーダ側の記述は以下のとおり
01varying vec3 P;
02varying vec3 N;
03 
04void main()
05{
06    //法線やライティング情報を受け取る
07    vec3 L = gl_LightSource[0].position.xyz - P;
08 
09    //ライトまでの距離
10    float d = length(L);
11 
12    //ライトへの向きベクトルを正規化する
13    L = normalize(L);
14 
15    //減衰係数
16    float attenuation = 1.0 / (gl_LightSource[0].constantAttenuation
17                       + gl_LightSource[0].linearAttenuation * d
18                       + gl_LightSource[0].quadraticAttenuation * d * d);
19 
20    vec3 N = normalize(N);
21 
22    //アンビエント確定
23    vec4 ambient = gl_FrontLightProduct[0].ambient;
24    float dotNL = dot(N, L);
25 
26    //ディフューズ確定
27    vec4 diffuse = gl_FrontLightProduct[0].diffuse * max(0.0, dotNL);
28 
29    vec3 V = normalize(-P);
30    vec3 H = normalize(L + V);
31    float powNH = pow(max(dot(N, H), 0.0), gl_FrontMaterial.shininess);
32 
33    if(dotNL <= 0.0)
34    {
35        powNH = 0.0;
36    }
37    //スペキュラ確定
38    vec4 specular = gl_FrontLightProduct[0].specular * powNH;
39 
40    //カラー値統合
41    gl_FragColor = ambient + diffuse + specular;
42 
43    //減衰係数を乗じる
44    gl_FragColor *= attenuation;
45}
まあほとんど教本のまんまなんですが・・・。
gl_LightSource[0].constantAttenuation
gl_LightSource[0].linearAttenuation
gl_LightSource[0].quadraticAttenuation
がそれぞれホスト側で設定した減衰係数です。

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

0 件のコメント:

コメントを投稿