Angled pixelation with color palette quantization and fog

Pixel art is cool, and pixelation shader’s are a nice shortcut. Something I’ve only seen once, is pixel art where the pixels are rotated 45 degrees.

I designed this shader to be used on a simple quad meshinstance rather than as a canvas_item shader, since I wanted the depth texture for fog. The angle of the pixels can be changed from 45 degrees as shown in the cover photo, or 0 degrees for classic pixel art.

The first screenshot shows the image that inspired this shader, and the second screenshot shows an alternate color palette with the the pixel angle set to 0 degrees.

Shader code
shader_type spatial;
render_mode unshaded, depth_draw_always, cull_disabled;

uniform sampler2D ScreenTexture : hint_screen_texture;

//Twixel Settings
uniform float TwixelAngle : hint_range(0.0, 45.0) = 45.0;
uniform float PixelSize : hint_range(1.0, 32.0) = 16.0;

//Fog Settings
uniform vec3 FogColor : source_color;
uniform sampler2D DepthTexture : hint_depth_texture, filter_linear_mipmap;
uniform float FogStart = 25.0;    // Distance where fog begins
uniform float FogEnd = 300.0;     // Distance where fog becomes fully opaque

//Palette Settings
uniform bool Quantize = true;
uniform sampler2D PaletteTexture : source_color;

void vertex(){
	//put this shader on a simple 1x1 quad mesh instance
	POSITION = vec4(VERTEX.xy*2.0, 1.0, 1.0);
}

vec3 quantized_color(vec3 original_color){
	// Palette quantization
    ivec2 palette_size = textureSize(PaletteTexture, 0);
    vec3 quantized_color = vec3(0.0, 1.0, 0.0); //bright debug-green color
    float min_dist = 9999.0;

    // Search through palette texture
    for (int y = 0; y < palette_size.y; y++) {
        for (int x = 0; x < palette_size.x; x++) {
			//get the color of the pixel at the center of each pixel rather than the corner.
            vec2 palette_uv = (vec2(float(x), float(y)) +0.5) / vec2(palette_size);
            vec3 palette_color = textureLod(PaletteTexture, palette_uv, 0.0).rgb;
            float dist = distance(original_color, palette_color);
	      	if (dist < min_dist) {
                min_dist = dist;
            	quantized_color = palette_color;
            }
        }
    }
	return quantized_color;
}

void fragment() {
    // Reconstruct screen-space coordinates
    vec2 frag_coord = FRAGCOORD.xy;
    vec2 screen_pixel_size = 1.0 / VIEWPORT_SIZE;
	
    // Center rotation at fragment center
    float aRad = radians(TwixelAngle);
    mat2 rotation = mat2(
        vec2(cos(aRad), -sin(aRad)),
        vec2(sin(aRad),  cos(aRad))
    );
    
    // Apply rotation and pixelation
    vec2 rotated_coord = rotation * frag_coord;
    vec2 pixelated_coord = round(rotated_coord / PixelSize) * PixelSize;
    vec2 final_coord = transpose(rotation) * pixelated_coord;
	final_coord = clamp(final_coord, vec2(1.0), VIEWPORT_SIZE - vec2(1.0));
    
	//Get the linear depth for fog
	float depth = texture(DepthTexture, final_coord * screen_pixel_size).x;
	vec3 ndc = vec3(final_coord * screen_pixel_size * 2.0 - 1.0, depth);
	vec4 view = INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
	view.xyz /= view.w;
	float linear_depth = -view.z;
	
    // Sample screen texture with NEAREST filtering for crisp pixels
    vec3 original_color = textureLod(ScreenTexture, final_coord * screen_pixel_size, 0.0).rgb;

	//get the fog
	float fog_factor = clamp((linear_depth - FogStart) / (FogEnd - FogStart), 0.0, 1.0);
	
	if(Quantize){
		vec3 quantized = quantized_color(original_color);
	    ALBEDO = mix(quantized, FogColor, fog_factor);	
	}else{
		ALBEDO = mix(original_color, FogColor, fog_factor);
	}
}

Tags
Fog, palette, pixel, pixelate, posterization, Quantization, quantize, Spatial
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

Extensible Color Palette (Gradient Maps) Now with Palette Blending!

Animated 2D Fog(Optional Pixelation)

Outline, Posterization, Color Palette and Dithering

guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments