Skip to content

Instantly share code, notes, and snippets.

@amirrajan
Last active September 12, 2026 07:46
Show Gist options
  • Select an option

  • Save amirrajan/48e843677e18e54365eabbd2dea71328 to your computer and use it in GitHub Desktop.

Select an option

Save amirrajan/48e843677e18e54365eabbd2dea71328 to your computer and use it in GitHub Desktop.
DragonRuby Game Toolkit - Water Ripples (https://youtu.be/NlAjjOEWPiM)
# https://www.youtube.com/watch?v=8rCRsOLiO7k
require "shaders/ripples.fragment.hlsl"
module Main
def start
@ripple_queue = []
end
def tick
if inputs.mouse.click
@ripple_queue << { x: inputs.mouse.x, y: inputs.mouse.y, at: Kernel.tick_count, duration: 120 }
end
@ripple_queue.reject! do |ripple|
ripple.at.elapsed_time(Kernel.tick_count) > ripple.duration
end
outputs[:displacement].set w: 1280, h: 720, background_color: [0, 0, 0, 0]
outputs[:displacement].primitives << scrolling_tiles(
tile_size: 275,
viewport_w: 1280,
viewport_h: 720,
dx: 0.1,
dy: 0.0,
path: "sprites/displacement.png",
tick_count: Kernel.tick_count
)
outputs[:displacement].primitives << @ripple_queue.map do |ripple|
perc = Easing.smooth_stop(start_at: ripple.at,
duration: ripple.duration,
power: 2,
tick_count: Kernel.tick_count)
{
x: ripple.x,
y: ripple.y,
w: 128 * perc,
h: 128 * perc,
path: "sprites/wave-feathered.png",
anchor_x: 0.5,
anchor_y: 0.5,
a: 128 * (1 - perc)
}
end
outputs[:soup].set w: 1280, h: 720, background_color: [0, 0, 0, 0]
outputs[:soup].primitives << scrolling_tiles(
tile_size: 546,
viewport_w: 1280,
viewport_h: 720,
dx: 0.2,
dy: 0.2,
tick_count: Kernel.tick_count,
path: "sprites/soup-left.png",
a: 128
)
outputs[:soup].primitives << scrolling_tiles(
tile_size: 546,
viewport_w: 1280,
viewport_h: 720,
dx: -0.1,
dy: 0.1,
tick_count: Kernel.tick_count,
path: "sprites/soup-right.png",
a: 128
)
outputs[:soup].primitives << @ripple_queue.map do |ripple|
perc = Easing.smooth_stop(start_at: ripple.at,
duration: ripple.duration,
power: 2,
tick_count: Kernel.tick_count)
{
x: ripple.x,
y: ripple.y,
w: 256 * perc,
h: 256 * perc,
path: "sprites/wave-feathered.png",
anchor_x: 0.5,
anchor_y: 0.5,
a: 128 * (1 - perc)
}
end
outputs[:mask].set w: 1280, h: 720, background_color: [0, 0, 0, 0]
outputs[:mask].primitives << { x: 635,
y: 365,
w: 475,
h: 475,
path: "sprites/circle/solid.png",
r: 0, g: 0, b: 0,
anchor_x: 0.5, anchor_y: 0.5 }
outputs[:scene].shader = {
path: "shaders/ripples.fragment.hlsl",
textures: [
:displacement,
:soup,
:mask
],
}
outputs[:scene].set w: 1280, h: 720, background_color: [219, 208, 191]
outputs[:scene].primitives << { x: 640,
y: 360,
w: 933,
h: 700,
path: 'sprites/arena.png',
anchor_x: 0.5,
anchor_y: 0.5 }
outputs.primitives << {
x: 0,
y: 0,
w: 1280,
h: 720,
path: :scene
}
end
def scrolling_tiles(tile_size:, viewport_w:, viewport_h:,
dx:, dy:, tick_count:,
path:, a: 255)
period = tile_size * 2
x_offset = (tick_count * dx) % period
y_offset = (tick_count * dy) % period
cols = viewport_w.fdiv(tile_size).ceil + 2
rows = viewport_h.fdiv(tile_size).ceil + 2
cols.map do |cx|
rows.map do |cy|
{
x: cx * tile_size + x_offset - period,
y: cy * tile_size + y_offset - period,
w: tile_size,
h: tile_size,
path: path,
flip_horizontally: cx.even?,
flip_vertically: cy.even?,
a: a
}
end
end
end
end
DR.reset
Texture2D scene : register(t0, space2);
Texture2D displacement : register(t1, space2);
Texture2D soup : register(t2, space2);
Texture2D mask : register(t3, space2);
SamplerState sampler_scene : register(s0, space2);
SamplerState sampler_displacement : register(s1, space2);
SamplerState sample_soup : register(s2, space2);
SamplerState sampler_mask : register(s3, space2);
struct Input {
float4 tex_color : COLOR0;
float2 tex_coord : TEXCOORD0;
};
struct Output {
float4 frag_color : SV_Target;
};
Output main(Input input) {
Output output;
float4 displacement_magnitude = displacement.Sample(sampler_displacement, input.tex_coord);
float displacement_perc = 0.06;
float2 displacement_uv = float2(
input.tex_coord.x + (displacement_magnitude.r * displacement_perc - displacement_perc / 2.0),
input.tex_coord.y + (displacement_magnitude.r * displacement_perc - displacement_perc / 2.0)
);
float2 resolved_uv = float2(
clamp(displacement_uv.x, 0.0, 1.0),
clamp(displacement_uv.y, 0.0, 1.0)
);
float4 soup_level = soup.Sample(sample_soup, input.tex_coord);
float4 should_mask = mask.Sample(sampler_mask, input.tex_coord);
if (should_mask.r < 0.5 && should_mask.a > 0.5) {
if (soup_level.r > 0.30) {
float highlight_alpha = 0.65;
float4 base_color = scene.Sample(sampler_scene, resolved_uv) * input.tex_color;
output.frag_color = float4(lerp(base_color.rgb,
float3(1.0, 1.0, 1.0),
highlight_alpha),
base_color.a);
} else {
output.frag_color = scene.Sample(sampler_scene, resolved_uv) * input.tex_color;
}
} else {
output.frag_color = scene.Sample(sampler_scene, input.tex_coord) * input.tex_color;
}
return output;
}

soup-left.png

soup-left

soup-right.png

soup-right

ripple.png

ripple

arena.png

arena
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment