VFX Frame Interpolator (with Gradient Tint)

A simple shader that allows you to smoothly interpolate animation frames in a sprite sheet. This makes your 16 frame VFX look smooth! As a bonus, the ability to use a gradient has also been added! 

Hint: To get the best results, use a gradient where the alpha is 0 at the start and set your desired color at the end. The red channel of the sprite sheet mask will then map to the gradient.

Shader code
shader_type canvas_item;


uniform int frames = 4; // Number of frames in the sprite sheet.
uniform float animation_progress : hint_range(0.0, 1.0, 0.001) = 0.0; // Animation progress (0.0 to 1.0).
uniform sampler2D sprite_sheet; // Sprite sheet containing animation frames.
uniform sampler2D gradient : source_color; // Gradient for final color lookup.
uniform float y_offset = 0.0; // Vertical UV offset for the sprite sheet.
uniform float y_size = 1.0; // Vertical UV scale for the sprite sheet.


// Creates a smooth S-curve
float quadratic_ease_in_out(float t) {
    t = clamp(t, 0.0, 1.0);
    // Formula: 2t^2 for first half, 1 - 2(1-t)^2 for second half.
    return t < 0.5 ? 2.0 * t * t : 1.0 - 2.0 * pow(1.0 - t, 2.0);
}

void fragment() {
    vec2 uv = UV;

    float frame_width = 1.0 / float(frames); // Width of a single frame.
    float timeline_pos = animation_progress * float(frames); // Position on the animation timeline.

    // Determine current and next frame indices.
    int current_frame_idx = int(floor(timeline_pos));
    int next_frame_idx = int(ceil(timeline_pos));

    // Clamp frame indices to valid range.
    current_frame_idx = clamp(current_frame_idx, 0, frames - 1);
    next_frame_idx = clamp(next_frame_idx, 0, frames - 1);

    // Calculate X-start for current and next frames.
    float current_frame_x_start = float(current_frame_idx) * frame_width;
    float next_frame_x_start = float(next_frame_idx) * frame_width;

    // Apply vertical offset and scale to UV.
    uv.y *= y_size;
    uv.y += y_offset;

    // Calculate full UVs for sampling from sprite sheet.
    vec2 current_uv = vec2(current_frame_x_start + uv.x * frame_width, uv.y);
    vec2 next_uv = vec2(next_frame_x_start + uv.x * frame_width, uv.y);

    // Sample colors from sprite sheet for current and next frames.
    vec4 color_current = texture(sprite_sheet, current_uv);
    vec4 color_next = texture(sprite_sheet, next_uv);

    // Interpolation factor based on the fractional part of timeline position.
    float linear_t = fract(timeline_pos);

    // Apply eased interpolation.
    float eased_t = quadratic_ease_in_out(linear_t);

    // Interpolate between frame colors.
    vec4 interpolated_color = mix(color_current, color_next, eased_t);

    // Use the red channel of the interpolated color to sample from the gradient.
    float sample_channel = interpolated_color.r;
    vec4 final_color = texture(gradient, vec2(sample_channel, 0.0));

    COLOR = final_color;
}
Live Preview
Tags
animation, Color, fx, gradient, smooth, vfx
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.

More from EmberNoGlow

Related shaders

guest

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
binbun
18 days ago

Thanks for this. Might come handy