Just My Life & My Work

[GLSL] toon shading

官方網站(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;
}
glsl toon shading

正常版。

glsl toon shading

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

glsl toon shading

normal = gl_NormalMatrix * gl_Normal;去掉gl_NormalMatrix。光源會跟著物體跑,因為gl_NormalMatrix把gl_Normal變換到eye coordinate。

glsl toon shading

在OpenGL application中,將glLightfv(GL_LIGHT0, GL_POSITION, lpos);註解掉,這是在沒有光源條件下的成像。

接下來就是利用計圖作業的光照牛來實現toon shading。準備model→cow.obj,將點v、法向量vn、面f讀取進程式裡,利用glBegin和glEnd繪畫多邊形,接著設定shader,最後跑出結果:

glsl toon shadingglsl toon shadingglsl toon shadingglsl toon shading

最後想強調的是,fragment shader裡若沒有將法向量正規化,也就是只做vec3 n = normal;而不做vec3 n = normalize(normal);,那麼顏色的過度區域就會不平滑。

內插法向量

儘管兩個頂點已做正規化,讓normal長度等於1,然而內插後normal值的長度並不一定等於1。

只做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

參考:

隨意留個言吧:)~

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料

標籤雲