Toon shading (Cel shading) Version 1.0

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
tenko
Posts: 2
Joined: Wed Nov 19, 2008 12:30 pm

Toon shading (Cel shading) Version 1.0

Post by tenko »

I thought about Toon shading.
Please give the evaluation to me.

DirectX 9 and HLSL only.
Linked Irrlicht1.4.2 and win32.
The model must make outline.

Development Environment.
Windows XP SP2
DirectX 9.0c
NVIDIA GeForce 8600 GT

Global variable.

Code: Select all

//*******************************************
//	Global variable.
//*******************************************
IrrlichtDevice* g_device = 0;
Shader call back class.

Code: Select all

//*******************************************
//	Shader call back class.
//*******************************************
class MyShaderCallBack : public IShaderConstantSetCallBack
{
private:
	
	IVideoDriver* m_driver;
	
	matrix4 mtxW;
	matrix4 mtxWVP;
	
	// Color of light.
	SColorf m_colLitDef;
	SColorf m_colLitAmb;

	// Vector of light.
	vector3df m_vectLitVec;
	
	// U position of shader texture.
	f32	m_fShadowU;
	
public:

	MyShaderCallBack()
	{
		m_colLitDef = SColor(255,255,255,255);
		m_colLitAmb = SColor(0,0,0,0);
		
		m_vectLitVec = vector3df( 100.0f, 0.0f, 0.0f );
		
		m_fShadowU = 0.25;
	};
	
	// Set U position of shader texture.
	void	SetMaterialU( s32 f_nMax, s32 f_nNow )
	{
		f32 oneU = ( 1.0f / (f32)f_nMax );
		f32 centerU = oneU / 2.0f;
		
		m_fShadowU = centerU + oneU * (f32)f_nNow;
	}

	virtual void OnSetConstants(IMaterialRendererServices* services, s32 userData)
	{
		m_driver = services->getVideoDriver();

		// World,View,Projection matrix to HLSL.
		mtxWVP = m_driver->getTransform(ETS_PROJECTION);
		mtxWVP *= m_driver->getTransform(ETS_VIEW);
		mtxWVP *= mtxW = m_driver->getTransform(ETS_WORLD);
		
		services->setVertexShaderConstant("MatrixWVP", mtxWVP.pointer(), 16);
		
		services->setVertexShaderConstant("MatrixW", mtxW.pointer(), 16);
		

		// Color of light to HLSL.
		services->setVertexShaderConstant("LightDiffuseColor", (f32*)(&m_colLitDef), 4);
		services->setVertexShaderConstant("LightAmbientColor", (f32*)(&m_colLitAmb), 4);

		// Vector of light to HLSL.
		services->setVertexShaderConstant("LightDirection", (f32*)(&m_vectLitVec), 3);

		// U position of shader texture to HLSL.
		services->setVertexShaderConstant("ShadowU", (f32*)(&m_fShadowU), 1);

//		f32 var0=0;
//		services->setPixelShaderConstant("MeshTexture", &var0, 1);

//		f32 var1=1;
//		services->setPixelShaderConstant("ShadeTexture", &var1, 1);

	}
};
Entry point and main loop.

Code: Select all

int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow )
{
	// Create Irrlicht device.
	SIrrlichtCreationParameters param;

	param.DriverType = EDT_DIRECT3D9;
	param.WindowSize.Width = 1024;
	param.WindowSize.Height = 768;
	param.AntiAlias = false;
	param.Vsync = false;
	param.Fullscreen = false;
	param.Bits = 32;

	g_device = createDeviceEx(param);
	if (g_device == 0)
		return 1;

	g_device->setWindowCaption(L"Toon Shading by Tenko - Irrlicht Engine Demo");

	IVideoDriver* driver = g_device->getVideoDriver();
	ISceneManager* smgr = g_device->getSceneManager();
	IGUIEnvironment* env = g_device->getGUIEnvironment();

	driver->setTextureCreationFlag( ETCF_ALWAYS_32_BIT, true );

	c8* vsFileName = 0; // Vertex shader file name.
	c8* psFileName = 0; // Pixel shader file name.

	psFileName = "./media/hlsl.fx";
	vsFileName = psFileName; // Copy file name.

	//================================
	// Check shader version.
	//================================
	if (!driver->queryFeature(EVDF_PIXEL_SHADER_1_1) &&
		!driver->queryFeature(EVDF_ARB_FRAGMENT_PROGRAM_1))
	{
		g_device->getLogger()->log("WARNING: Pixel shaders disabled "\
			"because of missing driver/hardware support.");
		psFileName = 0;
	}
	
	if (!driver->queryFeature(EVDF_VERTEX_SHADER_1_1) &&
		!driver->queryFeature(EVDF_ARB_VERTEX_PROGRAM_1))
	{
		g_device->getLogger()->log("WARNING: Vertex shaders disabled "\
			"because of missing driver/hardware support.");
		vsFileName = 0;
	}

	// Load mesh.
	IAnimatedMesh* g_mesh = g_device->getSceneManager()->getMesh( "media/test.x" );

	// Add mesh scene node.
	IAnimatedMeshSceneNode* g_Model = smgr->addAnimatedMeshSceneNode(g_mesh);
	g_Model->setMaterialFlag(EMF_LIGHTING, false);

	// Load toon shader texture.
	ITexture* pShadeTexture = driver->getTexture( "media/shade.bmp" );

	// Set shader texture to mesh.
	g_Model->setMaterialTexture( 1, pShadeTexture );
	
	IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices();

	for( u32 nMat=0; nMat<g_Model->getMaterialCount(); nMat++ )
	{
		// Create shader material.
		MyShaderCallBack* mc = new MyShaderCallBack();
		
		mc->SetMaterialU( g_Model->getMaterialCount(), nMat );

		// Create shader material from HLSL.
		s32 newMaterialType = gpu->addHighLevelShaderMaterialFromFiles(
		psFileName, "VS_Shade", EVST_VS_1_1,
		psFileName, "PS_Shade", EPST_PS_1_1,
		mc, EMT_SOLID, nMat);

		mc->drop();
		
		// Set shader material.
		g_Model->getMaterial(nMat).MaterialType = ((E_MATERIAL_TYPE)newMaterialType);	
	
		// Set texture mapping mode to MIRROR.
		g_Model->getMaterial(nMat).TextureLayer[1].TextureWrap = ETC_MIRROR;
	}
	
	// Set mesh animation parameter.
	g_Model->setAnimationSpeed(30);
	g_Model->setFrameLoop(0, 3000);

	// Set mesh scale.
	g_Model->setScale( vector3df(1.0f,1.0f,1.0f) );
	g_Model->setPosition( vector3df(0.0f,0.0f,0.0f) );

	// Add rotation animator to mesh.
	ISceneNodeAnimator* anim2 = smgr->createRotationAnimator(vector3df(0.0f,0.1f,0.0f));
	g_Model->addAnimator(anim2);
	anim2->drop();

//	g_Model->setDebugDataVisible( EDS_MESH_WIRE_OVERLAY );

	// Add camera scene node.
	ICameraSceneNode* cam = smgr->addCameraSceneNodeMaya( NULL, -300, 20, 15 );
	cam->setPosition(vector3df(0,0,-4));
	cam->setTarget(vector3df(0,0,0));
	cam->setFarValue( 2000.0f );
	cam->setNearValue( 0.01f );
	
	//================================
	// Draw.
	//================================

	int lastFPS = -1;

	while(g_device->run())
	{
		driver->beginScene(true, true, SColor(255,100,100,100));

		smgr->drawAll();

		driver->endScene();

		int fps = driver->getFPS();

		if (lastFPS != fps)
		{
			stringw str = L"Toon Shading by Tenko - Irrlicht Engine Demo [";
			str += driver->getName();
			str += "] FPS:";
			str += fps;

			g_device->setWindowCaption( str.c_str() );
			lastFPS = fps;
		}
	}

	g_device->drop();
	
	return 0;
}
HLSL file.

Code: Select all

float4x4 MatrixW;
float4x4 MatrixWVP;

float ShadowU;

float4 LightDiffuseColor;
float4 LightAmbientColor;
float3 LightDirection;

sampler MeshTextureSampler;
sampler ShadeTextureSampler;

//================================
// Vertex shader.
//================================

struct VS_OUTPUT_Shade
{
	float4 Pos			: POSITION;
	float4 Col			: COLOR0;
	float2 TexUV		: TEXCOORD0;
	float2 ShadeTexUV	: TEXCOORD1;
};

VS_OUTPUT_Shade VS_Shade(
	float4 Pos			: POSITION,
	float3 Normal		: NORMAL,
	float2 TexCoord0	: TEXCOORD0,
	float4 Diffuse		: COLOR0
)
{
	VS_OUTPUT_Shade Out = (VS_OUTPUT_Shade)0;
	
	Out.Pos = mul( Pos, MatrixWVP );

	float3 norm = normalize(Normal);
	float3 ligt = normalize(LightDirection);
	float dot = dot( mul( norm, (float3x3)MatrixW ), -ligt );
	
//	dot *= 0.01f;
	dot += 1.0f;
	dot *= 0.5f;
	
	Out.ShadeTexUV.x = dot;
	Out.ShadeTexUV.y = ShadowU;
	
	Out.Col = Diffuse * LightDiffuseColor + LightAmbientColor;

	Out.TexUV = TexCoord0;

	return Out;
}

//================================
// Pixel shader.
//================================
float4 PS_Shade( VS_OUTPUT_Shade In ) : COLOR0
{
	return tex2D( MeshTextureSampler, In.TexUV ) * tex2D( ShadeTextureSampler, In.ShadeTexUV ) * In.Col;
}
Shader texture.
Please edit it according to your model.

If it is composed of 5 meshes.
Image

If it is composed of 4 meshes.
Image

But it has the problem.
- Cannot change the scale of the model.
- Only Directional Light can be used.
- The light is only one.
- The model must make outline.
Please excuse my limited English skills.
MasterGod
Posts: 2061
Joined: Fri May 25, 2007 8:06 pm
Location: Israel
Contact:

Post by MasterGod »

A screen shot would be nice..
Image
Dev State: Abandoned (For now..)
Requirements Analysis Doc: ~87%
UML: ~0.5%
sio2
Competition winner
Posts: 1003
Joined: Thu Sep 21, 2006 5:33 pm
Location: UK

Post by sio2 »

That reminds me, I have a toon shader demo on my website. It's a very simple implementation, but may be of interest. Here's the entire source!

Code: Select all

using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

// --------------------------------------------------------------------------

#pragma comment(lib, "Irrlicht.lib")

// --------------------------------------------------------------------------

// OpenGL Vertex Program 1.1
const stringc vertToonShader =
"varying vec3 normal;"
"void main()"
"{"
"	normal = gl_NormalMatrix * gl_Normal;"
"	gl_Position = ftransform();"
"}";

// --------------------------------------------------------------------------

// OpenGL Fragment Program 1.1
const stringc fragToonShader =
"varying vec3 normal;"
"void main()"
"{"
"	float intensity;"
"	vec4 color;"
"	vec3 n = normalize(normal);"
""
"	intensity = dot(vec3(gl_LightSource[0].position),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;"
"}";

// --------------------------------------------------------------------------

int main()
{
	// Create device
	video::E_DRIVER_TYPE driverType = video::EDT_OPENGL;
	IrrlichtDevice *device = createDevice(driverType, dimension2di(800, 600), 32, false, false, false, 0);
	if (!device) {
		printf("Error creating Irrlicht device\n");
		return 0;
	}

	// Obtain device internals
	IVideoDriver* driver = device->getVideoDriver();
	ISceneManager* smgr = device->getSceneManager();

	// Set a window caption
	device->setWindowCaption(L"ToonShader - Irrlicht Engine Demo");

	// Create GLSL shaders
	video::IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices();
	s32 mtlToonShader = video::EMT_SOLID; // Fallback material type
	bool bCanDoGLSL_1_1 = false;
	if (gpu && (driverType == video::EDT_OPENGL)) {
		bCanDoGLSL_1_1 = true; // provisionally accept
		if (!driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)) {
			printf("queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1) failed\n");
			bCanDoGLSL_1_1 = false;
		}
		if (!driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1)) {
			printf("queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1) failed\n");
			bCanDoGLSL_1_1 = false;
		}
	}
	if (bCanDoGLSL_1_1) {
		// Shader uses built-in OpenGL GLSL uniforms, hence needs no explicit constants.
		// Therefore a callback function is not needed and is NULL.
		mtlToonShader = gpu->addHighLevelShaderMaterial(
					vertToonShader.c_str(), "main", video::EVST_VS_1_1,
					fragToonShader.c_str(), "main", video::EPST_PS_1_1,
					NULL, video::EMT_SOLID);
	} else {
		// This demo is for OpenGL!
		printf("This demo requires OpenGL with GLSL High-Level shaders\n");
		mtlToonShader = video::EMT_SOLID;
	}

	// Add an animated mesh
	IAnimatedMesh* mesh = smgr->getMesh("./data/sydney.md2");
	IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );
	if (node)
	{
		node->setMaterialFlag(EMF_LIGHTING, false);
		node->setMD2Animation ( scene::EMAT_STAND );
		node->setMaterialTexture( 0, driver->getTexture("./data/sydney.bmp") );
		node->setMaterialType((video::E_MATERIAL_TYPE)mtlToonShader); // Override material type
	}

	// Add a viewing camera
	smgr->addCameraSceneNode(0, vector3df(0,30,-40), vector3df(0,5,0));

	// Main rendering loop
	while(device->run())
	{
		driver->beginScene(true, true, SColor(255,100,101,140));
		smgr->drawAll();
		driver->endScene();
	}

	device->drop();

	// Done
	return 0;
}

// --------------------------------------------------------------------------
B@z
Posts: 876
Joined: Thu Jan 31, 2008 5:05 pm
Location: Hungary

Post by B@z »

sio2 wrote:That reminds me, I have a toon shader demo on my website. It's a very simple implementation, but may be of interest. Here's the entire source!
we want a screenshot from this too :P
Image
Image
Halifax
Posts: 1424
Joined: Sun Apr 29, 2007 10:40 pm
Location: $9D95

Post by Halifax »

B@z wrote:
sio2 wrote:That reminds me, I have a toon shader demo on my website. It's a very simple implementation, but may be of interest. Here's the entire source!
we want a screenshot from this too :P
Image
TheQuestion = 2B || !2B
oldskoolPunk
Posts: 199
Joined: Wed Nov 29, 2006 4:07 am

Post by oldskoolPunk »

I would also like to see a picture. I would like to see what it looks like before digging into this giant clump of code you have here. I don't understand about the images you are using? It is like a texture look-up for the color instead of a calculation? If so, then what if there is only one mesh? or 10?


Plus Im interested in finding a better inking solution, I would much like to see what yours looks like.

Im also wondering which is faster, a texture look-up or a dot(N,L) type of calculation?

It seems the calculation method is at least alot shorter :) Heres mine so far. It uses the vertex colors of the mesh.


Image

Code: Select all

// toon tree vertex shader

varying vec4 Color;
varying vec3 Normal;
varying vec3 ViewDirection;
varying vec3 LightDirection;

void main()
{
   gl_Position = ftransform();
   gl_TexCoord[0] = gl_MultiTexCoord0;
   Normal  = gl_NormalMatrix * gl_Normal;
   vec3 Position = gl_ModelViewMatrix * gl_Vertex;
   LightDirection =  gl_NormalMatrix * vec3(0.75,1,-0.75);//directional sunlight
   ViewDirection = -Position;
   Color = gl_Color;
         
}

Code: Select all

// toon tree pixel shader

varying vec3 Normal;
varying vec3 LightDirection;
varying vec3 ViewDirection;

varying vec4 Color;

void main()
{
   vec3 N = normalize(Normal);
   vec3 L = normalize(LightDirection);
   vec3 E = normalize(ViewDirection);
   vec3 R = reflect(-L, N);
   
   float light_angle = max(dot(R,E),0.0);
   if (light_angle > 0.01) Color *= 1.5;//diffuse
   if (light_angle > 0.9) Color=vec4(1,1,1,1);//specular
   
   float ink_angle = max(dot(N,E),0.0);
   if (ink_angle <0.25) Color=vec4(0,0,0,0);//inking
   
   gl_FragColor = Color;
}
Signature? I ain't signin nuthin!
sio2
Competition winner
Posts: 1003
Joined: Thu Sep 21, 2006 5:33 pm
Location: UK

Post by sio2 »

B@z wrote:
sio2 wrote:That reminds me, I have a toon shader demo on my website. It's a very simple implementation, but may be of interest. Here's the entire source!
we want a screenshot from this too :P
My website has a screenshot. :roll:
Dan911
Posts: 21
Joined: Tue Dec 16, 2008 11:28 pm

great

Post by Dan911 »

oldskoolPunk wrote:I would also like to see a picture. I would like to see what it looks like before digging into this giant clump of code you have here. I don't understand about the images you are using? It is like a texture look-up for the color instead of a calculation? If so, then what if there is only one mesh? or 10?


Plus Im interested in finding a better inking solution, I would much like to see what yours looks like.

Im also wondering which is faster, a texture look-up or a dot(N,L) type of calculation?

It seems the calculation method is at least alot shorter :) Heres mine so far. It uses the vertex colors of the mesh.


Image

Code: Select all

// toon tree vertex shader

varying vec4 Color;
varying vec3 Normal;
varying vec3 ViewDirection;
varying vec3 LightDirection;

void main()
{
   gl_Position = ftransform();
   gl_TexCoord[0] = gl_MultiTexCoord0;
   Normal  = gl_NormalMatrix * gl_Normal;
   vec3 Position = gl_ModelViewMatrix * gl_Vertex;
   LightDirection =  gl_NormalMatrix * vec3(0.75,1,-0.75);//directional sunlight
   ViewDirection = -Position;
   Color = gl_Color;
         
}

Code: Select all

// toon tree pixel shader

varying vec3 Normal;
varying vec3 LightDirection;
varying vec3 ViewDirection;

varying vec4 Color;

void main()
{
   vec3 N = normalize(Normal);
   vec3 L = normalize(LightDirection);
   vec3 E = normalize(ViewDirection);
   vec3 R = reflect(-L, N);
   
   float light_angle = max(dot(R,E),0.0);
   if (light_angle > 0.01) Color *= 1.5;//diffuse
   if (light_angle > 0.9) Color=vec4(1,1,1,1);//specular
   
   float ink_angle = max(dot(N,E),0.0);
   if (ink_angle <0.25) Color=vec4(0,0,0,0);//inking
   
   gl_FragColor = Color;
}

looks amazing oldschool, but i cant get your shader to work. Got full source for an example?
Post Reply