2D Toon shader

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By dejvid_bejlej

How would I go about creating a toon shader/canvas item material?

I have a Sprite node with a diffuse texture an a normal map. Since I’m using pixel art, it would look so much better this way.

I’ll take any help, this is really important to me.

:bust_in_silhouette: Reply From: estebanmolca

If you don’t know anything about shader, review the godot manual, then you can go to pages like shadertoy and convert the shader you like.

Few things change in language, for example in shadertoy and the screen resolution is expressed as iResolution and in godot it is (1.0 / SCREEN_PIXEL_SIZE).

I leave an example of this shader: Shader - Shadertoy BETA
Look at its code and compare how it becomes godot:

shader_type canvas_item;
uniform float nColors =4.0;


vec3 lerp(vec3 colorone, vec3 colortwo, float value)
{
	return (colorone + value*(colortwo-colorone));
} 

vec3 RGBToHSV( vec3 RGB ){
    
    vec4 k = vec4(0.0, -1.0/3.0, 2.0/3.0, -1.0);
    vec4 p = RGB.g < RGB.b ? vec4(RGB.b, RGB.g, k.w, k.z) : vec4(RGB.gb, k.xy);
    vec4 q = RGB.r < p.x   ? vec4(p.x, p.y, p.w, RGB.r) : vec4(RGB.r, p.yzx);
    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

vec3 HSVToRGB( vec3 HSV ){
    
    vec4 k = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(HSV.xxx + k.xyz) * 6.0 - k.www);
    return HSV.z * lerp(k.xxx, clamp(p - k.xxx, 0.0, 1.0), HSV.y);
}

void fragment()
{ 
    float vx_offset = 0.5;
	//vec2 uv = FRAGCOORD.xy / (1.0/SCREEN_PIXEL_SIZE);
	vec2 uv=UV;   
	vec3 tc = texture(TEXTURE, uv).rgb;
    vec2 coord = vec2(0.,0.);
    
    float cutColor = 1./nColors;
    
    if(uv.x < (vx_offset-0.001))
    {
         
   	 	tc = RGBToHSV(tc);
    
    	vec2 target_c = cutColor*floor(tc.gb/cutColor);
    
    	tc = HSVToRGB(vec3(tc.r,target_c));
    }
    else if (uv.x>=(vx_offset+0.01))
    {
        
        tc  = cutColor*floor(tc/cutColor);
    }
    
    
    
  	if(tc.g > (tc.r + tc.b)*0.7)
	{
		//tc.rgb = vec3(0.2,0.2,0.2);
	}
    
    
    COLOR = vec4(tc, 1.0);
   
}

To assign it to a sprite, for example, in the sprite options go to material, new material shader, edit, shader: new shader, edit, and paste the code in the shader editor.

Thanks for the reply!

Yeah, I don’t know anything about shading language, that’s why I tried using visual editor.

I’m trying to get the shadows using Light2D and a Sprite with normal maps. Sprite textures have transparent pixels. I’ve pasted your code and this is what I got:
enter image description here

In comparison, this is what I got so far using visual editor (the light source is in the top right):
enter image description here

So, yeah. I’m trying to make it work like that, but the shadow is too intense and I don’t know how to reduce it. Also, the shadow gets darker the closer the light source is.

I know it’s hard to follow what I’m trying to achieve because I don’t really know much about shaders.
Bottom line is - I want the Light2D to cast cell shadows, not smooth shadows on a Sprite with a normal map.

Thanks for your answer, I’ll keep trying to make it work.

dejvid_bejlej | 2020-01-09 22:30

Sorry, for reading fast I got confused, I thought you meant the ShaderMaterial but this is referred to the CanvasItemMaterial. I will also see how your CanvasItemMaterial behaves in godot

estebanmolca | 2020-01-09 23:24

No worries at all, I’m thankful for any help

dejvid_bejlej | 2020-01-10 00:07

Did you find a good working solution? The posted answer does not work for me. I can’t seem to find anything online about doing a cell shader for 2D sprites. Everything is all 3D oriented.

HeroicNate | 2021-04-28 04:34

:bust_in_silhouette: Reply From: gamedevshirious

I saw an amazing shader
Hope this helps

Damn, year and a half later and someone finally did that. Thanks a ton! Sadly, I don’t need it anymore

dejvid_bejlej | 2021-08-26 20:38