GPU accelerated sprite stacking graphics for the Godot Engine


NIUM gameplay
NIUM. Does not use Shader Stacker but features sprite stacking.

Sprite stacking is a 2.5D technique done by drawing cross sections of a 3D object with slight offset between each layer.

Seen sporatically in many 2D indie games, the main motivation for this project was from seeing NIUM. A demo game by OjiroFumoto and deadlyyucca. The project was ultimately abandoned.

I started developing this plugin in 2020 as a north star for learning how to make shaders. Since then, I have been inspired to get back into game development and will be maintaining the library as I use it myself.


Shader Stacker is made for Godot 4

Grab a copy of /addons/ShaderStacker from the official asset library or by cloning the repo and add it to your project.

Enable the plugin through Project -> Project Settings -> Plugins. You will have to close an reopen your scenes for the changes to take effect.

Reminder that if sprites look blurry or shaded weirdly, import filter has moved in Godot 4. To fix it, set Project -> Project Settings -> General -> Rendering -> Textures -> Canvas Textures -> Default Texture Filter to Nearest.


Preview Tool

Shader Stacker now comes with a tool for live previewing sprite sheets. You can open it by running PreviewTool/PreviewTool.tscn. It will automatically poll files every second for changes.

Shader Stacker preview tool screenshot


Property type Description
sprite_sheet Texture2D Single column sprite sheet
layers int Number of rows in the sprite sheet
z int Dictates how high or low the sprite stack is relative to the "floor"
yaw float Rotates the "up direction"
pitch float Amount of space between layers
static_yaw bool Do not automatically adjust yaw to be towards the top of the viewport
static_z bool Ignore z sorting from any StackCamera nodes in the same viewport


Currently has the same properties as Camera2d.

Gets all Shader Stacker nodes in the same viewport and sorts their z_index. Nodes closer to the top of the screen are drawn first.

You can opt in any node to be z sorted with:

func ready():
    var id = get_viewport().get_viewport_rid().get_id()
    add_to_group("zsort{viewportRID}".format({ "viewportRID": id }))

You may want to set zoom.y = 0.5 or some other fraction of zoom.x. This will "get everything vertically squashed and looking isometric" . As a consequence, all regular 2d nodes will appear squished. Reset2d will counteract this.


Property type Description
reset_rotation bool Inverse rotation transforms from the enabled camera
reset_scale bool Inverse scale transforms from the enabled camera
reset_position bool Must be enabled for z to work. Will overwrite any position value so child this node to a Node2D and position that instead.
z int Number of pixel "above the ground" the children appear to be

Transforms itself such that any child Node2d will look as expected before sprite stacking.

Ex. a Sprite2d alone will look like a floor tile while a child of Reset2d will look like a billboard.

Be careful when moving this node in the editor, as it is very easy to move child nodes by mistake causing z sorting to work unexpectedly.