OpenGLを学ぼうと足掻く フォンシェーディングを実装
前回はシェーディングの計算を頂点シェーダで行うグーローシェーディングというものをやってみました(ADSシェーディングではないですが、前々回も)。
各頂点でシェーディングするということは色を各頂点にひとつ関連付けるということです。
この値をフラグメントシェーダに渡し、面全体で補間させて描画したわけです。
例えば光源がポリゴンのド真ん中の上にあるとき、一番明るく見えるべきなのはもちろんポリゴンの中央部ですが、グーローシェーディングを用いると、色は頂点でしか設定されないため、ポリゴンの内部はその情報で補間された値を用いることにるので、そうはならないのです。
ここでフォンシェーディングというのが出てきます。
これは前回示したシェーディングの計算をフラグメントシェーダで行う方法です。
頂点シェーダでは色(反射光)を設定せず、頂点の座標値と法線ベクトルを設定し、フラグメントシェーダではその補間値を用いてフラグメントごとに全回示したシェーディングの計算を行うわけです。
もちろんグーローシェーディングと比べて計算量は増えますが、より正確なシェーディングができるようになります。
頂点シェーダは以下のようにしました。
#version 120 attribute vec4 VertexPosition; attribute vec3 VertexNormal; uniform mat4 M, V, P; uniform mat3 NormalMatrix; varying vec3 Position; varying vec3 Normal; void main() { mat4 ModelViewMatrix = V * M; Normal = normalize(NormalMatrix * VertexNormal); Position = vec3(ModelViewMatrix * VertexPosition); mat4 MVP = P * V * M; gl_Position = MVP * VertexPosition; }
フラグメントシェーダは以下のようにしました。
#version 120 varying vec3 Position; varying vec3 Normal; void main() { vec4 LightPosition = vec4(-10.0, -10.0, -10.0, 1.0); vec3 Kd = vec3(0.6, 0.3, 0.2); vec3 Ka = vec3(0.4, 0.4, 0.2); vec3 Ks = vec3(1.0, 1.0, 1.0); vec3 LightIntensity = vec3(1.0, 1.0, 1.0); float Shininess = 1.0; vec3 n = normalize(Normal); vec3 s = normalize(vec3(LightPosition) - Position); vec3 v = normalize(vec3(-Position)); vec3 r = reflect(-s, n); gl_FragColor = vec4(LightIntensity * (Ka + Kd * max(dot(s,n), 0.0) + Ks * pow(max(dot(r,v), 0.0), Shininess)), 1.0 ); }
特にいうことはないですね。
光の反射率はAmbient成分,Diffuse成分,Specular成分全てvec3(1.0, 1.0, 1.0)に設定しています。
以下はグーローシェーディングを行った時の猿です。
以下はフォンシェーディングを行った時の猿です。
確かにグーローシェーディングに比べて、フォンシェーディングでは正確で、細かくシェーディングされているような気がします。