hatch shader for dark scenes

hatch shader calculated as fragment so hatch patetrn works also if albedo color of base texture is set to fully black

Adapted from https://godotshaders.com/shader/toon-hatching-shader/ which sets the hatch pattern in the light method

example hatch texture as screenshot 2

Shader code
shader_type spatial;

const mat2 ORIENTATION_STRAIGHT = mat2(vec2(1.0f, 0.0f), vec2(0.0f, 1.0f));
const mat2 ORIENTATION_CROSS = mat2(vec2(0.0f, 1.0f), vec2(1.0, 0.0f));

uniform vec4 albedo : source_color = vec4(0.0);  // Default to black
uniform vec4 hatch_color : source_color = vec4(1.0);  // Default to white
uniform int cuts : hint_range(1, 8) = 2;
uniform float wrap : hint_range(-2.0f, 2.0f) = 0.0f;
uniform float steepness : hint_range(1.0f, 8.0f) = 1.0;
uniform bool use_attenuation = true;
uniform bool use_rim = false;
uniform float rim_width : hint_range(0.0f, 16.0f) = 8.0f;
uniform vec4 rim_color : source_color = vec4(1.0f);
uniform float hatch_scale = 2.0f;
uniform bool use_triplanar = true;
uniform sampler2D hatch_texture : source_color;

varying vec3 vertex_pos;
varying vec3 normal;

vec4 triplanar_texture(sampler2D p_sampler,vec3 p_weights,vec3 p_triplanar_pos, mat2 orientation) {
	p_weights = abs(p_weights);
	p_weights /= p_weights.x+p_weights.y+p_weights.z;
	vec4 samp=vec4(0.0);
	samp+= texture(p_sampler,orientation*p_triplanar_pos.xy) * p_weights.z;
	samp+= texture(p_sampler,orientation*p_triplanar_pos.xz) * p_weights.y;
	samp+= texture(p_sampler,orientation*p_triplanar_pos.zy * vec2(-1.0,1.0)) * p_weights.x;
	return samp;
}

float split_hatch(float diffuse, vec2 uv, vec3 weights, vec3 pos) {
    float value = 1.0f;
    float k = round((1.0f - diffuse) * float(cuts)) - 0.5;
    for (float i = 0.0f; i < k; ++i) {
        float offset = 2.0 * i / float(cuts);
        if (i >= float(cuts) / 2.0) {
            if (use_triplanar) {
                value *= triplanar_texture(hatch_texture, weights, pos + vec3(offset), ORIENTATION_CROSS).r;
            } else {
                value *= texture(hatch_texture, uv.yx + vec2(offset)).r;
            }
        } else {
            if (use_triplanar) {
                value *= triplanar_texture(hatch_texture, weights, pos + vec3(offset), ORIENTATION_STRAIGHT).r;
            } else {
                value *= texture(hatch_texture, uv.xy + vec2(offset)).r;
            }
        }
    }
    return 1.0 - value;  // Invert the value so that 1 means full hatch
}

void vertex() {
    vertex_pos = VERTEX;
    normal = NORMAL;
}

void fragment() {
    // Calculate base hatch value
    float base_hatch = split_hatch(0.5, hatch_scale * UV, normal, hatch_scale * vertex_pos);

    // Mix albedo and hatch color
    ALBEDO = mix(albedo.rgb, hatch_color.rgb, base_hatch);
}

void light() {
    // Calculate lighting
    float NdotL = dot(NORMAL, LIGHT);
    float diffuse_amount = NdotL + wrap;
    diffuse_amount *= steepness;
    float cuts_inv = 1.0f / float(cuts);
    float diffuse_stepped = clamp(diffuse_amount + mod(1.0f - diffuse_amount, cuts_inv), 0.0f, 1.0f);

    // Recalculate hatch value based on lighting
    float hatch_value = split_hatch(diffuse_stepped, hatch_scale * UV, normal, hatch_scale * vertex_pos);

    // Apply attenuation if enabled
    float attenuation = use_attenuation ? ATTENUATION : 1.0;

    // Calculate final color
    vec3 final_color = mix(ALBEDO, hatch_color.rgb, hatch_value);

    // Apply lighting
    DIFFUSE_LIGHT += final_color * LIGHT_COLOR * attenuation;

    // Apply rim lighting if enabled
    if (use_rim) {
        float NdotV = dot(NORMAL, VIEW);
        float rim_light = pow(1.0 - NdotV, rim_width);
        DIFFUSE_LIGHT += rim_light * rim_color.rgb * rim_color.a * LIGHT_COLOR * attenuation;
    }
}
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from graphific

Raymarched 3D Noise Decal (fork)

Related shaders

Electric Hatch Background Shader

far distance water shader (sea shader)

Pixel Cloud Shader

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments