Optimizing shaders with spritesheets: do they sample the entire texture?

Godot Version



I have a Sprite3D with character visuals that has a (color replacement) shader constantly applied to it. Previously I was using an AnimatedSprite, but I’m trying to move to an AnimationPlayer + AnimationTree combo to have more flexibility with animations. The image itself is contained in a spritesheet, the frame selection is done with a built-in “frame/hframes/vframes” property. So far so good.

The shader works by sampling the original texture:

# in script
shader_material.set_shader_parameter("sprite_texture", texture)

//in shader
uniform sampler2D sprite_texture : source_color, filter_nearest;
vec4 tex = texture(sprite_texture, UV);

My concern is this: looks like the “texture” parameter is the entire span of the spritesheet that doesn’t take “hframes/vframes” into account. So I’m essentially sampling a huge picture all the time. Is this optimal and/or scalable? If I add more animations (=more frames) to that sheet, will it become costly? And if so, is there a way for the shader to sample only the current frame?


It will sample the UV coordinates. 4K textures are used many times per frame in many games, how big are your sprite sheets?

Each frame is 140x192, but I’ve been thinking of upscaling them to 384 tall because I don’t quite like the current resolution quality (the original PSDs allow for that and more, they are huge). This will make a sheet 2240 wide and 1146 tall for the walking animation alone, and taller once I add idle/action/etc. If it’s only the UVs that are being sampled, then this should be fine, thanks!

The shader definitely ignores the hframes / vframes, that’s just a Godot helper feature!

I’m guessing the engine samples the spritesheets in some sort of an internal shader exactly how you described. It takes the entire texture and renders just a portion of it with UV coordinates. I think there is no need to worry :slight_smile: You probably don’t need to write a shader for this though. The engine provides this spritesheet-frame functionality out of the box.

1 Like

Thanks for the reply! The shader is for recoloring the character (think red team/blue team), not for selecting frames; it’s its interaction with the “out of the box” framing that I was concerned about. If, like gertkeno stated, the shader’s access to the entire texture doesn’t mean that it will do extra work outside of the UVs, then I guess it’s all fine.

You can color the sprites with the modulate property on all Node2Ds. Maybe it will do the job for you so you don’t have to write shaders :slight_smile:

I don’t think it’s doing any extra work exactly. The GPUs are a little weird in this regard. :thinking: I think it applies the shader to the entire texture anyway, because it’s actually faster - this is what they are made for! The engine would do this anyway, so don’t worry about it.

Huh. So there are two conflicting answers so far. If it’s applied to the entire texture, I should probably consider keeping different animations in different spritesheets to reduce the load?

As for the shader itself, I’m afraid I still need it - but not to worry, it’s already written and it works fine :slight_smile: My concern here is scalability: previously I’ve been using get_frame_texture() with an AnimatedSprite, and a shift to the Sprite + AnimationPlayer combo means that I no longer have that method - thus the question.

I think they mean the modulate would apply to the entire texture, which could be true. Unless you are sampling more than your UV the shader will not access that data; it will be in GPU memory but this is unavoidable and probably what you want if your character will change from walking to idle quickly and often without lag.

You will have to make your own uniform for hframes/vframes and current_frame.


My answer is still - it’s fine, don’t worry about it. Modern games may sample multiple 4k textures (albedo, normal, bump, …) just to render a single 3D model. I’m sure your spritesheets will be fine :slight_smile:

I’d only split them if you reach some crazy filesize, like hundreds of megabytes or something

1 Like