(HLSL) Cel (Toon) Shading

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

(HLSL) Cel (Toon) Shading

Post by BlindSide »

UPDATE:

Because of the recent interest in this, I have decided to improve it:

Image

Shader and Demo can be downloaded from: http://irrlichtirc.g0dsoft.com/BlindSide/celshading.zip

(Disregard all outdated instructions below, just follow what I do in the example provided.)

UPDATE:
To use this with the newest Irrlicht version do:
YOURSCENENODE->getMaterial(0).TextureLayer[2].BilinearFilter = false;
---------------

Hey guys I made a nice cel shader based on the OGRE and Omaremad's shader a while ago heres the code:

toonshade.hlsl:

Code: Select all

//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
float4x4 mWorldViewProj;  // World * View * Projection transformation
float4x4 mInvWorld;       // Inverted world matrix
float3 mLightPos;         // Light position

struct VS_OUTPUT 
{
   float4 Position: POSITION0;
   float2 TexCoord:   TEXCOORD0;
   float2 diffuse: TEXCOORD1;
};


VS_OUTPUT vertexMain( 
   float4 Position: POSITION0,
   float2 TexCoord: TEXCOORD0,
   float2 diffuse: TEXCOORD1,
   float3 Normal:   NORMAL0)
{
	VS_OUTPUT Output;

	// calculate light vector
	float3 N = normalize(Normal);
	float3 L = normalize(mLightPos - Position.xyz);

	// Calculate diffuse component
	diffuse = max(dot(N, L),0);

	// Shading offset
	float offset = 0.1f;
	
	//Subtract the offset and clamp the texture to 0
	diffuse = clamp(diffuse+offset,0,1);

	Output.Position = mul(Position,mWorldViewProj);
	Output.diffuse = diffuse;
	Output.TexCoord = TexCoord;
	return( Output );
}
	
sampler2D tex0 : register(s0) ;
sampler1D diffuseRamp : register(s2) ;

float4 pixelMain(
	float2 TexCoord:  	TEXCOORD0,
   	float2 diffuseIn: 	TEXCOORD1 
	) : COLOR0
{
	float4 color = float4(1,1,1,1);
	float4 diffuse =  float4(0.8f, 0.8f, 0.8f, 1.0f);
	// Step functions from textures
	diffuseIn = tex1D(diffuseRamp, diffuseIn).x;

		
	
	color = (diffuse * diffuseIn);
  	float4 col = tex2D( tex0, TexCoord);  
  	col*=2;
   	return( color*col );
 
}
Usage:

In the vertex shader callback make sure to send the light position as mLightPos and on your scene node make the 3rd (not 2nd not 4th) material similar to this:
Image

But 10 times smaller, so you get a 1D texture 16 pixels wide determining the shades of the cel shading.

Preview:
Image

Cheers.
Last edited by BlindSide on Mon Mar 02, 2009 10:36 pm, edited 6 times in total.
posttool
Posts: 13
Joined: Thu Dec 07, 2006 5:40 pm
Location: San Francisco
Contact:

nice work!

Post by posttool »

we should get a group together for shaders & post processing effects and incorporate them into irr|cinema

i could try to organize (and contribute)... who is interested?
noreg
Posts: 158
Joined: Fri Jun 23, 2006 6:01 pm
Location: continental europe

Post by noreg »

I'm tinkering with shaders cause i'm fascinated by the fact that the average GPU already has a power of a Cray Supercomputer bout ten years ago. However, after adapting and altering some shaders for Blender and Irrlicht I see a need for some concerted efforts. I just could not find a general workflow for this, even understanding most of the probs. A good idea how to organise common efforts would be highly appreciated.
Cheers, noreg
omaremad
Competition winner
Posts: 1027
Joined: Fri Jul 15, 2005 11:30 pm
Location: Cairo,Egypt

Post by omaremad »

Get the cg toolkit and read the refrence and user guides in it (its general ttalk about shaders not just cg) they are the best shader books that you can get for free. they dont focus on any effects at all, they teach you the hardware and shader coding fundumentals. Things go really smoothly with a good understanding.
"Irrlicht is obese"

If you want modern rendering techniques learn how to make them or go to the engine next door =p
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Post by BlindSide »

Yes noreg it is pretty easy when you understand what commands equal ones in other languages like GLSL, HLSL etc. I do not really know what all these things do in the shader I just managed to convert using sample code. (But slowly I am learning :) ) I hope that people will find more nice shaders and convert them to irrlicht (Unfortunately for me I only have PS 1.4 (And want to stick with it to ensure compatibility, and cuz im cheap :P ) so I cannot see many nice shader effects, but alot is achievable with just 1.4, people like to use 2.0+ to make it easier)
klikmaster
Posts: 40
Joined: Mon Sep 11, 2006 1:06 pm

Post by klikmaster »

Hey, can I firstly say, nice shader :)

I'm having some problems though. I can get my project to run fine, but these errors pop up in the console:

Code: Select all

HLSL pixel shader compilation failed:
(54): Warning X3509: Tex1D will considered dependant since texcoord was not declared as at least float2
(54): Warning X4523: cannot map this dependant texture read to ps_1_1
My model appears like this:
Image
As you can see, it's not being cel shaded.
~IRRLICHT ROX MY SOX~
sio2
Competition winner
Posts: 1003
Joined: Thu Sep 21, 2006 5:33 pm
Location: UK

Post by sio2 »

Vertex output to texcoord1 is float2 but pixel input to texcoord1 is float. Change it to float2. PS1.1 can be tricky at times.

I wondering why the shader compilation failed with just warnings, though?

BTW I have a cel shader demo with source on my website.
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Post by BlindSide »

Yeah sio2 is probably right try changing "float diffuseIn" to "float2 diffuseIn" (I already changed this in main post.)

Also changing the "Sampler1D" to "Sampler2D" might make a difference...Im not sure if PS1.1 supports 1D textures or whatever.
klikmaster
Posts: 40
Joined: Mon Sep 11, 2006 1:06 pm

Post by klikmaster »

I changed sampler1D to sampler2D (had to also change tex1D to tex2D) but I still get the ps1.1 error.

I changed float to float2, but that gives me these errors:

Code: Select all

(54): warning X3083: Truncating 2-vector to size 1
(58): error X3017: cannot implicitly convert from 'const float2' to 'float4'
~IRRLICHT ROX MY SOX~
sio2
Competition winner
Posts: 1003
Joined: Thu Sep 21, 2006 5:33 pm
Location: UK

Post by sio2 »

With a few alterations I got it through FXC (with vs1.1 and ps1.1 targets) and it gave no warnings or errors:

Code: Select all

//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
float4x4 mWorldViewProj;  // World * View * Projection transformation
float4x4 mInvWorld;       // Inverted world matrix
float3 mLightPos;         // Light position

struct VS_OUTPUT
{
   float4 Position: POSITION0;
   float2 TexCoord:   TEXCOORD0;
   float2 diffuse: TEXCOORD1;
};


VS_OUTPUT vertexMain(
   float4 Position: POSITION0,
   float2 TexCoord: TEXCOORD0,
   float2 diffuse: TEXCOORD1,
   float3 Normal:   NORMAL0)
{
   VS_OUTPUT Output;

   // calculate light vector
   float3 N = normalize(Normal);
   float3 L = normalize(mLightPos - Position.xyz);

   // Calculate diffuse component
   diffuse = max(dot(N, L),0);

   // Shading offset
   float offset = 0.1f;
   
   //Subtract the offset and clamp the texture to 0
   diffuse = clamp(diffuse+offset,0,1);

   Output.Position = mul(Position,mWorldViewProj);
   Output.diffuse = diffuse;
   Output.TexCoord = TexCoord;
   return( Output );
}
   
sampler2D tex0 : register(s0) ;
sampler1D diffuseRamp : register(s1) ;

float4 pixelMain(
   float2 TexCoord:     TEXCOORD0,
      float2 diffuseIn:    TEXCOORD1
   ) : COLOR0
{
   float4 color = float4(1,1,1,1);
   float4 diffuse =  float4(0.8f, 0.8f, 0.8f, 1.0f);
   // Step functions from textures
   diffuseIn = tex1D(diffuseRamp, diffuseIn.x).x;

      
   
   color = (diffuse * diffuseIn.x);
     float4 col = tex2D( tex0, TexCoord); 
     col*=2;
      return( color*col );
 
} 
sio2
Competition winner
Posts: 1003
Joined: Thu Sep 21, 2006 5:33 pm
Location: UK

Post by sio2 »

BTW I should point out that I changed diffuseRamp sampler from s2 to s1.

To compile with FXC I put this into "compile.cmd" (note that I have FXC in my system paths):

Code: Select all

fxc toon.hlsl /Fc toon.vsc /T vs_1_1 /E vertexMain
fxc toon.hlsl /Fc toon.psc /T ps_1_1 /E pixelMain
This gives...

Code: Select all

//
// Generated by Microsoft (R) D3DX9 Shader Compiler 9.15.779.0000
//
//   fxc toon.hlsl /Fc toon.vsc /T vs_1_1 /E vertexMain
//
//
// Parameters:
//
//   float3 mLightPos;
//   float4x4 mWorldViewProj;
//
//
// Registers:
//
//   Name           Reg   Size
//   -------------- ----- ----
//   mWorldViewProj c0       4
//   mLightPos      c4       1
//

    vs_1_1
    def c5, 0, 0.100000001, 1, 0
    dcl_position v0
    dcl_texcoord v1
    dcl_normal v2
    add r0.xyz, -v0, c4
    dp3 r2.x, r0, r0
    dp3 r1.x, v2, v2
    rsq r1.w, r2.x
    rsq r0.w, r1.x
    mul r1.xyz, r0, r1.w
    mul r0.xyz, r0.w, v2
    dp3 r0.x, r0, r1
    max r0.w, r0.x, c5.x
    add r0.w, r0.w, c5.y
    min oT1.xy, r0.w, c5.z
    dp4 oPos.x, v0, c0
    dp4 oPos.y, v0, c1
    dp4 oPos.z, v0, c2
    dp4 oPos.w, v0, c3
    mov oT0.xy, v1

// approximately 16 instruction slots used
...and...

Code: Select all

//
// Generated by Microsoft (R) D3DX9 Shader Compiler 9.15.779.0000
//
//   fxc toon.hlsl /Fc toon.psc /T ps_1_1 /E pixelMain
//
//
// Parameters:
//
//   sampler1D diffuseRamp;
//   sampler2D tex0;
//
//
// Registers:
//
//   Name         Reg   Size
//   ------------ ----- ----
//   tex0         s0       1
//   diffuseRamp  s1       1
//

    ps_1_1
    def c0, 1, 0, 0, 0
    def c1, 0.800000012, 0.800000012, 0.800000012, 1
    tex t0
    tex t1
    dp3 r0, c0, t1
    mul r0, r0, c1
    mul_x2 r0, t0, r0

// approximately 5 instruction slots used (2 texture, 3 arithmetic)
klikmaster
Posts: 40
Joined: Mon Sep 11, 2006 1:06 pm

Post by klikmaster »

Thanks sio2, that seemed to help a little...
but now my model is black and I get this printed in the console:

Code: Select all

HLSL variable to set not found: mLightcolor
HLSL variable to set not found: mTransWorld
HLSL variable to set not found: mInvWorld
Sorry if I sound noobish :P I'm a shader noob :(
~IRRLICHT ROX MY SOX~
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

The shaders are no longer high level shaders, so you can't refer to the shader variables by name any more. You need to refer to the documentation of IMaterialRenderServices::setPixelShaderConstant() and setVertexShaderConstant().

You want the overloads of those functions that take a parameter named startRegister instead of the ones that take name. The shader compiler output he posted tells you what registers each of the variables are now mapped to. You might also want to have a look at the code in the Shader tutorial.

Travis
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Post by BlindSide »

sio2 wrote:BTW I should point out that I changed diffuseRamp sampler from s2 to s1.
sio2, theres a good reason its s2, the effect does not work with s1 due to a filtering bug with irrlicht's materials. (To be more specific Im utilizing this bug slightly because s2 and s3 are only point filtered or something. Is there a way to force this on s0 and s1 so this wouldnt be a problem when they fix this bug?)

Either way, Thanks for fixing sio2 :D

klikmaster, you have to pass it the correct variables. mLightcolor and the rest there are NOT needed. What you need to pass is mLightPos as the light position and mWorldViewProj as the world projection matrix (This last one is already done in the example) But i think the light position you will have to do yourself.

Eg.

Code: Select all

vector3df lightPosOS;
invWorld.transformVect(lightnode->getPosition(),lightPosOS); 
services->setVertexShaderConstant("mLightPos", reinterpret_cast<f32*>(&lightPosOS), 3);
Note that "lightnode" is your *erhum* light node...

Goodluck :D, tell me if you want to add specular highlights or (maybe) edge detection to this shader. (I removed them from the original OGRE shader because i did not need them)

PS: Vitek, I think the errors are because those variables are all unused in the shader (This includes mInvWorld even though its included its not called in a function so shader emits it and gives an error anyway)
sio2
Competition winner
Posts: 1003
Joined: Thu Sep 21, 2006 5:33 pm
Location: UK

Post by sio2 »

If I'd have known it would confuse people I wouldn't have posted the ps1.1 asm. :wink: The asm code was to show how to use the Effect Compiler FXC.exe to compile HLSL into asm.

@Blindside: I did wonder about the sampler thing. :wink: If you need different filters on textures then I don't think you can currently do it* - a filter is set per-material. :|

(*unless you exploit the 1.2 bug, but that's gone in the latest svn version)
Post Reply