2.5d sprite shader
Reproduces the behaviour of 2.5D/Doom-style sprites inside a shader.
The shader is designed to minimise the work done in scripting that’s usually required with this sort of thing in godot.
The other benefit is you can see the effect in-editor.
The shader is set up so the angular sprites are stacked horizontally, with the animation frames stacked vertically
The “frame” instance uniform can be used to change the animation from by offsetting the UV Y coordinate. Because you can animate everything in godot, you can animate your sprite by changing the frame value.
Updates provided in part thanks to MrTriPie’s comments.
Updates:
- Y billboard option bool
- alpha option bool
- scale uniform
Shader code
//By Kyra July 2023. Updated December 2023
//Tested in Godot 4.2
//Made with help from Blackle Mori at suricrasia.online and MrTriPie's commnets on GDshaders
shader_type spatial;
uniform sampler2D albedo;
uniform int directions = 8;
uniform int frame_count = 5;
uniform int frame = 0;
uniform float scale = 1.0;
uniform bool y_billboard;
uniform bool use_alpha;
void vertex() {
if(y_billboard){
MODELVIEW_MATRIX = VIEW_MATRIX * mat4(
vec4(normalize(cross(vec3(0.0, 1.0, 0.0), INV_VIEW_MATRIX[2].xyz)), 0.0),
vec4(0.0, 1.0, 0.0, 0.0),
vec4(normalize(cross(INV_VIEW_MATRIX[0].xyz, vec3(0.0, 1.0, 0.0))), 0.0),
MODEL_MATRIX[3]);
}else{
MODELVIEW_MATRIX = VIEW_MATRIX * mat4(INV_VIEW_MATRIX[0], INV_VIEW_MATRIX[1], INV_VIEW_MATRIX[2], MODEL_MATRIX[3]);
MODELVIEW_NORMAL_MATRIX = mat3(MODELVIEW_MATRIX);
}
VERTEX *= scale;
vec3 direction_to_camera = normalize(CAMERA_POSITION_WORLD-NODE_POSITION_WORLD);
float angle_to_camera = atan(direction_to_camera.x,direction_to_camera.z);
vec4 model_direction = MODEL_MATRIX*vec4(1.0,0.0,0.0,0.0);
float y_angle = atan(model_direction.x,model_direction.z)-PI;
float final_angle = angle_to_camera-y_angle;
float int_y_angle = round((final_angle / TAU) * float(directions)) / float(directions);
UV.y += float(frame);
UV /= vec2(float(directions), float(frame_count));
UV.x += int_y_angle;
}
void fragment() {
vec4 albedo_tex = texture(albedo,UV);
if(use_alpha){
ALPHA_SCISSOR_THRESHOLD = 0.5;
ALPHA = albedo_tex.a;
}
ALBEDO = albedo_tex.rgb;
}
I had problems with it working correctly on a different amount of directions than 8
I think changing this line:
to this fixes it:
If you want the billboarding to be constrained to the Y axis, you can replace these lines:
With these (copied from Godot’s StandardMaterial converted to ShaderMaterial):
I’ve updated the shader based on your suggestions
is there a way to chnage the size?
I’ve updated the shader to include a scale value
I was getting some funky colors but adding an export hint to the albedo fixed it.
uniform sampler2D albedo: source_color;