Skip to content

Instantly share code, notes, and snippets.

@amirrajan
Last active September 5, 2026 06:04
Show Gist options
  • Select an option

  • Save amirrajan/8ba6f10bbe1feab8cb6f543757c762bc to your computer and use it in GitHub Desktop.

Select an option

Save amirrajan/8ba6f10bbe1feab8cb6f543757c762bc to your computer and use it in GitHub Desktop.
DragonRuby Game Toolkit - Rustling leaves via shaders (https://youtu.be/zxmUJXgA57A?si=nbFPZG9a-L5mQr7M)
// this is the main scene (args.outputs)
Texture2D tex_scene : register(t0, space2);
// this is the render target that represents the wind displacement map (args.outputs[:wind])
Texture2D tex_wind : register(t1, space2);
// sampler for the main scene texture
SamplerState sam_scene : register(s0, space2);
// sampler for the wind displacement texture
SamplerState sam_wind : register(s1, space2);
struct Input {
// this is the color of the pixel from the main scene texture
float4 tex_color : COLOR0;
// this is the texture coordinate of the pixel from the main scene texture
float2 tex_coord : TEXCOORD0;
};
struct Output {
// this is the final color of the pixel after applying the wind effect
float4 frag_color : SV_Target;
};
Output main(Input input) {
// for the current pixel, sample the wind displacement texture to get the displacement value
// white means the pixel will move to the left, gray means the pixel will stay in place, and black means the pixel will move to the right
float4 wind_displacement = tex_wind.Sample(sam_wind, input.tex_coord);
float displacement_perc = wind_displacement.r - 0.5;
float displacement_amount = 0.08;
// given the displacement percentage, calculate the new texture coordinate for the pixel
float2 displacement_uv = float2((float)input.tex_coord.x + displacement_perc * displacement_amount,
(float)input.tex_coord.y + displacement_perc * displacement_amount);
// clamp the new texture coordinate to be within the bounds of the texture
float2 resolved_uv = float2(clamp(displacement_uv.x, 0.0, 1.0),
clamp(displacement_uv.y, 0.0, 1.0));
// sample the main scene texture at the new texture coordinate to get the final color of the pixel
Output output;
output.frag_color = tex_scene.Sample(sam_scene, resolved_uv);
return output;
}
# require the shader
require "shaders/leaves.fragment.hlsl"
module Main
# start is invoked when Kernel.tick_count == 0
def start
# represents the wind speed that we should be at
@target_wind_speed = 1.5
# current wind speed
@wind_speed = 1.5
# represents the wind intensity that we should be at
@target_wind_intensity = 1.0
# current wind intensity
@wind_intensity = 1.0
end
def wind_offset
# the noise texture is shifted by the wind speed and tick_count
# we clamp movement to 8 pixels so that the noise texture shifts
# at pixel whole numbers
(Kernel.tick_count * @wind_speed % 1280).ifloor(8)
end
def tick
# every five seconds, compute a new target wind speed and intensity
if Kernel.tick_count.zmod?(300)
@target_wind_speed = Numeric.rand(0..0.2) + 1.5
@target_wind_intensity = rand
end
# lerp the current wind speed and intensity towards the target values
# we do this so that the wind speed and intensity change smoothly over time instead of instantly
@wind_speed = @wind_speed.lerp(@target_wind_speed, 0.01, tolerance: 0)
@wind_intensity = @wind_intensity.lerp(@target_wind_intensity, 0.01, tolerance: 0)
# this represents the displacement map composed of noise
# with a hole punch that represents the area where the leaves are
outputs[:wind].set w: 1280, h: 720, background_color: [128, 128, 128, 255]
# we draw the noise texture twice, once at the current offset and
# once at the offset minus the width of the texture
outputs[:wind].primitives << {
x: 0 + wind_offset, y: 0, w: 1280, h: 1280,
path: "sprites/noise.png",
a: 255 * @wind_intensity
}
outputs[:wind].primitives << {
x: -1280 + wind_offset, y: 0, w: 1280, h: 1280,
path: "sprites/noise.png",
a: 255 * @wind_intensity
}
# then we draw the hole punch over the noise so that
# only the leaves move and the tree remains static
outputs[:wind].primitives << {
x: 640, y: 360, w: 512, h: 512,
anchor_x: 0.5, anchor_y: 0.5,
path: "sprites/leaves-area.png",
}
# for the top level outputs, we set the shader
# and pass in the wind texture as a uniform to the shader
outputs.shader = {
path: "shaders/leaves.fragment.hlsl",
textures: [:wind]
}
# render the scene
outputs.background_color = [30, 30, 30]
outputs.primitives << {
x: 640, y: 360, w: 512, h: 512,
anchor_x: 0.5, anchor_y: 0.5,
path: "sprites/tree.png",
}
# if you want to see what the displacement map looks like,
# you can uncomment this line to render it to the screen
# outputs.primitives << {
# x: 0, y: 0, w: 1280, h: 720, path: :wind
# }
end
end
DR.reset

noise.png

noise

leaves-area.png

leaves-area

tree.png

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