官方網站(Lighthouse3D)的教學淺顯易懂,它以OpenGL提供的茶壺來做toon shading,產生類似卡通的效果,可以知道shader如何運作。一般的OpenGL無法在打光之後再做色彩的變化,若以shader來著色,便可做出特殊效果。
vertex shader程式碼:
varying vec3 lightDir,normal;
void main()
{
lightDir = normalize(vec3(gl_LightSource[0].position));
normal = gl_NormalMatrix * gl_Normal;
gl_Position = ftransform();
}
fragment shader程式碼:
varying vec3 lightDir,normal;
void main()
{
float intensity;
vec4 color;
// normalizing the lights position to be on the safe side
vec3 n = normalize(normal);
intensity = dot(lightDir,n);
if (intensity > 0.95)
color = vec4(1.0,0.5,0.5,1.0);
else if (intensity > 0.5)
color = vec4(0.6,0.3,0.3,1.0);
else if (intensity > 0.25)
color = vec4(0.4,0.2,0.2,1.0);
else
color = vec4(0.2,0.1,0.1,1.0);
gl_FragColor = color;
}

intensity = dot(lightDir,normal);的normal沒有normalize,最亮區比正常版大很多,表示內積後intensity值大於0.95者眾多,往前推知,沒有normalize的normal之長度有大於1。

normal = gl_NormalMatrix * gl_Normal;去掉gl_NormalMatrix。光源會跟著物體跑,因為gl_NormalMatrix把gl_Normal變換到eye coordinate。
接下來就是利用計圖作業的光照牛來實現toon shading。準備model→cow.obj,將點v、法向量vn、面f讀取進程式裡,利用glBegin和glEnd繪畫多邊形,接著設定shader,最後跑出結果:
最後想強調的是,fragment shader裡若沒有將法向量正規化,也就是只做vec3 n = normal;而不做vec3 n = normalize(normal);,那麼顏色的過度區域就會不平滑。
基本上位在管線前頭的vertex shader取得的normal不需要正規化,只要後頭的fragment shader有正規化即可,為什麼?因為是在內插後所得到的值,接著只要在內積前把normal的長度正規化為1,內積後的值就會有平滑的表現,而就算vertex shader中的normal值沒有正規化,只要normal的方向正確即可。那假如我把內積搬往vertex shader來做呢?那在此正規化normal鐵定做白工,因為normal值根本還沒有內插。
如果還不理解原理的話,那麼無論normal在vertex或fragment shader中,都一起正規化,保證呈現的結果為正確:P
參考:









隨意留個言吧:)~