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