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.

Shader code
//By Kyra July 2023
//Tested in Godot 4.1
//Made with help from Blackle Mori at suricrasia.online

shader_type spatial;

uniform sampler2D albedo;

uniform int directions = 8; //The number of direction frames on the sheet

uniform int frame_count = 1; //The number of animation frames on the sheet

instance uniform int frame = 0; //The animation frame to display

void vertex() {
    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);
    vec3 direction_to_camera = normalize(CAMERA_POSITION_WORLD-NODE_POSITION_WORLD);
    float angle_to_camera_y = 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_y = angle_to_camera_y-y_angle;
    float int_y_angle = float(2+int(((final_angle_y)/TAU)*9.0))/8.0;
    float final_frame = float( frame ) / float( frame_count );
    UV /= vec2( float( directions ), float( frame_count ) );
    UV += vec2( int_y_angle, final_frame );
}

void fragment() {
    vec4 albedo_tex = texture(albedo,UV);
    ALPHA_SCISSOR_THRESHOLD = 0.5;
    ALPHA = albedo_tex.a;
    ALBEDO = albedo_tex.rgb;
}
Tags
2.5d, 2d, 3d, doom, 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.

Related shaders

2d sprite based vfx gradient shader

Rotating Billboard Sprite 3D

Basic Vector Sprite Upscaling

Subscribe
Notify of
guest

3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
MrTriPie
MrTriPie
1 month ago

I had problems with it working correctly on a different amount of directions than 8

I think changing this line:

float int_y_angle = float(2+int(((final_angle_y)/TAU)*9.0))/8.0;

to this fixes it:

float int_y_angle = round((final_angle_y/TAU)*float(directions))/float(directions);
MrTriPie
MrTriPie
1 month ago

If you want the billboarding to be constrained to the Y axis, you can replace these lines:

 MODELVIEW_MATRIX = VIEW_MATRIX * mat4(
        INV_VIEW_MATRIX[0], INV_VIEW_MATRIX[1], INV_VIEW_MATRIX[2], MODEL_MATRIX[3]);

With these (copied from Godot’s StandardMaterial converted to ShaderMaterial):

    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]
    );
AncientStoneStudios
AncientStoneStudios
1 month ago

is there a way to chnage the size?