-
Notifications
You must be signed in to change notification settings - Fork 15
/
Raindrops on Glass.sksl
418 lines (312 loc) · 16.4 KB
/
Raindrops on Glass.sksl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
uniform float iTime;
uniform float2 iResolution;
uniform shader iImage1;
// Constants
const float RandomSeed = 4.3315;
// Number Scale Of Static Raindrops On Same Screen, Range: [0.0, 1.0]
const float NumberScaleOfStaticRaindrops = 0.35;
const float NumberScaleOfRollingRaindrops = 0.35;
const float RaindropBlur = 0.0;
const float BackgroundBlur = 2.0;
const float StaticRaindropUVScale = 20.0;
const float RollingRaindropUVScaleLayer01 = 2.25;
const float RollingRaindropUVScaleLayer02 = 2.25;
//
//////////////////// 3D OpenSimplex2S noise with derivatives ////////////////////
//////////////////// Output: float4(dF/dx, dF/dy, dF/dz, value) ////////////////////
// Permutation polynomial hash credit Stefan Gustavson
float4 permute(float4 t) {
return t * (t * 34.0 + 133.0);
}
// Gradient set is a normalized expanded rhombic dodecahedron
float3 grad(float hash) {
// Random vertex of a cube, +/- 1 each
float3 cube = mod(floor(hash / float3(1.0, 2.0, 4.0)), 2.0) * 2.0 - 1.0;
// Random edge of the three edges connected to that vertex
// Also a cuboctahedral vertex
// And corresponds to the face of its dual, the rhombic dodecahedron
float3 cuboct = cube;
float hashIndex = hash / 16.0;
if (hashIndex < 1.0) {
cuboct.x = 0.0;
} else if (hashIndex < 2.0) {
cuboct.y = 0.0;
} else {
cuboct.z = 0.0;
}
// In a funky way, pick one of the four points on the rhombic face
float type = mod(floor(hash / 8.0), 2.0);
float3 rhomb = (1.0 - type) * cube + type * (cuboct + cross(cube, cuboct));
// Expand it so that the new edges are the same length
// as the existing ones
float3 grad = cuboct * 1.22474487139 + rhomb;
// To make all gradients the same length, we only need to shorten the
// second type of vector. We also put in the whole noise scale constant.
// The compiler should reduce it into the existing floats. I think.
grad *= (1.0 - 0.042942436724648037 * type) * 3.5946317686139184;
return grad;
}
// BCC lattice split up into 2 cube lattices
float4 os2NoiseWithDerivativesPart(float3 X) {
float3 b = floor(X);
float4 i4 = float4(X - b, 2.5);
// Pick between each pair of oppposite corners in the cube.
float3 v1 = b + floor(dot(i4, float4(.25)));
float3 v2 = b + float3(1, 0, 0) + float3(-1, 1, 1) * floor(dot(i4, float4(-.25, .25, .25, .35)));
float3 v3 = b + float3(0, 1, 0) + float3(1, -1, 1) * floor(dot(i4, float4(.25, -.25, .25, .35)));
float3 v4 = b + float3(0, 0, 1) + float3(1, 1, -1) * floor(dot(i4, float4(.25, .25, -.25, .35)));
// Gradient hashes for the four vertices in this half-lattice.
float4 hashes = permute(mod(float4(v1.x, v2.x, v3.x, v4.x), 289.0));
hashes = permute(mod(hashes + float4(v1.y, v2.y, v3.y, v4.y), 289.0));
hashes = mod(permute(mod(hashes + float4(v1.z, v2.z, v3.z, v4.z), 289.0)), 48.0);
// Gradient extrapolations & kernel function
float3 d1 = X - v1;
float3 d2 = X - v2;
float3 d3 = X - v3;
float3 d4 = X - v4;
float4 a = max(0.75 - float4(dot(d1, d1), dot(d2, d2), dot(d3, d3), dot(d4, d4)), 0.0);
float4 aa = a * a;
float4 aaaa = aa * aa;
float3 g1 = grad(hashes.x);
float3 g2 = grad(hashes.y);
float3 g3 = grad(hashes.z);
float3 g4 = grad(hashes.w);
float4 extrapolations = float4(dot(d1, g1), dot(d2, g2), dot(d3, g3), dot(d4, g4));
// Derivatives of the noise
float3 derivative = -8.0 * (d1 * (aa.x * a.x * extrapolations.x) + d2 * (aa.y * a.y * extrapolations.y) + d3 * (aa.z * a.z * extrapolations.z) + d4 * (aa.w * a.w * extrapolations.w))
+ (g1 * aaaa.x + g2 * aaaa.y + g3 * aaaa.z + g4 * aaaa.w);
// Return it all as a float4
return float4(derivative, dot(aaaa, extrapolations));
}
// Gives X and Y a triangular alignment, and lets Z move up the main diagonal.
// Might be good for terrain, or a time varying X/Y plane. Z repeats.
float4 os2NoiseWithDerivatives_ImproveXY(float3 X) {
// Not a skew transform.
mat3 orthonormalMap = mat3(
0.788675134594813, -0.211324865405187, -0.577350269189626,
-0.211324865405187, 0.788675134594813, -0.577350269189626,
0.577350269189626, 0.577350269189626, 0.577350269189626);
X = orthonormalMap * X;
float4 result = os2NoiseWithDerivativesPart(X) + os2NoiseWithDerivativesPart(X + 144.5);
return float4(result.xyz * orthonormalMap, result.w);
}
//////////////////////////////// End noise code ////////////////////////////////
//
float GradientWave(float b, float t) {
return smoothstep(0., b, t) * smoothstep(1., b, t);
}
float Random(float2 UV, float Seed) {
return fract(sin(dot(UV.xy * 13.235, float2(12.9898, 78.233)) * 0.000001) * 43758.5453123 * Seed);
}
float3 RandomVec3(float2 UV, float Seed) {
return float3(Random(UV, Seed), Random(UV * 2.0, Seed), Random(UV * 3.0, Seed));
}
float3 RaindropSurface(float2 XY, float DistanceScale, float ZScale) {
/*
Given the following equation, where A,M, N and S are all constants and Z and t are intermediate variables.
YeHaike's raindrop(Raindrop on glass) surface equation:
Z = (1-(x/A)^2-(y/A)^2)^(A/2)
t=min(max((Z-M)/(N-M),0.0),1.0)
z=S*(t^2)*(3.0-2.0*t)
Find the derivative of z with respect to x and y:
t(x, y) = min(max(((1 - (x/A)^2 - (y/A)^2)^(A/2) - M) / (N - M), 0.0), 1.0)
N = 1.5
M = 0.5
When 0.0 < (Z - M)/(N - M) < 1.0:
∂z/∂x = S*(6t - 8t^2) * (1/(N - M)) * (-x/A*(1 - (x/A)^2 - (y/A)^2)^((A/2)-1))
When (Z - M)/(N - M) ≤ 0.0 or (Z - M)/(N - M) ≥ 1.0:
∂z/∂x = 0
Similarly, we can find the partial derivative of z with respect to y:
When 0.0 < (Z - M)/(N - M) < 1.0:
∂z/∂y = S*(6t - 8t^2) * (1/(N - M)) * (-y/A*(1 - (x/A)^2 - (y/A)^2)^((A/2)-1))
When (Z - M)/(N - M) ≤ 0.0 or (Z - M)/(N - M) ≥ 1.0:
∂z/∂y = 0
This is the partial derivative of z with respect to x and y.
*/
float A = DistanceScale;
float x = XY.x;
float y = XY.y;
float N = 1.5;
float M = 0.5;
float S = ZScale;
float TempZ = 1.0 - pow(x / A, 2.0) - pow(y / A, 2.0);
float Z = pow(TempZ, A / 2.0);
float ZInMAndN = (Z - M) / (N - M);
float t = min(max(ZInMAndN, 0.0), 1.0);
float Height = S * t * t * (3.0 - 2.0 * t);
float Part01 = S * (6.0 * t - 8.0 * t * t);
float Part02 = 1.0 / (N - M);
float Part03 = -1.0 / A * pow(TempZ, A / 2.0 - 1.0);
float Part03OfX = x * Part03;
float Part03OfY = y * Part03;
float TempValue = (ZInMAndN > 0.0 && ZInMAndN < 1.0) ? Part01 * Part02 : 0.0;
float PartialDerivativeX = TempValue * Part03OfX;
float PartialDerivativeY = TempValue * Part03OfY;
float2 PartialDerivative = Height > 0.0 ? float2(PartialDerivativeX, PartialDerivativeY) : float2(0.0, 0.0);
return float3(Height, PartialDerivative);
}
// Map to range and clamp to [0.0,1.0]
float MapToRange(float edge0, float edge1, float x) {
float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t;
}
// x is in [0.0,1.0]
float ProportionalMapToRange(float edge0, float edge1, float x) {
float t = edge0 + (edge1 - edge0) * x;
return t;
}
// Return: x is height; yz is normal
// UVScale: Default is 20.0
vec3 StaticRaindrops(vec2 UV, float Time, float UVScale) {
vec2 TempUV = UV;
TempUV *= UVScale;
vec2 ID = floor(TempUV);
vec3 RandomValue = RandomVec3(vec2(ID.x * 470.15, ID.y * 653.58), RandomSeed);
TempUV = fract(TempUV) - 0.5;
vec2 RandomPoint = (RandomValue.xy - 0.5) * 0.25;
vec2 XY = RandomPoint - TempUV;
float Distance = length(TempUV - RandomPoint);
vec3 X = vec3(vec2(TempUV.x * 305.0 * 0.02, TempUV.y * 305.0 * 0.02), 1.8660254037844386);
vec4 noiseResult = os2NoiseWithDerivatives_ImproveXY(X);
float EdgeRandomCurveAdjust = noiseResult.w * mix(0.02, 0.175, fract(RandomValue.x));
Distance = EdgeRandomCurveAdjust * 0.5 + Distance;
Distance = Distance * clamp(mix(1.0, 55.0, RandomPoint.x), 1.0, 3.0);
float Height = smoothstep(0.2, 0.0, Distance);
float GradientFade = GradientWave(0.0005, fract(Time * 0.02 + RandomValue.z));
float DistanceMaxRange = 1.45 * GradientFade;
vec2 Direction = TempUV - RandomPoint;
float Theta = 3.141592653 - acos(dot(normalize(Direction), vec2(0.0, 1.0)));
Theta = Theta * RandomValue.z;
float DistanceScale = 0.2 / (1.0 - 0.8 * cos(Theta - 3.141593 / 2.0 - 1.6));
float YDistance = length(vec2(0.0, TempUV.y) - vec2(0.0, RandomPoint.y));
float NewDistance = MapToRange(0.0, DistanceMaxRange * pow(DistanceScale, 1.0), Distance);
float Scale = 1.65 * (0.2 + DistanceScale * 1.0) * DistanceMaxRange * mix(1.5, 0.5, RandomValue.x);
vec2 TempXY = vec2(XY.x * 1.0, XY.y) * 4.0;
float RandomScale = ProportionalMapToRange(0.85, 1.35, RandomValue.z);
TempXY.x = RandomScale * mix(TempXY.x, TempXY.x / smoothstep(1.0, 0.4, YDistance * RandomValue.z), smoothstep(1.0, 0.0, RandomValue.x));
TempXY = TempXY + EdgeRandomCurveAdjust * 1.0;
vec3 HeightAndNormal = RaindropSurface(TempXY, Scale, 1.0);
HeightAndNormal.yz = -HeightAndNormal.yz;
float RandomVisible = (fract(RandomValue.z * 10.0 * RandomSeed) < NumberScaleOfStaticRaindrops ? 1.0 : 0.0);
HeightAndNormal.yz = HeightAndNormal.yz * RandomVisible;
HeightAndNormal.x = smoothstep(0.0, 1.0, HeightAndNormal.x) * RandomVisible;
return HeightAndNormal;
}
// Return: x is height; yz is normal; w is trail.
// UVScale: Default is 2.25
vec4 RollingRaindrops(vec2 UV, float Time, float UVScale) {
vec2 LocalUV = UV * UVScale;
vec2 TempUV = LocalUV;
vec2 ConstantA = vec2(6.0, 1.0);
vec2 GridNum = ConstantA * 2.0;
vec2 GridID = floor(LocalUV * GridNum);
float RandomFloat = Random(vec2(GridID.x * 131.26, GridID.x * 101.81), RandomSeed);
float TimeMovingY = Time * 0.85 * ProportionalMapToRange(0.1, 0.25, RandomFloat);
LocalUV.y += TimeMovingY;
float YShift = RandomFloat;
LocalUV.y += YShift;
vec2 ScaledUV = LocalUV * GridNum;
GridID = floor(ScaledUV);
vec3 RandomVec = RandomVec3(vec2(GridID.x * 17.32, GridID.y * 2217.54), RandomSeed);
vec2 GridUV = fract(ScaledUV) - vec2(0.5, 0.0);
float SwingX = RandomVec.x - 0.5;
float SwingY = TempUV.y * 20.0;
float SwingPosition = sin(SwingY + sin(GridID.y * RandomVec.z + SwingY) + GridID.y * RandomVec.z);
SwingX += SwingPosition * (0.5 - abs(SwingX)) * (RandomVec.z - 0.5);
SwingX *= 0.65;
float RandomNormalizedTime = fract(TimeMovingY + RandomVec.z) * 1.0;
SwingY = (GradientWave(0.87, RandomNormalizedTime) - 0.5) * 0.9 + 0.5;
SwingY = clamp(SwingY, 0.15, 0.85);
vec2 Position = vec2(SwingX, SwingY);
vec2 XY = Position - GridUV;
vec2 Direction = (GridUV - Position) * ConstantA.yx;
float Distance = length(Direction);
//---------
vec3 X = vec3(vec2(TempUV.x * 513.20 * 0.02, TempUV.y * 779.40 * 0.02), 2.1660251037743386);
vec4 NoiseResult = os2NoiseWithDerivatives_ImproveXY(X);
float EdgeRandomCurveAdjust = NoiseResult.w * mix(0.02, 0.175, fract(RandomVec.y));
Distance = EdgeRandomCurveAdjust + Distance;
float Height = smoothstep(0.2, 0.0, Distance);
float NewDistance = MapToRange(0.0, 0.2, Distance);
float DistanceMaxRange = 1.45;
float Theta = 3.141592653 - acos(dot(normalize(Direction), vec2(0.0, 1.0)));
Theta = Theta * RandomVec.z;
float DistanceScale = 0.2 / (1.0 - 0.8 * cos(Theta - 3.141593 / 2.0 - 1.6));
float Scale = 1.65 * (0.2 + DistanceScale * 1.0) * DistanceMaxRange * mix(1.0, 0.25, RandomVec.x * 1.0);
vec2 TempXY = vec2(XY.x * 1.0, XY.y) * 4.0;
float RandomScale = ProportionalMapToRange(0.85, 1.35, RandomVec.z);
TempXY = TempXY * vec2(1.0, 4.2) + EdgeRandomCurveAdjust * 0.85;
vec3 HeightAndNormal = RaindropSurface(TempXY, Scale, 1.0);
//----------
// Trail
float TrailY = pow(smoothstep(1.0, SwingY, GridUV.y), 0.5);
float TrailX = abs(GridUV.x - SwingX) * mix(0.8, 4.0, smoothstep(0.0, 1.0, RandomVec.x));
float Trail = smoothstep(0.25 * TrailY, 0.15 * TrailY * TrailY, TrailX);
float TrailClamp = smoothstep(-0.02, 0.02, GridUV.y - SwingY);
Trail *= TrailClamp * TrailY;
float SignOfTrailX = sign(GridUV.x - SwingX);
vec3 NoiseInput = vec3(vec2(TempUV.x * 513.20 * 0.02 * SignOfTrailX, TempUV.y * 779.40 * 0.02), 2.1660251037743386);
vec4 TrailNoiseResult = os2NoiseWithDerivatives_ImproveXY(NoiseInput);
float TrailEdgeRandomCurveAdjust = TrailNoiseResult.w * mix(0.002, 0.175, fract(RandomVec.y));
float TrailXDistance = MapToRange(0.0, 0.1, TrailEdgeRandomCurveAdjust * 0.5 + TrailX);
vec2 TrailDirection = SignOfTrailX * vec2(1.0, 0.0) + vec2(0.0, 1.0) * smoothstep(1.0, 0.0, Trail) * 0.5;
vec2 TrailXY = TrailDirection * 1.0 * TrailXDistance;
vec3 TrailHeightAndNormal = RaindropSurface(TrailXY, 1.0, 1.0);
TrailHeightAndNormal = TrailHeightAndNormal * pow(Trail * RandomVec.y, 2.0);
TrailHeightAndNormal.x = smoothstep(0.0, 1.0, TrailHeightAndNormal.x);
// Remain Trail Droplets
SwingY = TempUV.y;
float RemainTrail = smoothstep(0.2 * TrailY, 0.0, TrailX);
float RemainDroplet = max(0.0, (sin(SwingY * (1.0 - SwingY) * 120.0) - GridUV.y)) * RemainTrail * TrailClamp * RandomVec.z;
SwingY = fract(SwingY * 10.0) + (GridUV.y - 0.5);
vec2 RemainDropletXY = GridUV - vec2(SwingX, SwingY);
RemainDropletXY = RemainDropletXY * vec2(1.2, 0.8);
RemainDropletXY = RemainDropletXY + EdgeRandomCurveAdjust * 0.85;
vec3 RemainDropletHeightAndNormal = RaindropSurface(RemainDropletXY, 2.0 * RemainDroplet, 1.0);
RemainDropletHeightAndNormal.x = smoothstep(0.0, 1.0, RemainDropletHeightAndNormal.x);
RemainDropletHeightAndNormal = TrailHeightAndNormal.x > 0.0 ? vec3(0.0, 0.0, 0.0) : RemainDropletHeightAndNormal;
vec4 ReturnValue;
ReturnValue.x = HeightAndNormal.x + TrailHeightAndNormal.x * TrailY * TrailClamp + RemainDropletHeightAndNormal.x * TrailY * TrailClamp;
ReturnValue.yz = HeightAndNormal.yz + TrailHeightAndNormal.yz + RemainDropletHeightAndNormal.yz;
ReturnValue.w = Trail;
float RandomVisible = (fract(RandomVec.z * 20.0 * RandomSeed) < NumberScaleOfRollingRaindrops ? 1.0 : 0.0);
ReturnValue = ReturnValue * RandomVisible;
return ReturnValue;
}
vec4 Raindrops(vec2 UV, float Time, float UVScale00, float UVScale01, float UVScale02) {
vec3 StaticRaindrop = StaticRaindrops(UV, Time, UVScale00);
vec4 RollingRaindrop01 = RollingRaindrops(UV, Time, UVScale01);
float Height = StaticRaindrop.x + RollingRaindrop01.x;
vec2 Normal = StaticRaindrop.yz + RollingRaindrop01.yz;
float Trail = RollingRaindrop01.w;
return vec4(Height, Normal, Trail);
}
half4 main(vec2 fragCoord) {
float Time = iTime;
float ScaledTime = Time * 0.2;
// Flip the y coordinate
vec2 flippedFragCoord = float2(fragCoord.x, iResolution.y - fragCoord.y);
vec2 GlobalUV = flippedFragCoord / iResolution.xy;
vec2 LocalUV = (flippedFragCoord - 0.5 * iResolution.xy) / iResolution.y;
float RaindropsAmount = sin(Time * 0.25) * 0.5 + 0.5;
float MaxBlur = mix(BackgroundBlur, BackgroundBlur * 2.0, RaindropsAmount);
float MinBlur = RaindropBlur;
float StaticRaindropsAmount = smoothstep(-0.5, 1.0, RaindropsAmount) * 2.0;
float RollingRaindropsAmount01 = smoothstep(0.25, 0.75, RaindropsAmount);
float RollingRaindropsAmount02 = smoothstep(0.0, 0.5, RaindropsAmount);
vec4 Raindrop = Raindrops(LocalUV, Time,
StaticRaindropUVScale, RollingRaindropUVScaleLayer01, RollingRaindropUVScaleLayer02);
float RaindropHeight = Raindrop.x;
float RaindropTrail = Raindrop.w;
vec2 RaindropNormal = -Raindrop.yz;
RaindropNormal = RaindropHeight > 0.0 ? RaindropNormal * 0.15 : vec2(0.0, 0.0);
vec2 UVWithNormal = GlobalUV + RaindropNormal;
float EdgeColorScale = smoothstep(0.2, 0.0, length(RaindropNormal));
EdgeColorScale = RaindropHeight > 0.0 ? pow(EdgeColorScale, 0.5) * 0.2 + 0.8 : 1.0;
float Blur = mix(MinBlur, MaxBlur, smoothstep(0.0, 1.6, length(RaindropNormal)));
Blur = RaindropHeight > 0.0 ? Blur : MaxBlur;
Blur = ProportionalMapToRange(MinBlur, Blur, 1.0 - RaindropTrail);
EdgeColorScale = pow(EdgeColorScale, 0.85);
float3 FinalColor = iImage1.eval(UVWithNormal).rgb * EdgeColorScale;
return half4(FinalColor, 1.0);
}