PS1 Post-processing

This shader is meant to be used with the PS1 Shader. It gives your game that PS1 dithered look. Works in editor, too!

Usage:
Make a MeshInstance, give it a QuadMesh, make the size 2 by 2, set the Extra Cull Margin to the highest possible value, apply a material with this shader to it.

Known issues:
Doesn’t support transparent objects.

Uniforms:
Color Depth – Reduce color depth to X bits per color. The PS1 supported many modes, some of them being 24 bit (8 bits per color) and 15 bit (5 bits per color), the latter (5 bits per color) used in majority of 3D games and being the default.
Dithering – Apply the “dotted” look that gives an illusion of more colors.
Resolution Scale – Reduce screen resolution.

Shader code
shader_type spatial;
render_mode unshaded, shadows_disabled, depth_test_disable, depth_draw_never;

uniform int color_depth : hint_range(1, 8) = 5;
uniform bool dithering = true;
uniform int resolution_scale = 4;

int dithering_pattern(ivec2 fragcoord) {
	const int pattern[] = {
		-4, +0, -3, +1, 
		+2, -2, +3, -1, 
		-3, +1, -4, +0, 
		+3, -1, +2, -2
	};
	
	int x = fragcoord.x % 4;
	int y = fragcoord.y % 4;
	
	return pattern[y * 4 + x];
}

void vertex() {
	POSITION = vec4(VERTEX, 1.0);
}

void fragment() {
	ivec2 uv = ivec2(FRAGCOORD.xy / float(resolution_scale));
	vec3 color = texelFetch(SCREEN_TEXTURE, uv * resolution_scale, 0).rgb;
	
	// Convert from [0.0, 1.0] range to [0, 255] range
	ivec3 c = ivec3(round(color * 255.0));
	
	// Apply the dithering pattern
	if (dithering) {
		c += ivec3(dithering_pattern(uv));
	}
	
	// Truncate from 8 bits to color_depth bits
	c >>= (8 - color_depth);

	// Convert back to [0.0, 1.0] range
	ALBEDO = vec3(c) / float(1 << color_depth);
}
Tags
ps1, retro
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 Mighty Duke

PS1 Shader

Mandelbrot Set

Related shaders

Bloom post processing for viewports

PS1 Shader

Post Effect Outline Shader

guest
6 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Zorochase
Zorochase (@zorochase)
9 months ago

Awesome shader! One problem though, it doesn’t support transparency at all. Transparent objects disappear at certain angles and are not affected by the reduced resolution and dithering.

Last edited 9 months ago by Zorochase
Cyanide
Cyanide
9 months ago

It works, but I’m not convinced 8 bit color is the mode used by most PS1 games, least of all Metal Gear Solid.

https://en.wikipedia.org/wiki/PlayStation_technical_specifications

  • Mode 4: 4-bit CLUT (16 colors)
  • Mode 8: 8-bit CLUT (256 colors)
  • Mode 15: 15-bit direct (32,768 colors)
  • Mode 24: 24-bit (16,777,216 colors)

I suspect Mode 15 was the most common.

Transparency could work with with this if it was suface shader rather than a post process shader.

https://menacingmecha.itch.io/godot-psx-style-demo

Here is someone else’s attempt at it – it has many problems of its own, which I’ve been trying to fix, but it does support transparency.

freddles
freddles
8 months ago

any chance of a GLES2 compatible version? it’s kinda weird to use a higher-quality renderer to make my graphics look worse

Exuin
Exuin (@exuin)
8 months ago

Wait, this isn’t a canvasitem shader…