Written by:
http://darkwynter.com
- Jason Hardman
- Subhir Rao
- Amanda Chaffin
Summary:
The term Toon Shader has many terms. A Toon Shader can include code for color blending, color palette reduction, or even edge detection. With a wide variety of combinations, finding the one that gives your game that unique look and feel should be more of an art than a science, or so we think… Here is a base template in HLSL that reduces the color palette (giving objects a paint-by-numbers look) that darkens pixels based on distance from light source.
This post marks the first of a series of shading tutorials coming from darkwynter.
Stay “Tooned”..
:-j
Code:
//====================================================================
// Uniform, Varying, and FrameBuffer Stream Structures -
// Used to Communicate between CPU, VertexProcessor, FragmentProcessor, and FrameBuffer
//====================================================================
struct VertexInput
{
float4 position : POSITION0; // Position
float4 texCoord : TEXCOORD0; // Texture coordinates
float3 normal : NORMAL0; // Normal vector
};
struct VertexOutput
{
// These values are used directly by the rasterizer
float4 position : POSITION; // Position
float4 texCoord : TEXCOORD0; // Texture coordinates
float3 normal : TEXCOORD1; // Texture coordinates
};
struct FragmentInput
{
float4 texCoord : TEXCOORD0; // Texture coordinates
float3 normal : TEXCOORD1; // Texture coordinates
};
struct FragmentOutput
{
float4 color : COLOR0; // Color for render target n
};
// ModelTexture1
texture modelTexture1;
sampler modelTextureSampler1 = sampler_state
{
texture = <modelTexture1>;
AddressU = MIRROR; AddressV = MIRROR; AddressW = MIRROR;
MIPFILTER = ANISOTROPIC; MINFILTER = ANISOTROPIC;
MAGFILTER = ANISOTROPIC;
};
// Viewport Projection Matrix
float4×4 ViewProj;
// Object position in 3D space
float4×4 World;
// Position of Light in 3D space
float3 lightPosition0;
// Color Palette Scaler
// eg – ( 0.1f = 10 Colors Per Channel (RGBA))
// eg – ( 0.01f = 100 Colors Per Channel (RGBA))
float discretePallete = 0.1f;
//————————————————————————————————–
// Toon (Cell shading) Main
//————————————————————————————————–
// Calculate World Coordinates and Pass along..
VertexOutput VS_Toon(VertexInput IN, VertexOutput OUT)
{
OUT.position = mul(IN.position, mul(World, ViewProj));
OUT.texCoord = IN.texCoord;
OUT.normal = IN.normal;
return OUT;
}
// Reduce color palette and darken based on distance from light source
FragmentOutput FS_Toon(FragmentInput IN, FragmentOutput OUT)
{
// Calc light direction and intensity per pixel
float3 lightDir = normalize(lightPosition0);
float intensity = dot(lightDir,normalize(IN.normal));
//Get colour
OUT.color = tex2D(modelTextureSampler1, IN.texCoord);
// Reduce color palette by flooring values at discrete intervals.
OUT.color.rgba -= (OUT.color.rgba % discretePallete );
// Darken color based on intensity
if (intensity > 0.95)
OUT.color -= float4(0.0,0.0,0.0,0.0);
else if (intensity > 0.5)
OUT.color -= float4(0.2,0.2,0.2,0.0);
else if (intensity > 0.25)
OUT.color -= float4(0.4,0.4,0.4,0.0);
else
OUT.color -= float4(0.6,0.6,0.6,0.0);
//return final pixel colour
return OUT;
}
// Vertex Driver Function
VertexOutput ToonVertexMain(VertexInput VS_IN)
{
VertexOutput VS_OUT = (VertexOutput)0;
VS_OUT = VS_Toon(VS_IN, VS_OUT);
return VS_OUT;
}
// Fragment Driver Function
FragmentOutput ToonFragmentMain(FragmentInput FS_IN)
{
FragmentOutput FS_OUT = (FragmentOutput)0;
FS_OUT = FS_Toon(FS_IN, FS_OUT);
return FS_OUT;
}
// Toon Shader Technique
technique ToonShaderMain
{
pass Pass0
{
VertexShader = compile vs_3_0 ToonVertexMain();
PixelShader = compile ps_3_0 ToonFragmentMain();
// Alpha blending
AlphaBlendEnable = true; SrcBlend = SrcAlpha; DestBlend = InvSrcAlpha;
FillMode = Solid;
}
}