bevy_toon_material 0.1.1

A toon shader in the style of Windwaker or Breath of the Wild
Documentation
// Heavily adapted from https://github.com/tbillington/bevy_toon_shader
// And https://roystan.net/articles/toon-shader/
#import bevy_pbr::forward_io::VertexOutput

struct ToonMaterial {
    base_color: vec4<f32>,
    light_direction: vec3<f32>,
    light_color: vec4<f32>,
    camera_position: vec3<f32>,
    ambient_color: vec4<f32>,
    rim_amount: f32,
    rim_color: vec4<f32>,
    rim_threshold: f32,
    band_count: u32
};

@group(2) @binding(0) var<uniform> material: ToonMaterial;
@group(2) @binding(1) var texture: texture_2d<f32>;
@group(2) @binding(2) var sample: sampler;

@fragment
fn fragment(input: VertexOutput) -> @location(0) vec4<f32> {

    #ifdef VERTEX_UVS_A
        let uv = input.uv;
    #else
        let uv = vec2(1.0, 1.0);
    #endif

    let base_color = material.base_color * textureSample(texture, sample, uv);

    // shading the object
    let normal = normalize(input.world_normal); // make the world_normal of the input mesh have a length of one
    let n_dot_l = dot(material.light_direction, normal) ;
    var light_intensity = 0.0;

    // if we want bands, create bands, otherwise use smooth lighting
    if material.band_count > 0 {
        if n_dot_l > 0.0 {
            var x = round(n_dot_l * f32(material.band_count));
            light_intensity = x / f32(material.band_count);
        } else {
            light_intensity = 0.0;
        }
    } else {
        light_intensity = smoothstep(0.0, 0.01, n_dot_l);
    }
   

    let light = (light_intensity * material.light_color);

    // specular 
    let view_direction = normalize(material.camera_position - input.world_position.xyz);
    let half_vector = normalize(material.light_direction + view_direction);
    let n_dot_h = dot(normal, half_vector);
    let spec_color = vec4<f32>(0.9, 0.9, 0.9, 1.0);
    let glossiness = 32.0;
    let spec_intensity = pow(n_dot_h * light_intensity, glossiness * glossiness);
    let spec_intensity_smooth = smoothstep(0.005, 0.01, spec_intensity);
    let specular = spec_intensity_smooth * spec_color;

    // rim lighting
    let rim_dot = 1 - dot(view_direction, normal);
    var rim_intensity = rim_dot * pow(n_dot_l, material.rim_threshold);
    rim_intensity = smoothstep(material.rim_amount - 0.01, material.rim_amount + 0.01, rim_intensity);
    let rim = rim_intensity * material.rim_color;

    return base_color * (material.ambient_color + light + specular + rim);
}