フォンシェーディング - フラグメントシェーダでのライティング
注意:ここでかかれている内容は、本来のフォンシェーディングという用語に対して使い方が間違っているかもしれませんが、ここでは、フラグメントシェーダでのライティングをフォンシェーディングと呼ぶことにします。

サンプルは白の光源が物体のまわりをぐるぐる回ります。
環境
- OpenGL 3.3
- GLUT(freeglut) - 2.6.0
- GLEW - 1.5.7
- GLM - 0.9.0.6
フォンシェーディング(Phong shading)について
フォンシェーディングでは、ポリゴン表面の法線ベクトルを頂点の法線ベクトルから線形補間で求めます。(フォン補間)

グローシェーディングは頂点シェーダで処理していたのに対して、フォンシェーディングはフラグメントシェーダにより計算します。
ハードウェアによるフォン補間は、ラスタライザに処理された法線ベクトルをフラグメント(ピクセル)ごとの法線ベクトル解釈して計算します。
ハードウェアによるフォン補間は、ラスタライザに処理された法線ベクトルをフラグメント(ピクセル)ごとの法線ベクトル解釈して計算します。
照明モデル
照明モデルは、グローシェーディング時と同じ計算。
実装
- 頂点シェーダ
#version 330
uniform mat4 u_modelMatrix; // モデル・マトリックス
uniform mat4 u_viewMatrix; // ビュー・マトリックス
uniform mat4 u_projectionMatrix; // 射影・マトリックス
uniform vec3 u_lightPos; // 光源位置
in vec3 a_position;
in vec3 a_normal;
out vec3 v_P; // eye direction
out vec3 v_N; // normal direction
out vec3 v_L; // light direction
void main(void)
{
mat4 modelViewMatrix = u_viewMatrix * u_modelMatrix;
mat3 n_mat = mat3( transpose( inverse(modelViewMatrix) ) ); // normal Matrix
vec3 lightVec = vec3(u_viewMatrix * vec4(u_lightPos.xyz, 1.0));
v_P = vec3(modelViewMatrix * vec4(a_position,1.0));
v_N = normalize(n_mat * a_normal);
v_L = normalize(lightVec - v_P);
gl_Position = u_projectionMatrix * modelViewMatrix * vec4(a_position.xyz, 1.0);
}
- フラグメントシェーダ
#version 330
struct MaterialParam {
vec4 ambient;
vec4 diffuse;
vec4 specular;
float shininess;
};
uniform MaterialParam u_lightMaterial;
uniform MaterialParam u_material;
in vec3 v_P;
in vec3 v_N;
in vec3 v_L;
out vec4 fragColor;
void main(void)
{
vec3 V = normalize(-v_P);
vec3 N = normalize(v_N); // 正規化が必要
vec3 L = normalize(v_L);
vec4 ambient = u_lightMaterial.ambient * u_material.ambient;
float nDotL = dot(N,L);
float diffuseLight = max(nDotL, 0.0);
vec4 diffuse = u_lightMaterial.diffuse * u_material.diffuse * diffuseLight;
vec3 R = reflect(-L,N); // 反射ベクトルによるスペキュラー
float vDotR = pow( max(dot(V,R), 0.0), u_material.shininess );
if (nDotL < 0.0) vDotR = 0.0;
vec4 specular = u_lightMaterial.specular * u_material.specular * vDotR;
// vec3 H = normalize(L+V); // ハーフベクトルによるスペキュラー
// float nDotH = pow( max(dot(N,H), 0.0) , u_material.shininess );
// if (nDotL < 0.0) nDotH = 0.0;
// vec4 specular = u_lightMaterial.specular * u_material.specular * nDotH;
fragColor = ambient + diffuse + specular;
}
- メモ1
光源ベクトルLは、頂点シェーダで計算してからフラグメントシェーダに渡しています。
方法としては他に、Lをアプリケーション側で計算してuniformでフラグメントシェーダに直接渡す方法もあります。HW処理負荷を減らすならば後者を選択すべきでしょう。
方法としては他に、Lをアプリケーション側で計算してuniformでフラグメントシェーダに直接渡す方法もあります。HW処理負荷を減らすならば後者を選択すべきでしょう。
Blinn-Phongシェーディング
Blinn-Phongは物理的に根拠がないですが、経験的にPhongよりも正しいことことがあります。
計算は、反射ベクトルではなく、ハーフベクトルを使います。
シェーダでは、以下のように変更します。
計算は、反射ベクトルではなく、ハーフベクトルを使います。
シェーダでは、以下のように変更します。
vec3 R = reflect(-L,N); // 反射ベクトルによるスペキュラー float vDotR = pow( max(dot(V,R), 0.0), u_material.shininess ); if (nDotL < 0.0) vDotR = 0.0; vec4 specular = u_lightMaterial.specular * u_material.specular * vDotR;
↓
vec3 H = normalize(L+V); // ハーフベクトルによるスペキュラー float nDotH = pow( max(dot(N,H), 0.0) , u_material.shininess ); if (nDotL < 0.0) nDotH = 0.0; vec4 specular = u_lightMaterial.specular * u_material.specular * nDotH;
ダウンロード
