0 votes

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.

asked Jan 9 in Engine by dejvid_bejlej (28 points)

1 Answer

0 votes

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.
https://docs.godotengine.org/en/3.1/tutorials/shading/migrating_to_godot_shader_language.html

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

I leave an example of this shader: https://www.shadertoy.com/view/XlSSRW
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.

answered Jan 9 by estebanmolca (814 points)

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.

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

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

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.