Ascii shader

ENGINE: Godot 4.3

Hi! I’ve tried to implement Acerola’s ascii shader, hope you find it useful ๐Ÿ™‚

I’ve explained all the steps in making this shader inside my repository: Ascii_Shader

Shader code
/*
	AUTHOR: Daniel Bologna (call me Dan)
	GIT: https://github.com/AbstractBorderStudio
	ENGINE_VERSION: 4.3
*/

shader_type spatial;
render_mode unshaded;

uniform sampler2D _screen : hint_screen_texture, repeat_disable;
uniform sampler2D _ascii_tex : filter_nearest;
uniform sampler2D _ascii_edge_tex : filter_nearest;

uniform float _char_size = 8.0; // 8 is the ascii character size in the texture (res://Assets/Texture/fillASCII.png) 
uniform float _char_count = 10.0;

vec2 _downscale_tex(vec2 uv, vec2 screen_size, float pixel_size)
{
	return vec2(floor(uv.x * screen_size.x / pixel_size) / screen_size.x * pixel_size, floor(uv.y * screen_size.y / pixel_size) / screen_size.y * pixel_size);
}

float _compute_luminosity(vec3 tex)
{
	// get image luminosity (https://learn.microsoft.com/it-it/windows/win32/medfound/about-yuv-video)
	return 0.2126 * tex.r + 0.7152 * tex.g + 0.0722 * tex.b; // [0, 1]
}

float _quantize(float value, float size)
{
	// value is a [0, 1] range
	// quantize the value as an integer between [0, 1] with a step of 
	return clamp(floor(value * size), 0.0, size - 1.0);
}

vec2 _map_pixel_coord(vec2 fragcoord, float pixel_size)
{
	// clamp pixel coordinate inside a (pixel_size x pixel_size) square
	return vec2(float(int(fragcoord.x) % int(pixel_size)), float(int(fragcoord.y) % int(pixel_size)));
}

float _get_ascii(float index, vec2 coordinate, vec2 uv)
{
	// get the single pixel scale relative to the [0, 1] uv range.
	float x_scale = 1.0 / (_char_count * _char_size);
	float y_scale = 1.0 / _char_size;
	
	// get the coordinate of the (x, y) pixel of the texture and scale the pixel fullscreen to get the color
	vec2 scaled_uv = uv / (vec2(_char_count, 1.0) * _char_size) 		// scale the uv so we have a single pixel from the texture
			+ vec2(x_scale * _char_size * index, 0.0) 					// displace by moving 8 pixel at a time to reach the correct character
			+ vec2(x_scale * coordinate.x, y_scale * coordinate.y); 	// get the (x,y) displaced position inside the character grid
	
	// return the ascii texture sampled in this new uv
	return texture(_ascii_tex, scaled_uv).r;
}

void vertex() 
{
	// godot 4.3 way to set the full screen quad for Screen Space Shaders
	// https://godotengine.org/article/introducing-reverse-z/
	POSITION = vec4(VERTEX.xy, 1.0, 1.0);
}

void fragment() 
{
	// get screen UV
	vec2 uv = SCREEN_UV;
	
	// make each pixel 8x8 by upscaling, flooring and then re-downscaling the uv
	vec2 downscaled_uv = _downscale_tex(uv, VIEWPORT_SIZE, _char_size);
	
	// sample the texture on the downscaled uv
	vec3 tex = texture(_screen, downscaled_uv).rgb; 

	// quantize colospace to 10 colors to match ascii characters number
	// in this way lm goes from 0 to 9 and we can get correct index from the ascii texture 
	// the floor delets all float values and leaves only the integers [0, 1, ..., 9]
	// allowing omogeneus spread of the characters
	float lm = _quantize(
					_compute_luminosity(tex), 
					_char_count);
	
	// map the pixel position inside an 8x8 square so it goes from 0 to 7
	vec2 pixel_offset = _map_pixel_coord(FRAGCOORD.xy, _char_size);
	
	// get corresponding pixel value based on offset and luminosity from che ascii texture
	float ascii = _get_ascii(lm, pixel_offset, uv);
	
	// clamp the values
	ascii = (ascii > 0.1) ? 1.0 : 0.0;
	
	// add color
	vec3 col = tex * ascii;
	ALBEDO = vec3(ascii);// col;
}
Tags
Ascii, postprocess, shader, Spatial
The shader code and all code snippets in this post are under MIT license and can be used freely. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from Dan

Energy shield with impact effect

Related shaders

Ascii Effect Shader

Glass shader

2.5d sprite shader

Subscribe
Notify of
guest

5 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Hashir Shazad
Hashir Shazad
2 months ago

Nice shaders ๐Ÿ˜€

Londanomala
Londanomala
2 months ago

Hey Dan, i don’t really get how to use this, i’m pretty new to shaders. I want to apply this to a viewportcontainer so it affects all objects inside it. But if I use this shader in the viewportcontainer material it does not work. Where am i supposed to use the code?

Lan
Lan
20 days ago

Hi, i am new to shaders and Godot. Why wont this shader apply for the entirety of the screen ? Some elements on the screen are unaffected by the shader (my character and near elements). Is it a layer issue ? Also colors are not showing up. Cool shader nevertheless.

Last edited 20 days ago by Lan