GLSLを用いてフォンシェーディングを実装する.ここではまず,反射モデルとシェーディング方法に分けてそれぞれ説明する.
反射モデル†
反射モデルは光の物体表面における反射をモデル化したものであり,
ここでは,反射を環境光,拡散反射,鏡面反射に分けて考えるPhong反射モデルを用いる.
k_e,k_a,k_dはそれぞれ物体表面の放射,環境,拡散反射色であり,l_a,l_dは光源色である.
I_s は鏡面反射項であり,以下で説明する鏡面反射モデルにより計算する.
Phong 鏡面反射モデル*1, *2†
k_s は物体の鏡面反射色,m は鏡面反射指数でハイライトの強さを制御する.l_s は光源の鏡面反射色である.
また,R は以下で計算される.
Blinn-Phong 鏡面反射モデル†
Blinn により,R をハーフベクトルH に置き換えるモデルが提案されている.
Blinn 鏡面反射モデル*3†
Torrance-Sparrow モデルを基にして,反射面を微少面(micro facet) の集合と捉えるのが,Blinn モデルである.
D,G,Fそれぞれの項は,
- D : 微少面分布関数であり,法線がH であるmicro facet の割合を示す.
- Phongモデル :

- Torrance-Sparrowモデル :

- TrowbridgeとReitzのモデル :

が用いられる.α はNとHのなす角(
),
c_2 は分布の標準偏差,c_3 は楕円の偏心度で0ならハイライトがとても強い表面に,1 なら拡散面に近くなる.
- G : 微少面に入射する光,もしくは,微少面から反射する光は近傍の微少面による凸凹により遮蔽される.
これにより,鏡面反射が暗くなる現象を表すための項である.
1 は光が遮断されない場合,G1 は反射項の一部が遮断される場合,G2 は入射項の一部が遮断される場合である.
- F : フレネル項であり,θ1 を入射角,θ2 は透過角とすると,
鏡面反射に関わる微少面の法線ベクトルはHとなっていることから,
となる.
これにより,上式を整理していくと以下のようになる.
ここで,
は屈折率である.
Cook-Torrance 鏡面反射モデル*4†
Blinn モデルを改良して,微少面分布関数D にBeckmann 分布関数を用いたものである.
グーローとフォンシェーディング†
反射モデルに基づき,画面にCGモデルを描画する際にどのように各ピクセルの色を決定するのかが問題となる.
CG分野ではグーローシェーディングとフォンシェーディングの2種類が用いられている.
グーローシェーディングはまず,頂点ごとに上記の反射モデルに基づき色を決定し,
その後,ピクセルごとの処理において頂点色を線形補間することでレンダリングを行う.
一方,フォンシェーディングはピクセルごとに色を計算する方法である.
OpenGLの描画ではグーローシェーディングが採用されている.
そのため,フォンシェーディングを行いたい場合,GLSLやNVIDIA Cgのようなシェーディング言語を用いて実装しなければならない.
GLSLによるフォンシェーディング†
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
| |
#version 120
varying vec3 vPos;
varying vec3 vNrm;
void main(void)
{
vPos = (gl_ModelViewMatrix*gl_Vertex).xyz;
vNrm = gl_NormalMatrix*gl_Normal;
gl_Position = ftransform();
}
|
フラグメントシェーダでは,Blinn-Phongの鏡面反射モデルを用いてPhongシェーディングを行う.
以下にコード例を示す.コード例では上記の式との対応関係が分かりやすくなるように記述しているが,
実際には,表面の色とライトの色の掛け算などは gl_FrontLightProduct[0] で取得することもできる.
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
50
51
52
53
54
55
56
57
58
59
60
61
62
| |
#version 120
varying vec3 vPos;
varying vec3 vNrm;
void main(void)
{
vec3 La = gl_LightSource[0].ambient.xyz; vec3 Ld = gl_LightSource[0].diffuse.xyz; vec3 Ls = gl_LightSource[0].specular.xyz; vec3 Lp = gl_LightSource[0].position.xyz;
vec3 Ke = gl_FrontMaterial.emission.xyz; vec3 Ka = gl_FrontMaterial.ambient.xyz; vec3 Kd = gl_FrontMaterial.diffuse.xyz; vec3 Ks = gl_FrontMaterial.specular.xyz; float shine = gl_FrontMaterial.shininess;
vec3 V = normalize(-vPos.xyz); vec3 N = normalize(vNrm); vec3 L = normalize(Lp-vPos.xyz);
vec3 emissive = Ke;
vec3 ambient = Ka*La;
float diffuseLight = max(dot(L, N), 0.0);
vec3 diffuse = Kd*Ld*diffuseLight;
vec3 specular = vec3(0.0);
if(diffuseLight > 0.0){
vec3 H = normalize(L+V);
float specularLight = pow(max(dot(H, N), 0.0), shine);
specular = Ks*Ls*specularLight;
}
gl_FragColor.xyz = emissive+ambient+diffuse+specular;
gl_FragColor.w = 1.0;
}
|
以下のコード例はCook-Torrance鏡面反射モデルに基づくフラグメントプログラムである.
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
| |
#version 120
uniform float m;
uniform float refrac;
varying vec3 vPos;
varying vec3 vNrm;
void main(void)
{
vec3 La = gl_LightSource[0].ambient.xyz; vec3 Ld = gl_LightSource[0].diffuse.xyz; vec3 Ls = gl_LightSource[0].specular.xyz; vec3 Lp = gl_LightSource[0].position.xyz;
vec3 Ke = gl_FrontMaterial.emission.xyz; vec3 Ka = gl_FrontMaterial.ambient.xyz; vec3 Kd = gl_FrontMaterial.diffuse.xyz; vec3 Ks = gl_FrontMaterial.specular.xyz; float shine = gl_FrontMaterial.shininess;
vec3 V = normalize(-vPos.xyz); vec3 N = normalize(vNrm); vec3 L = normalize(Lp-vPos.xyz); vec3 H = normalize(L+V);
vec3 emissive = Ke;
vec3 ambient = Ka*La;
float diffuseLight = max(dot(L, N), 0.0);
vec3 diffuse = Kd*Ld*diffuseLight;
vec3 specular = vec3(0.0);
if(diffuseLight > 0.0){
float NH = dot(N, H);
float VH = dot(V, H);
float NV = dot(N, V);
float NL = dot(N, L);
float alpha = acos(NH);
float D = (1.0/(4*m*m*NH*NH*NH*NH))*exp((NH*NH-1)/(m*m*NH*NH));
float G = min(1, min((2*NH*NV)/VH, (2*NH*NL)/VH));
float c = VH;
float g = sqrt(refrac*refrac+c*c-1);
float F = ((g-c)*(g-c)/((g+c)*(g+c)))*(1+(c*(g+c)-1)*(c*(g+c)-1)/((c*(g-c)-1)*(c*(g-c)-1)));
float specularLight = D*G*F/NV;
specular = Ks*Ls*specularLight;
}
gl_FragColor.xyz = emissive+ambient+diffuse+specular;
gl_FragColor.w = 1.0;
}
|
レンダリング結果†
左からグーローシェーディング,フォンシェーディング(Phong反射モデル),フォンシェーディング(Cook-Torrance反射モデル)の結果である.

ソースコード†
Visual Studio 2010用のソースコードを以下に置く.