PS1 Shader
This is a shader trying to emulate the look of a PS1 game. The PS1 graphics pipeline took a lot of shortcuts, for example it used exclusively integers.
Simply apply it to any mesh. Best used with a texture with disabled filtering.
Uniforms:
Jitter – The amount of polygon “jitter”, higher value means more intensive jittering.
Jitter Z Coordinate – If disabled, the Z axis (depth) of a vertex isn’t affected. Can avoid some clipping.
Jitter Depth Independent – If disabled, makes further objects jitter less, basically “scaling” the jitter with distance.
Affine Texture Mapping – If disabled, gets rid of the “weird floaty textures” effect at oblique angles. PS1 developers counteracted this by subdividing geometry, so you should too.
Shader code
shader_type spatial;
render_mode vertex_lighting, skip_vertex_transform,
specular_phong, diffuse_lambert_wrap;
uniform sampler2D albedo: hint_black_albedo;
uniform sampler2D specular: hint_black;
uniform sampler2D emission: hint_black;
uniform float jitter: hint_range(0.0, 1.0) = 0.5;
uniform bool jitter_z_coordinate = true;
uniform bool jitter_depth_independent = true;
uniform bool affine_texture_mapping = true;
uniform float alpha_scissor: hint_range(0.0, 1.0) = 1.0;
void vertex() {
VERTEX = (MODELVIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
float z_orig = VERTEX.z;
float i = (1.0 - jitter) * min(VIEWPORT_SIZE.x, VIEWPORT_SIZE.y) / 2.0;
if (jitter_depth_independent) {
float w = (PROJECTION_MATRIX * vec4(VERTEX, 1.0)).w;
VERTEX = round(VERTEX / w * i) / i * w;
} else {
VERTEX = round(VERTEX * i) / i;
}
if (!jitter_z_coordinate) {
VERTEX.z = z_orig;
}
if (affine_texture_mapping) {
UV *= VERTEX.z;
}
}
void fragment() {
vec2 uv = UV;
if (affine_texture_mapping) {
uv /= VERTEX.z;
}
ALBEDO = texture(albedo, uv).rgb;
ALPHA = texture(albedo, uv).a;
ALPHA_SCISSOR = alpha_scissor;
EMISSION = texture(emission, uv).rgb;
SPECULAR = texture(specular, uv).r;
}
Do you plan to add UV scaling? Such as tiling a floor with a texture on repeat? (I just finished implementing this myself but was wondering if you had plans to make your own updates to further long this shader!)
A quick heads up, though the vertex effect is great it does NOT transform normals, they will remain in local space.
Add:
NORMAL = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);
into the vertex function to fix this
hey ive been trying to use this shader but its not tiling my textures so im not able to use this!
Howdy, I used this shader extensively in my game, Synesthesia. It only appears in the desktop versions due to graphical constraints. It was vital to forming the aesthetic for the game. Thank you for sharing this shader with the community.
Here’s a trailer if anyone wants to see the shader in action: https://www.youtube.com/watch?v=v2BZei_-wUs
It does not work. its just black for me.
If you want to control the texture warping intensity you can make these changes:
Add this line to the variables:
Change line 33 to:
Change line 41 to:
With this you will be able to control the texture warping intensity from 0 (no distortion), to -2 (full distortion).
Hey Duke, thanks for the shader. Is this supposed to work with the 4.0 releases of Godot? When trying to use it I get
Invalid render mode: specular_phong
. I’ve tried other modes but then there are other errors I’m not sure how to correct.Not sure about the ALPHA_SCISSOR_THRESHOLD but at least this should get you going:
shader_type spatial;
render_mode vertex_lighting, skip_vertex_transform,
diffuse_lambert_wrap;
uniform sampler2D albedo: hint_default_black;
uniform sampler2D specular: hint_default_black;
uniform sampler2D emission: hint_default_black;
uniform float jitter: hint_range(0.0, 1.0) = 0.5;
uniform bool jitter_z_coordinate = true;
uniform bool jitter_depth_independent = true;
uniform bool affine_texture_mapping = true;
uniform float alpha_scissor: hint_range(0.0, 1.0) = 1.0;
void vertex() {
VERTEX = (MODELVIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
float z_orig = VERTEX.z;
float i = (1.0 – jitter) * min(VIEWPORT_SIZE.x, VIEWPORT_SIZE.y) / 2.0;
if (jitter_depth_independent) {
float w = (PROJECTION_MATRIX * vec4(VERTEX, 1.0)).w;
VERTEX = round(VERTEX / w * i) / i * w;
} else {
VERTEX = round(VERTEX * i) / i;
}
if (!jitter_z_coordinate) {
VERTEX.z = z_orig;
}
if (affine_texture_mapping) {
UV *= VERTEX.z;
}
}
void fragment() {
vec2 uv = UV;
if (affine_texture_mapping) {
uv /= VERTEX.z;
}
ALBEDO = texture(albedo, uv).rgb;
ALPHA = texture(albedo, uv).a;
ALPHA_SCISSOR_THRESHOLD = alpha_scissor;
EMISSION = texture(emission, uv).rgb;
SPECULAR = texture(specular, uv).r;
}