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 Snow { 18 highp float flakeMask; 19 highp vec2 cellUv; 20}; 21 22const mat2 rot45 = mat2( 23 0.7071067812, 0.7071067812, // First column. 24 -0.7071067812, 0.7071067812 // second column. 25); 26 27uniform half intensity; 28 29const float farthestSnowLayerWiggleSpeed = 2.18; 30const float closestSnowLayerWiggleSpeed = 0.9; 31 32/** 33 * Generates snow flakes. 34 * 35 * @param uv the UV of the fragment where we will display the snow effect. 36 * @param screenAspectRatio the aspect ratio of the fragment where we will display the effect. 37 * @param time the elapsed time. 38 * @param snowGridSize the size of the grid, where each cell contains a snow flake. 39 * @param layerIndex the index of the current layer of snow that we want to draw. (Higher index 40 * indicates that it's farther away from camera). 41 * @param minLayerIndex the index of the minimum layer. 42 * @param maxLayerIndex the index of the maximum layers. 43 * 44 * @returns Snow with the snow info. 45 */ 46Snow generateSnow( 47 // UVs of the target fragment (normalized). 48 in vec2 uv, 49 in float screenAspectRatio, 50 in float time, 51 in vec2 snowGridSize, 52 in float layerIndex, 53 in float minLayerIndex, 54 in float maxLayerIndex 55) { 56 // Normalize the layer index. 0 is closest, 1 is farthest. 57 half normalizedLayerIndex = map(layerIndex, minLayerIndex, maxLayerIndex, 0, 1); 58 59 /* Grid. */ 60 // Increase the last number to make each layer more separate from the previous one. 61 float depth = 0.65 + layerIndex * 0.555; 62 float speedAdj = 1. + layerIndex * 0.225; 63 float layerR = idGenerator(layerIndex); 64 snowGridSize *= depth; 65 time += layerR * 58.3; 66 // Number of rows and columns (each one is a cell, a drop). 67 float cellAspectRatio = snowGridSize.x / snowGridSize.y; 68 // Aspect ratio impacts visible cells. 69 uv.y /= screenAspectRatio; 70 // Skew uv.x so it goes to left or right 71 uv.x += uv.y * (0.8 * layerR - 0.4); 72 // scale the UV to allocate number of rows and columns. 73 vec2 gridUv = uv * snowGridSize; 74 // Invert y (otherwise it goes from 0=top to 1=bottom). 75 gridUv.y = 1. - gridUv.y; 76 float verticalGridPos = 0.4 * time / speedAdj; 77 // Move grid vertically down. 78 gridUv.y += verticalGridPos; 79 // Generate column id, to offset columns vertically (so snow flakes are not aligned). 80 float columnId = idGenerator(floor(gridUv.x)); 81 // Have time affect the position of each column as well. 82 gridUv.y += columnId * 2.6 + time * 0.19 * (1 - columnId); 83 84 /* Cell. */ 85 // Get the cell ID based on the grid position. Value from 0 to 1. 86 float cellId = idGenerator(floor(gridUv)); 87 // For each cell, we set the internal UV from -0.5 (left, bottom) to 0.5 (right, top). 88 vec2 cellUv = fract(gridUv) - 0.5; 89 cellUv.y *= -1.; 90 91 /* 92 * Disable snow flakes with some probabilty. This is done by 1) assigning a random intensity 93 * value to the cell 2) then compare it with the given intensity. 94 */ 95 half cellIntensity = idGenerator(floor(vec2(cellId * 856.16, 272.2))); 96 if (cellIntensity < 1. - intensity) { 97 // Remove snow flakes by seeting flake mask to 0. 98 return Snow(/* flakeMask= */ 0, cellUv); 99 } 100 101 /* Cell-id-based variations. */ 102 // 0 = snow flake invisible, 1 = snow flake visible. 103 float visibilityFactor = smoothstep( 104 cellIntensity, 105 max(cellIntensity - (0.02 + 0.18 * intensity), 0.0), 106 1 - intensity); 107 // Adjust the size of each snow flake (higher is smaller) based on cell ID. 108 float decreaseFactor = 2.0 + map(cellId, 0., 1., -0.1, 2.8) + 5. * (1 - visibilityFactor); 109 // Adjust the opacity of the particle based on the cell id and distance from the camera. 110 float farLayerFadeOut = map(normalizedLayerIndex, 0.7, 1, 1, 0.4); 111 float closeLayerFadeOut = map(normalizedLayerIndex, 0, 0.2, 0.6, 1); 112 float opacityVariation = 113 (1. - 0.9 * cellId) * 114 visibilityFactor * 115 closeLayerFadeOut * 116 farLayerFadeOut; 117 118 /* Cell snow flake. */ 119 // Calculate snow flake. 120 vec2 snowFlakeShape = vec2(0.28, 0.26); 121 vec2 snowFlakePos = vec2(cellUv.x, cellUv.y * cellAspectRatio); 122 snowFlakePos -= vec2( 123 0., 124 (uv.y - 0.5 / screenAspectRatio) - cellUv.y / snowGridSize.y 125 ) * screenAspectRatio; 126 snowFlakePos *= snowFlakeShape * decreaseFactor; 127 vec2 snowFlakeShapeVariation = vec2(0.055) * // max variation 128 vec2((cellId * 2. - 1.), // random A based on cell ID 129 (fract((cellId + 0.03521) * 34.21) * 2. - 1.)); // random B based on cell ID 130 vec2 snowFlakePosR = 1.016 * abs(rot45 * (snowFlakePos + snowFlakeShapeVariation)); 131 snowFlakePos = abs(snowFlakePos); 132 // Create the snowFlake mask. 133 float flakeMask = smoothstep( 134 0.3, 135 0.200 - 0.3 * opacityVariation, 136 snowFlakePos.x + snowFlakePos.y + snowFlakePosR.x + snowFlakePosR.y 137 ) * opacityVariation; 138 139 return Snow(flakeMask, cellUv); 140} 141