Skip to content

Instantly share code, notes, and snippets.

@Rahkeen

Rahkeen/Rainy.kt Secret

Created December 16, 2023 17:47
Show Gist options
  • Save Rahkeen/9d1deda69e56f45fe5db22e179b48d5e to your computer and use it in GitHub Desktop.
Save Rahkeen/9d1deda69e56f45fe5db22e179b48d5e to your computer and use it in GitHub Desktop.
Art of Code Rainy Window - Adapted for Android
@Language("AGSL")
val rainyWindow = """
uniform float2 resolution;
uniform float time;
uniform shader composable;
const float twopi = 6.2831;
// smoothstep alias
float S(float a, float b, float t) {
return smoothstep(a, b, t);
}
// pseudo random value, takes a point and returns a float
float N21(float2 p) {
p = fract(p*float2(123.34, 345.45));
p += dot(p, p+34.345);
return fract(p.x*p.y);
}
half4 main(float2 coord) {
// initializing things
float2 uv = coord.xy / resolution.y;
float2 uv0 = uv;
half3 color = half3(0.0);
float t = mod(time, 7200);
// gives us longer rectangles to work with
float2 aspect = float2(2, 1);
// invert y coordinates to follow along better
uv.y = 1.0 - uv.y;
// increase the range of values so that we can make a grid
uv *= 3.0 * aspect;
// globally moves coords down, this will conteract the oscillating of the
float yShift = t * 0.25;
uv.y += yShift;
// create the grid
float2 grid_pos = fract(uv) - 0.5;
float2 grid_id = floor(uv);
float n = N21(grid_id); // random value from 0..2pi
t += n * twopi;
// creating the horizontal movement
float w = uv0.y * 10;
float x = (n - 0.5) * 0.8; // random value from -0.4..0.4
// function for the drop wiggling as it goes down
// if the drop is close to the edge of its cell, it will wiggle less.
x += (0.4 - abs(x)) * sin(3.0 * w) * pow(sin(w), 6.0) * 0.45;
// creating the vertical movement
// this moves the drop down
float y = -sin(t + sin(t + sin(t) * 0.5)) * 0.45;
// this helps make the drop shape by distorting the y
float dropShape = dot(grid_pos.x - x, grid_pos.x - x);
y -= dropShape;
// place the drop
float2 dropPos = (grid_pos-float2(x,y)) / aspect;
float drop = S(0.05, 0.03, length(dropPos));
// place the trail of drops
float2 trailPos = (grid_pos-float2(x,yShift)) / aspect;
trailPos.y = (fract(trailPos.y * 8.0) - 0.5) / 8.0;
float trail = S(0.03, 0.01, length(trailPos));
// makes sure that drops appear above main drop only
trail *= S(-0.05, 0.05, dropPos.y);
// fade out the drops
trail *= S(0.5, y, grid_pos.y);
color += trail;
color += drop;
// using out water drop mask as a texture on the original component
float2 offset = float2(drop*dropPos + trail*trailPos);
offset *= resolution;
// comment this out to see just the texture
color = composable.eval(coord+(offset*-5)).rgb;
// uncomment this show the grid lines
// if (grid_pos.x > 0.48 || grid_pos.y > 0.49) color = half3(1.0, 0.0, 0.0);
return half4(color, 1.0);
}
""".trimIndent()
@Preview
@Composable
fun RainyWindowPreview() {
// a simple timer that can drive the animation
var time by remember { mutableStateOf(0f) }
LaunchedEffect(Unit) {
do {
withFrameMillis {
time += 0.01f
}
} while (true)
}
// setup our shader to use
val shader = remember { RuntimeShader(rainyWindow) }
// replace this with a Box or use any image of your choice
Image(
modifier = Modifier
.graphicsLayer {
with(shader) {
setFloatUniform(
"resolution",
size.width,
size.height
)
setFloatUniform(
"time",
time
)
}
renderEffect = RenderEffect
.createRuntimeShaderEffect(
shader,
"composable"
)
.asComposeRenderEffect()
}
.fillMaxWidth()
.aspectRatio(1f),
painter = painterResource(id = R.drawable.bird),
contentScale = ContentScale.Crop,
contentDescription = "Bird"
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment