Pencil screen space shader

This is a simple screen space shader imitating pencil drawing (sort of).

Shader code
shader_type canvas_item;

uniform vec4 u_bgColor: hint_color = vec4(1.0, 1.0, 1.0, 1.0);
uniform float u_bgColorFactor: hint_range(0.0, 1.0) = 0.4;
uniform vec4 u_patternColor: hint_color = vec4(0.0, 0.0, 0.0, 1.0);

uniform float u_threshold1: hint_range(0.0, 1.0) = 0.75;
uniform float u_threshold2: hint_range(0.0, 1.0) = 0.50;
uniform float u_threshold3: hint_range(0.0, 1.0) = 0.25;
uniform float u_threshold4: hint_range(0.0, 1.0) = 0.05;

uniform vec2 u_bgTiling = vec2(1.0, 1.0);
uniform vec2 u_patternTiling = vec2(1.0, 1.0);

uniform sampler2D u_bgTexture;
uniform sampler2D u_patternTexture;

const float C2_SQRT2 = 0.707106781;
const mat2 ROT_45 = mat2(vec2(C2_SQRT2, -C2_SQRT2), vec2(C2_SQRT2, C2_SQRT2));
const vec4 COLOR_WHITE = vec4(1.0, 1.0, 1.0, 1.0);

float getIntensity(vec3 color)
{
	return 0.299*color.r + 0.587*color.g + 0.114*color.b;
}

vec4 getPatternColor(vec2 uv, float intensity)
{
	vec2 patternUV1 = vec2(-uv.x, uv.y) * u_patternTiling;
	vec2 patternUV2 = uv * u_patternTiling;
	vec2 patternUV3 = ROT_45*(uv + vec2(0.2358, 0.9123)) * u_patternTiling;
	vec2 patternUV4 = (vec2(uv.x, -uv.y) + vec2(0.4123, 0.7218)) * u_patternTiling;
	vec4 pCol1 = texture(u_patternTexture, patternUV1);
	vec4 pCol2 = texture(u_patternTexture, patternUV2);
	vec4 pCol3 = texture(u_patternTexture, patternUV3);
	vec4 pCol4 = texture(u_patternTexture, patternUV4);
	
	if(intensity > u_threshold1)
		return vec4(1.0, 1.0, 1.0, 1.0);
	if(intensity > u_threshold2)
		return mix(pCol1, COLOR_WHITE, 0.5);
	if(intensity > u_threshold3)
		return mix(pCol1*pCol2, COLOR_WHITE, 0.3);
	if(intensity > u_threshold4)
		return mix(pCol1*pCol2*pCol3, COLOR_WHITE, 0.1);
	return pCol1*pCol2*pCol3*pCol4*0.8;
}

void fragment()
{
	vec4 origColor = texture(SCREEN_TEXTURE, SCREEN_UV);
	float intensity = getIntensity(origColor.rgb);
	vec4 bgColor = mix(texture(u_bgTexture, UV*u_bgTiling), u_bgColor, u_bgColorFactor);
	vec4 patternColor = getPatternColor(UV, intensity);
	vec4 color = mix(u_patternColor, bgColor, getIntensity(patternColor.rgb));
	COLOR = color;
}
Tags
pencil screen-space
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.

Related shaders

Moving planets in space for Skyboxes

Screen Noise Effect Shader

screen tile shader

guest

3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Lihn
Lihn
5 months ago

I absolutely love this shader! It’s so short and simple but it completely changed the aesthetic of my project <3

rob
rob
4 months ago

to make the tiling work properly, you can use “fract” like this

shader_type canvas_item;

uniform vec4 u_bgColor: hint_color = vec4(1.0, 1.0, 1.0, 1.0);
uniform float u_bgColorFactor: hint_range(0.0, 1.0) = 0.4;
uniform vec4 u_patternColor: hint_color = vec4(0.0, 0.0, 0.0, 1.0);

uniform float u_threshold1: hint_range(0.0, 1.0) = 0.75;
uniform float u_threshold2: hint_range(0.0, 1.0) = 0.50;
uniform float u_threshold3: hint_range(0.0, 1.0) = 0.25;
uniform float u_threshold4: hint_range(0.0, 1.0) = 0.05;

uniform vec2 u_bgTiling = vec2(1.0, 1.0);
uniform vec2 u_patternTiling = vec2(1.0, 1.0);

uniform sampler2D u_bgTexture;
uniform sampler2D u_patternTexture;

const float C2_SQRT2 = 0.707106781;
const mat2 ROT_45 = mat2(vec2(C2_SQRT2, -C2_SQRT2), vec2(C2_SQRT2, C2_SQRT2));
const vec4 COLOR_WHITE = vec4(1.0, 1.0, 1.0, 1.0);

float getIntensity(vec3 color)
{
return 0.299*color.r + 0.587*color.g + 0.114*color.b;
}

vec4 getPatternColor(vec2 uv, float intensity)
{
vec2 patternUV1 = fract(vec2(-uv.x, uv.y) * u_patternTiling);
vec2 patternUV2 = fract(uv * u_patternTiling);
vec2 patternUV3 = fract(ROT_45*(uv + vec2(0.2358, 0.9123)) * u_patternTiling);
vec2 patternUV4 = fract((vec2(uv.x, -uv.y) + vec2(0.4123, 0.7218)) * u_patternTiling);
vec4 pCol1 = texture(u_patternTexture, patternUV1);
vec4 pCol2 = texture(u_patternTexture, patternUV2);
vec4 pCol3 = texture(u_patternTexture, patternUV3);
vec4 pCol4 = texture(u_patternTexture, patternUV4);

if(intensity > u_threshold1)
return vec4(1.0, 1.0, 1.0, 1.0);
if(intensity > u_threshold2)
return mix(pCol1, COLOR_WHITE, 0.5);
if(intensity > u_threshold3)
return mix(pCol1*pCol2, COLOR_WHITE, 0.3);
if(intensity > u_threshold4)
return mix(pCol1*pCol2*pCol3, COLOR_WHITE, 0.1);
return pCol1*pCol2*pCol3*pCol4*0.8;
}

void fragment()
{
vec4 origColor = texture(SCREEN_TEXTURE, SCREEN_UV);
float intensity = getIntensity(origColor.rgb);
vec4 bgColor = mix(texture(u_bgTexture, fract(UV*u_bgTiling)), u_bgColor, u_bgColorFactor);
vec4 patternColor = getPatternColor(UV, intensity);
vec4 color = mix(u_patternColor, bgColor, getIntensity(patternColor.rgb));
COLOR = color;
}