Skip to content

Instantly share code, notes, and snippets.

@w0wca7a
Last active April 15, 2025 09:04
Show Gist options
  • Select an option

  • Save w0wca7a/355b73d2956540e038ce347f31397ee6 to your computer and use it in GitHub Desktop.

Select an option

Save w0wca7a/355b73d2956540e038ce347f31397ee6 to your computer and use it in GitHub Desktop.
Gorgeous rain on glass shader for Stride game engine
// Copyright (c) Stride contributors (https://stride3d.net).
// Distributed under the MIT license.
using Stride.Engine;
using Stride.Graphics;
namespace Additionals.Shader.ShadertoyExam
{
[Display("RainDrops effect")]
[ComponentCategory("Effects")]
public class RainEffect : StartupScript
{
public Texture Texture { get; set; }
public override void Start()
{
base.Start();
var model = Entity.Components.Get<ModelComponent>();
model.Materials[0].Passes[0].Parameters.Set(RainEffectKeys.Texture, this.Texture);
}
}
}
// original shader https://www.shadertoy.com/view/M3G3zV
// Copyright (c) Stride contributors (https://stride3d.net).
// Distributed under the MIT license.
namespace Additionals.Shader.ShadertoyExam
{
shader RainEffect<bool CheapNormals, bool UsePostProcessing, bool UseZoom, bool UseBlur> : Texturing, ComputeColor, ShaderBase
{
// Текстура / texture
rgroup PerMaterial
{
stage Texture2D Texture; // Основная текстура
}
compose ComputeColor Texture;
// Вспомогательные функции шума / Noises generators
float3 N13(float p)
{
float3 p3 = frac(float3(p) * float3(.1031f, .11369f, .13787f));
p3 += dot(p3, p3.yzx + 19.19f);
return frac(float3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x));
}
float4 N14(float t)
{
return frac(sin(t*float4(123.f, 1024.f, 1456.f, 264.f))*float4(6547.f, 345.f, 8799.f, 1564.f));
}
float N(float t)
{
return frac(sin(t*12345.564f)*7658.76f);
}
float Saw(float b, float t)
{
return smoothstep(0.f, b, t)*smoothstep(1.f, b, t);
}
// Функции генерации капель
float2 DropLayer2(float2 uv, float t)
{
float2 UV = uv;
uv.y += t*0.75f;
float2 a = float2(6.f, 1.f);
float2 grid = a*2.f;
float2 id = floor(uv*grid);
float colShift = N(id.x);
uv.y += colShift;
id = floor(uv*grid);
float3 n = N13(id.x*35.2f + id.y*2376.1f);
float2 st = frac(uv*grid) - float2(.5f, 0.f);
float x = n.x - .5f;
float y = UV.y*20.f;
float wiggle = sin(y + sin(y));
x += wiggle*(.5f - abs(x))*(n.z - .5f);
x *= .7f;
float ti = frac(t + n.z);
y = (Saw(.85f, ti) - .5f)*.9f + .5f;
float2 p = float2(x, y);
float d = length((st - p)*a.yx);
float mainDrop = smoothstep(.4f, .0f, d);
float r = sqrt(smoothstep(1.f, y, st.y));
float cd = abs(st.x - x);
float trail = smoothstep(.23f*r, .15f*r*r, cd);
float trailFront = smoothstep(-.02f, .02f, st.y - y);
trail *= trailFront*r*r;
y = UV.y;
float trail2 = smoothstep(.2f*r, .0f, cd);
float droplets = max(0.f, (sin(y*(1.f - y)*120.f) - st.y))*trail2*trailFront*n.z;
y = frac(y*10.f) + (st.y - .5f);
float dd = length(st - float2(x, y));
droplets = smoothstep(.3f, 0.f, dd);
float m = mainDrop + droplets*r*trailFront;
return float2(m, trail);
}
float StaticDrops(float2 uv, float t)
{
uv *= 40.f;
float2 id = floor(uv);
uv = frac(uv) - .5f;
float3 n = N13(id.x*107.45f + id.y*3543.654f);
float2 p = (n.xy - .5f)*.7f;
float d = length(uv - p);
float fade = Saw(.025f, frac(t + n.z));
float c = smoothstep(.3f, 0.f, d)*frac(n.z*10.f)*fade;
return c;
}
float2 Drops(float2 uv, float t, float l0, float l1, float l2)
{
float s = StaticDrops(uv, t)*l0;
float2 m1 = DropLayer2(uv, t)*l1;
float2 m2 = DropLayer2(uv*1.85f, t)*l2;
float c = s + m1.x + m2.x;
c = smoothstep(.3f, 1.f, c);
return float2(c, max(m1.y*l0, m2.y*l1));
}
// Основная функция шейдера
override float4 Compute()
{
// Получаем координаты
float2 uv = streams.TexCoord.xy;
float2 center = float2(0.5, 0.5);
uv -= center;
float2 UV = streams.TexCoord.xy;
// Управление временем и параметрами
float T = Global.Time;
float t = T * 0.2f;
// Интенсивность дождя
float rainAmount = sin(T * 0.05f) * 0.3f + 0.7f;
// Настройки размытия
float maxBlur = 0.0f;
float minBlur = 0.0f;
if (UseBlur)
{
maxBlur = lerp(3.f, 6.f, rainAmount);
minBlur = 2.0f;
}
// Эффект приближения
if (UseZoom)
{
float zoom = -cos(T * 0.2f);
uv *= 0.7f + zoom * 0.3f;
UV = (UV - 0.5f) * (0.9f + zoom * 0.1f) + 0.5f;
}
// Генерация капель
float staticDrops = smoothstep(-0.5f, 1.f, rainAmount) * 2.f;
float layer1 = smoothstep(0.25f, 0.75f, rainAmount);
float layer2 = smoothstep(0.f, 0.5f, rainAmount);
float2 c = Drops(uv, t, staticDrops, layer1, layer2);
// Вычисление нормалей
float2 n;
if (CheapNormals)
{
n = float2(ddx(c.x), ddy(c.x)); // Быстрые нормали
}
else
{
float2 e = float2(0.001f, 0.f);
float cx = Drops(uv + e, t, staticDrops, layer1, layer2).x;
float cy = Drops(uv + e.yx, t, staticDrops, layer1, layer2).x;
n = float2(cx - c.x, cy - c.x); // Качественные нормали
}
// Применение эффектов к текстуре
float focus = lerp(maxBlur - c.y, minBlur, smoothstep(0.1f, 0.2f, c.x));
float4 col = Texture.Sample(LinearSampler, UV + n, focus);
//float4 col = Texture.Compute().a + focus;
// Постобработка
if (UsePostProcessing)
{
t = (T + 3.f) * 0.5f;
float colFade = sin(t * 0.2f) * 0.5f + 0.5f;
col.rgb *= lerp(float3(1.f), float3(0.8f, 0.9f, 1.3f), colFade);
float fade = smoothstep(0.f, 10.f, T);
float lightning = sin(t * sin(t * 10.f));
lightning *= pow(max(0.f, sin(t + sin(t))), 10.f);
col.rgb *= 1.f + lightning * fade;
col.rgb *= 1.f - dot(UV - 0.5f, UV - 0.5f);
col.rgb *= fade;
}
return col;
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment