Simple Toon Shading

From GameStudio Wiki

Jump to: navigation, search

the entity should use a texture with solid colors only. no gradients, photorealistic stuff and so on should be used but maybe this would lead to some nice effects. :)

normalize+6.tga is this normalization cube map:

Image:Normalize69ks.png

toonlookup.tga is a 1d look-up table and should be similar to this example:

Image:toonlookup7yr.png

you can experiment with this one to achieve different shading styles.

the outline thickness can be set in the entity action, the outline color can be set as pixelshader constant. the entity will get lit from the sun direction. DirectX 9 version :

bmap bmp_normalizationcube=<normalize+6.tga>;
bmap bmp_toonlookup=<toonlookup.tga>; 

function mtl_toon_init()
{
	bmap_to_cubemap(mtl.skin1);
}

material mtl_toon
{
	skin1=bmp_normalizationcube;
	skin2=bmp_toonlookup;
	event=mtl_toon_init;
	effect
	"
	texture entSkin1;
	texture mtlSkin1;
	texture mtlSkin2;
	matrix matWorldViewProj;
	matrix matWorld;
	vector vecSkill41;
	vector vecSunDir; 

	technique toon
	{
		//-------------------------------paint
		pass p0
		{
			texture[0]=<mtlSkin1>;
			texture[1]=<mtlSkin2>;
			texture[2]=<entSkin1>; 

			addressU[1]=clamp; 

			magFilter[2]=linear;
			minFilter[2]=linear;
			mipFilter[2]=linear;

			vertexShaderConstant[0]=<matWorldViewProj>;
			vertexShaderConstant[4]=<matWorld>; 

			vertexShaderConstant[16]=<vecSunDir>; 

			vertexShader=
			asm
			{
				vs.1.1
 		                dcl_position v0
				dcl_normal v3
				dcl_texcoord0 v7
				dcl_texcoord1 v8

				m4x4 oPos,v0,c0	  // transform position to clip space
				m3x3 r0,v3,c4	  // transform normal to world space
				dp3 r0.w,r0,r0	  // renormalize it
				rsq r0.w,r0.w
				mul r0,r0,r0.w
				mov oT0.xyz,r0.xyz  // output normal to oT0
				mov oT1.xyz,-c16    // output light direction to oT1
				mov oT2.xy,v7.xy    // output uvs to oT2
			};
			pixelShader=
			asm
			{
				ps.1.3
				tex t0		    // fetch normalized normal
				texdp3tex t1,t0_bx2 // light.normal=u -> use u to lookup in t1 
                                                   // _bx2 -> 0..1 of normcube will be   -1..1 
	 			tex t2		    // sample color map
				mul r0,t2,t1	    // modulate with shading
			};
		}
		//--------------------------------------------ink
		pass p1
		{
			cullMode=cw;
			vertexShaderConstant[0]=<matWorldViewProj>;
			vertexShaderConstant[16]=<vecSkill41>; // outline_thickness, 0, 0, 0
			vertexShader=
			asm
			{
				vs.1.1
				dcl_position v0
				dcl_normal v3
				dcl_texcoord0 v7
				dcl_texcoord1 v8

				mov r0,v0
				mul r1,c16.x,v3		  // scale normal
				add r0.xyz,r0.xyz,r1.xyz  // shell offset 
                                                         //(vertex position + scaled normal)
				m4x4 oPos,r0,c0		  // transform position to clip space
			};
			pixelShader=
			asm
			{
				ps.1.3
				def c0,0,0,0,1		  // outline rgba
 				mov r0,c0
 			};
 		}
 	}
	";
}

action toon
{
	my.skill41=float(0.8); // outline_thickness
	my.material=mtl_toon;
}

DirectX 8 version :

bmap bmp_normalizationcube=<normalize+6.tga>;
bmap bmp_toonlookup=<toonlookup.tga>; 

function mtl_toon_init()
{
	bmap_to_cubemap(mtl.skin1);
}

material mtl_toon
{
	skin1=bmp_normalizationcube;
	skin2=bmp_toonlookup;
	event=mtl_toon_init;
	effect
	"
	texture entSkin1;
	texture mtlSkin1;
	texture mtlSkin2;
	matrix matWorldViewProj;
	matrix matWorld;
	vector vecSkill41;
	vector vecSunDir; 

	technique toon
	{
		//---------------------------------------------------------------------------paint
		pass p0
		{
			texture[0]=<mtlSkin1>;
			texture[1]=<mtlSkin2>;
			texture[2]=<entSkin1>; 

			addressU[1]=clamp; 

			// magFilter[1]=point;
			// minFilter[1]=point;
			// mipFilter[1]=point;
			magFilter[2]=linear;
			minFilter[2]=linear;
			mipFilter[2]=linear;

			vertexShaderConstant[0]=<matWorldViewProj>;
			vertexShaderConstant[4]=<matWorld>; 

			vertexShaderConstant[16]=<vecSunDir>; 

			vertexShader=
			decl
			{
				stream 0;
				float v0[3]; // position
				float v3[3]; // normal
				float v7[3]; // uv
			}
			asm
			{
				vs.1.1
				m4x4 oPos,v0,c0		// transform position to clip space
				m3x3 r0,v3,c4		// transform normal to world space
				dp3 r0.w,r0,r0		// renormalize it
				rsq r0.w,r0.w
				mul r0,r0,r0.w
				mov oT0.xyz,r0.xyz	// output normal to oT0
				mov oT1.xyz,-c16		// output light direction to oT1
				mov oT2.xy,v7.xy	// output uvs to oT2
			};
			pixelShader=
			asm
			{
				ps.1.3
				tex t0			// fetch normalized normal
				texdp3tex t1,t0_bx2	// light.normal=u -> use u to lookup in t1 // _bx2 -> 0..1 of normcube will be   -1..1 
	 			tex t2			// sample color map
				mul r0,t2,t1		// modulate with shading
			};
		}
		//---------------------------------------------------------------------------ink
		pass p1
		{
			cullMode=cw;
			vertexShaderConstant[0]=<matWorldViewProj>;
			vertexShaderConstant[16]=<vecSkill41>; // outline_thickness, 0, 0, 0
			vertexShader=
			decl
			{
				stream 0;
				float v0[3];
				float v3[3];
				float v7[3];
			}
			asm
			{
				vs.1.1
				mov r0,v0
				mul r1,c16.x,v3			// scale normal
				add r0.xyz,r0.xyz,r1.xyz	// shell offset (vertex position + scaled normal)
				m4x4 oPos,r0,c0			// transform position to clip space
			};
			pixelShader=
			asm
			{
				ps.1.3
				def c0,0,0,0,1			// outline rgba
 				mov r0,c0
 			};
 		}
 	}
	";
}

action toon
{
	my.skill41=float(0.8); // outline_thickness
	my.material=mtl_toon;
}
Personal tools