1/*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17struct GlassRain {
18    highp vec2 drop;
19    highp float dropMask;
20    highp vec2 dropplets;
21    highp float droppletsMask;
22    highp float trailMask;
23    highp vec2 cellUv;
24};
25
26/**
27 * Generates a single layer of rain running down on a foggy glass surface.
28 *
29 * @param uv the UV of the fragment where we will display the rain effect.
30 * @param screenAspectRatio the aspect ratio of the fragment where we will display the effect.
31 * @param time the elapsed time.
32 * @param rainGridSize the size of the grid, where each cell contains a main drop and some
33 * dropplets.
34 * @param rainIntensity how many of the cells will contain drops. Value from 0 (no rain) to 1
35 * (each cell contains a drop).
36 *
37 * @returns GlassRain an object containing all the info to draw the rain.
38 */
39GlassRain generateGlassRain(
40    // UVs of the target fragment (normalized).
41    in vec2 uv,
42    in float screenAspectRatio,
43    in float time,
44    in vec2 rainGridSize,
45    in float rainIntensity
46) {
47    vec2 dropPos = vec2(0.);
48    float cellMainDropMask = 0.0;
49    vec2 trailDropsPos = vec2(0.);
50    float cellDroppletsMask = 0.0;
51    float cellTrailMask = 0.0;
52
53    /* Grid. */
54    // Number of rows and columns (each one is a cell, a drop).
55    float cellAspectRatio = rainGridSize.x / rainGridSize.y;
56    // Aspect ratio impacts visible cells.
57    rainGridSize.y /= screenAspectRatio;
58    // scale the UV to allocate number of rows and columns.
59    vec2 gridUv = uv * rainGridSize;
60    // Invert y (otherwise it goes from 0=top to 1=bottom).
61    gridUv.y = 1. - gridUv.y;
62    float verticalGridPos = 2.4 * time / 5.0;
63    // Move grid vertically down.
64    gridUv.y += verticalGridPos;
65
66    /* Cell. */
67    // Get the cell ID based on the grid position. Value from 0 to 1.
68    float cellId = idGenerator(floor(gridUv));
69    // For each cell, we set the internal UV from -0.5 (left, bottom) to 0.5 (right, top).
70    vec2 cellUv = fract(gridUv) - 0.5;
71
72    /* Cell-id-based variations. */
73    // Adjust time based on cellId.
74    time += cellId * 7.1203;
75    // Adjusts UV.y based on cell ID. This will make that the wiggle variation is different for
76    // each cell.
77    uv.y += cellId * 3.83027;
78    // Adjusts scale of each drop (higher is smaller).
79    float scaleVariation = 1.0 + 0.7 * cellId;
80    // Make some cells to not have drops.
81    if (cellId < 1. - rainIntensity) {
82        return GlassRain(dropPos, cellMainDropMask, trailDropsPos, cellDroppletsMask,
83            cellTrailMask, cellUv);
84    }
85
86    /* Cell main drop. */
87    // vertical movement: Fourier Series-Sawtooth Wave (ascending: /|/|/|).
88    float verticalSpeed = TAU / 5.0;
89    float verticalPosVariation = 0.45 * 0.63 * (
90            -1.2 * sin(verticalSpeed * time)
91            -0.5 * sin(2. * verticalSpeed * time)
92            -0.3333 * sin(3. * verticalSpeed * time)
93    );
94
95    // Horizontal movement: Wiggle.
96    float wiggleSpeed = 6.0;
97    float wiggleAmp = 0.5;
98    // Define the start based on the cell id.
99    float horizontalStartAmp = 0.5;
100    float horizontalStart = (cellId - 0.5) * 2.0 * horizontalStartAmp / cellAspectRatio;
101    // Add the wiggle (equation decided by testing in Grapher).
102    float horizontalWiggle = wiggle(uv.y, wiggleSpeed);
103
104    // Add the start and wiggle and make that when we are closer to the edge, we don't wiggle much
105    // (so the drop doesn't go outside it's cell).
106    horizontalWiggle = horizontalStart
107        + (horizontalStartAmp - abs(horizontalStart)) * wiggleAmp * horizontalWiggle;
108
109    // Calculate main cell drop.
110    float dropPosUncorrected = (cellUv.x - horizontalWiggle);
111    dropPos.x = dropPosUncorrected / cellAspectRatio;
112    // Create tear drop shape.
113    verticalPosVariation -= dropPosUncorrected * dropPosUncorrected / cellAspectRatio;
114    dropPos.y = cellUv.y - verticalPosVariation;
115    // Adjust scale.
116    dropPos *= scaleVariation;
117    // Create a circle for the main drop in the cell, based on position.
118    cellMainDropMask = smoothstep(0.06, 0.04, length(dropPos));
119
120    /* Cell trail dropplets. */
121    trailDropsPos.x = (cellUv.x - horizontalWiggle)/ cellAspectRatio;
122    // Substract verticalGridPos to mage the dropplets stick in place.
123    trailDropsPos.y = cellUv.y -verticalGridPos;
124    trailDropsPos.y = (fract(trailDropsPos.y * 4.) - 0.5) / 4.;
125    // Adjust scale.
126    trailDropsPos *= scaleVariation;
127    cellDroppletsMask = smoothstep(0.03, 0.02, length(trailDropsPos));
128    // Fade the dropplets frop the top the farther they are from the main drop.
129    // Multiply by 1.2 so we show more of the trail.
130    float verticalFading = 1.2 * smoothstep(0.5, verticalPosVariation, cellUv.y);
131    cellDroppletsMask *= verticalFading;
132    // Mask dropplets that are under main cell drop.
133    cellDroppletsMask *= smoothstep(-0.06, 0.08, dropPos.y);
134
135    /* Cell trail mask (it will show the image unblurred). */
136    // Gradient for top of the main drop.
137    cellTrailMask = smoothstep(-0.04, 0.04, dropPos.y);
138    // Fades out the closer we get to the top of the cell.
139    cellTrailMask *= verticalFading;
140    // Only show the main section of the trail.
141    cellTrailMask *= smoothstep(0.07, 0.02, abs(dropPos.x));
142
143    cellDroppletsMask *= cellTrailMask;
144
145    return GlassRain(
146        dropPos, cellMainDropMask, trailDropsPos, cellDroppletsMask, cellTrailMask, cellUv);
147}
148
149/**
150 * Generate rain drops that stay in place on the glass surface.
151 */
152vec3 generateStaticGlassRain(vec2 uv, half screenAspectRatio, half time, half intensity) {
153    vec2 gridSize = vec2(15., 15.);
154    // Aspect ratio impacts visible cells.
155    gridSize.y /= screenAspectRatio;
156    // scale the UV to allocate number of rows and columns.
157    vec2 gridUv = uv * gridSize;
158    // Invert y (otherwise it goes from 0=top to 1=bottom).
159    gridUv.y = 1. - gridUv.y;
160    // Generate column id, to offset columns vertically (so rain is not aligned).
161    float columnId = idGenerator(floor(gridUv.x));
162    gridUv.y += columnId * 5.6;
163
164    // Get the cell ID based on the grid position. Value from 0 to 1.
165    float cellId = idGenerator(floor(gridUv));
166
167    // Draw rain drops with a probability based on the cell id.
168    if (cellId < 0.8) {
169        return vec3(0.);
170    }
171
172    // For each cell, we set the internal UV from -0.5 (left, bottom) to 0.5 (right, top).
173    vec2 cellUv = fract(gridUv) - 0.5;
174    vec2 pixUv = cellUv;
175    pixUv.x *= -1;
176    vec2 pixDistance = screenSize * pixUv / gridSize;
177
178    float delay = 3.5173;
179    float duration = 8.2;
180    float t = time + 100. * cellId;
181    float circletime = floor(t / (duration + delay));
182    float delayOffset = idGenerator(floor(gridUv) + vec2(circletime, 43.14 * cellId));
183    float normalizedTime = map(/* value */     mod(t, duration + delay) - delay * delayOffset,
184                               /* in range */  0, duration,
185                               /* out range */ 0, 1);
186    // Apply a curve to the time.
187    normalizedTime *= normalizedTime;
188
189    vec2 pos = cellUv * (1.5 - 0.5 * cellId + normalizedTime * 50.);
190    float mask = smoothstep(0.3, 0.2, length(pos))
191                 * smoothstep(0.2, 0.06, normalizedTime)
192                 * smoothstep(0., 0.45, intensity);
193
194    return vec3(pos * 0.19, mask);
195}
196