Loading weathereffects/graphics/assets/shaders/snow.agsl +54 −17 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ const mat2 rot45 = mat2( uniform half intensity; const float farthestSnowLayerWiggleSpeed = 5.8; const float closestSnowLayerWiggleSpeed = 2.6; /** * Generates snow flakes. * Loading @@ -33,7 +36,10 @@ uniform half intensity; * @param screenAspectRatio the aspect ratio of the fragment where we will display the effect. * @param time the elapsed time. * @param snowGridSize the size of the grid, where each cell contains a snow flake. * @param layerNumber the layer of snow that we want to draw. Higher is farther from camera. * @param layerIndex the index of the current layer of snow that we want to draw. (Higher index * indicates that it's farther away from camera). * @param minLayerIndex the index of the minimum layer. * @param maxLayerIndex the index of the maximum layers. * * @returns Snow with the snow info. */ Loading @@ -43,13 +49,18 @@ Snow generateSnow( in float screenAspectRatio, in float time, in vec2 snowGridSize, in float layerNumber in float layerIndex, in float minLayerIndex, in float maxLayerIndex ) { // Normalize the layer index. 0 is closest, 1 is farthest. half normalizedLayerIndex = map(layerIndex, minLayerIndex, maxLayerIndex, 0, 1); /* Grid. */ // Increase the last number to make each layer more separate from the previous one. float depth = 1. + layerNumber * 0.35; float speedAdj = 1. + layerNumber * 0.15; float layerR = idGenerator(layerNumber); float depth = 0.65 + layerIndex * 0.41; float speedAdj = 1. + layerIndex * 0.15; float layerR = idGenerator(layerIndex); snowGridSize *= depth; time += layerR * 58.3; // Number of rows and columns (each one is a cell, a drop). Loading @@ -67,7 +78,8 @@ Snow generateSnow( gridUv.y += verticalGridPos; // Generate column id, to offset columns vertically (so snow flakes are not aligned). float columnId = idGenerator(floor(gridUv.x)); gridUv.y += columnId * 2.6; // Have time affect the position of each column as well. gridUv.y += columnId * 2.6 + time * 0.09 * (1 - columnId); /* Cell. */ // Get the cell ID based on the grid position. Value from 0 to 1. Loading @@ -87,21 +99,43 @@ Snow generateSnow( } /* Cell-id-based variations. */ // Adjust time based on columnId. // Adjusts scale of each snow flake (higher is smaller). float scaleVariation = 2.0 + 2.7 * cellId; float opacityVariation = (1. - 0.9 * cellId); // 0 = snow flake invisible, 1 = snow flake visible. float visibilityFactor = smoothstep( cellIntensity, max(cellIntensity - (0.02 + 0.18 * intensity), 0.0), 1 - intensity); // Adjust the size of each snow flake (higher is smaller) based on cell ID. float decreaseFactor = 2.0 + map(cellId, 0., 1., -0.1, 2.8) + 5. * (1 - visibilityFactor); // Adjust the opacity of the particle based on the cell id and distance from the camera. float farLayerFadeOut = map(normalizedLayerIndex, 0.7, 1, 1, 0.4); float closeLayerFadeOut = map(normalizedLayerIndex, 0, 0.2, 0.6, 1); float opacityVariation = (1. - 0.9 * cellId) * visibilityFactor * closeLayerFadeOut * farLayerFadeOut; /* Cell snow flake. */ // Horizontal movement: Wiggle. float wiggleSpeed = 3.0; // Horizontal movement: Wiggle (Adjust the wiggle speed based on the distance). float wiggleSpeed = map( normalizedLayerIndex, 0.2, 0.7, closestSnowLayerWiggleSpeed, farthestSnowLayerWiggleSpeed); // Adjust wiggle based on layer number (0 = closer to screen => we want less movement). float wiggleAmp = 0.4 + 0.4 * smoothstep(0.5, 2.5, layerNumber); float wiggleAmp = 0.6 + 0.4 * smoothstep(0.5, 2.5, layerIndex); // Define the start based on the cell id. float horizontalStartAmp = 0.5; // Add the wiggle (equation decided by testing in Grapher). float horizontalWiggle = wiggle(uv.y + cellId * 2.1, wiggleSpeed * speedAdj); float horizontalWiggle = wiggle( // Current uv position. uv.y // Adjustment so the shape is not skewed. - cellUv.y / snowGridSize.y // variation based on cell ID. + cellId * 2.1, wiggleSpeed * speedAdj); // Add the start and wiggle and make that when we are closer to the edge, we don't wiggle much // (so the drop doesn't go outside it's cell). Loading @@ -114,8 +148,11 @@ Snow generateSnow( vec2 snowFlakeShape = vec2(1., 1.2); vec2 snowFlakePos = vec2(snowFlakePosUncorrected / cellAspectRatio, cellUv.y); snowFlakePos -= vec2(0., uv.y - 0.5) * cellId; snowFlakePos *= snowFlakeShape * scaleVariation; vec2 snowFlakePosR = 1.016 * abs(rot45 * (snowFlakePos + (cellId * 2. - 1.) * vec2(0.050))); snowFlakePos *= snowFlakeShape * decreaseFactor; vec2 snowFlakeShapeVariation = vec2(0.055) * // max variation vec2((cellId * 2. - 1.), // random A based on cell ID (fract((cellId + 0.03521) * 34.21) * 2. - 1.)); // random B based on cell ID vec2 snowFlakePosR = 1.016 * abs(rot45 * (snowFlakePos + snowFlakeShapeVariation)); snowFlakePos = abs(snowFlakePos); // Create the snowFlake mask. float flakeMask = smoothstep( Loading weathereffects/graphics/assets/shaders/snow_effect.agsl +71 −45 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ uniform shader foreground; uniform shader background; uniform shader accumulatedSnow; uniform shader noise; uniform float2 uvOffsetFgd; uniform float2 uvScaleFgd; uniform float2 uvOffsetBgd; Loading @@ -29,71 +30,96 @@ uniform float2 screenSize; #include "shaders/utils.agsl" #include "shaders/snow.agsl" /* Constants that can be modified. */ // Snow tint. const vec3 snowColor = vec3(0.9); // Glass tint. const vec3 glassTint = vec3(0.8); // gray const vec4 snowColor = vec4(1., 1., 1., 0.95); // Background tint const vec4 bgdTint = vec4(0.8, 0.8, 0.8, 0.07); // snow opacity (how visible it is). const float snowOpacity = 1.4; // how frosted the glass is. const float frostedGlassIntensity = 0.07; // Indices of the different snow layers. const float farthestSnowLayerIndex = 9; const float midSnowLayerIndex = 3; const float closestSnowLayerIndex = 0; vec4 main(float2 fragCoord) { float2 uv = fragCoord / screenSize; float2 uvAdjusted = vec2(uv.x, uv.y / screenAspectRatio); /** * The effect is consisted of 2 image textures (foreground and background) + 10 layers of * snow + 1 layer of snow accumulation. Below describes the rendering order (back to front): * 1. Background * 2. Background snow layers (from farthest layer to mid layer) * 3. Foreground * 4. Snow accumulation layer (on subject) * 5. Foreground snow layers (from mid layer to closest layer) */ // Adjusts the UVs to have the expected rect of the image. float2 adjustedUvForeground = fragCoord * uvScaleFgd + uvOffsetFgd; vec4 colorForeground = foreground.eval(adjustedUvForeground); vec4 colorBackground = background.eval(fragCoord * uvScaleBgd + uvOffsetBgd); vec4 color = vec4(0., 0., 0., 1.); // 1. Draw background. vec4 color = colorBackground; // Add some slight tint to the frosted glass. // Add slight tint to the background. color.rgb = normalBlendNotPremultiplied(color.rgb, bgdTint.rgb, bgdTint.a); // Get color of the background texture. color.rgb = mix(colorBackground.rgb, glassTint, frostedGlassIntensity); for (half i = 9.; i > 2.; i--) { // Generate snow behind the subject. // Normalized layer index. half idx = (i - 2.) / (9. - 2.); // 2. Generate snow layers behind the subject. for (float i = farthestSnowLayerIndex; i > midSnowLayerIndex; i--) { Snow snow = generateSnow( uv, screenAspectRatio, time * mix(1.25, 5., idx), /* Grid size = */ vec2(mix(3.0, 6.0, idx), mix(1.0, 3.0, idx)), /* layer number = */ i); color.rgb = mix(color.rgb, snowColor, snowOpacity * snow.flakeMask); time, // TODO: adjust grid size based on aspect ratio. /* Grid size = */ vec2(7., 1.5), /* layer number = */ i, closestSnowLayerIndex, farthestSnowLayerIndex); color.rgb = normalBlendNotPremultiplied(color.rgb, snowColor.rgb, snowColor.a * snow.flakeMask); } // Add the foreground. Any effect from here will be in front of the subject. // 3. Add the foreground layer. Any effect from here will be in front of the subject. color.rgb = normalBlend(color.rgb, colorForeground.rgb, colorForeground.a); // Add accumulated snow. vec2 accSnow = accumulatedSnow.eval(adjustedUvForeground).rg; float snowLayer = smoothstep(0.2, 0.8, accSnow.r); float snowTexture = smoothstep(0.2, 0.7, accSnow.g); color.rgb = mix(color.rgb, vec3(0.95), 0.98 * snowLayer * (0.05 + 0.95 * snowTexture)); // 4. Add accumulated snow layer. // Load noise texture to give "fluffy-ness" to the snow. Displace the sampling of the noise. vec3 cloudsNoise = noise.eval(uvAdjusted * 7000 + vec2(fragCoord.y, -fragCoord.x)).rgb; // Add dither to give texture to the snow and ruffle the edges. float dither = abs(triangleNoise(fragCoord * 0.01)); for (half i = 2.; i >= 0.; i--) { // Generate snow behind the subject. // Get the accumulated snow buffer. r contains its mask, g contains some random noise. vec2 accSnow = accumulatedSnow.eval(adjustedUvForeground).rg; // Sharpen the mask of the accumulated snow, but not in excess. float accSnowMask = smoothstep(0.1, 0.9, /* mask= */ accSnow.r); // Makes the edges of the snow layer accumulation rougher. accSnowMask = map(accSnowMask, 1. - cloudsNoise.b - 0.3 * dither, 1., 0., 1.); // Load snow texture and dither. Make it have gray-ish values. float accSnowTexture = smoothstep(0.2, 0.7, /* noise= */ accSnow.g) * 0.7; accSnowTexture = map(accSnowTexture, dither - 1, 1, 0, 1); // Adjust snow texture coverage/shape. accSnowTexture = map(accSnowTexture, 0.67, 0.8, 0, 1); accSnowMask = map(accSnowMask, 0., 1., 0., 1.- 0.6 * accSnowTexture - 0.35 * dither); color.rgb = normalBlendNotPremultiplied(color.rgb, snowColor.rgb, snowColor.a * accSnowMask); // 5. Generate snow in front of the subject. for (float i = midSnowLayerIndex; i >= closestSnowLayerIndex; i--) { Snow snow = generateSnow( uv, screenAspectRatio, time * 1.25, /* Grid size = */ vec2(i + 1., 1.4), /* layer number = */ i); color.rgb = mix(color.rgb, snowColor, snowOpacity * snow.flakeMask); time, // TODO: adjust grid size based on aspect ratio /* Grid size = */ vec2(7., 1.5), /* layer number = */ i, closestSnowLayerIndex, farthestSnowLayerIndex); color.rgb = normalBlendNotPremultiplied(color.rgb, snowColor.rgb, snowColor.a * snow.flakeMask); } /* Debug snow */ // resets color. // color.rgb *= 0.; // color.rgb += snow.flakeMask; // if (snow.cellUv.x > 0.49 || snow.cellUv.y > 0.49) color.r = 1.0; return color; } weathereffects/graphics/assets/shaders/utils.agsl +14 −0 Original line number Diff line number Diff line Loading @@ -94,6 +94,20 @@ vec3 normalBlendWithWhiteSrc(vec3 b, float o) { return b * (1. - o) + o; } /* * This is the normal blend mode in which the foreground is painted on top of the background based * on the foreground opacity. * * @param b the background color. * @param f the foreground color. * @param o the mask or the foreground alpha. * * Note: this blending function expects the foreground to NOT have premultiplied alpha. */ vec3 normalBlendNotPremultiplied(vec3 b, vec3 f, float o) { return mix(b, f, o); } /** Math Utils */ // function created on Grapher (equation decided by testing in Grapher). float wiggle(float time, float wiggleSpeed) { Loading weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/snow/SnowEffect.kt +5 −0 Original line number Diff line number Diff line Loading @@ -153,6 +153,11 @@ class SnowEffect( "background", BitmapShader(snowConfig.background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR) ) snowConfig.shader.setInputBuffer( "noise", BitmapShader(snowConfig.noiseTexture, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT) ) } private fun prepareColorGrading() { Loading weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/snow/SnowEffectConfig.kt +8 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,11 @@ data class SnowEffectConfig( val accumulatedSnowShader: RuntimeShader, /** The color grading shader. */ val colorGradingShader: RuntimeShader, /** * The noise texture, which will be used to add fluffiness to the snow flakes. The texture is * expected to be tileable, and at least 16-bit per channel for render quality. */ val noiseTexture: Bitmap, /** The main lut (color grading) for the effect. */ val lut: Bitmap?, /** A bitmap containing the foreground of the image. */ Loading Loading @@ -64,6 +69,8 @@ data class SnowEffectConfig( accumulatedSnowShader = GraphicsUtils.loadShader(context.assets, ACCUMULATED_SNOW_SHADER_PATH), colorGradingShader = GraphicsUtils.loadShader(context.assets, COLOR_GRADING_SHADER_PATH), noiseTexture = GraphicsUtils.loadTexture(context.assets, NOISE_TEXTURE_PATH) ?: throw RuntimeException("Noise texture is missing."), lut = GraphicsUtils.loadTexture(context.assets, LOOKUP_TABLE_TEXTURE_PATH), foreground, background, Loading @@ -77,6 +84,7 @@ data class SnowEffectConfig( private const val SHADER_PATH = "shaders/snow_effect.agsl" private const val ACCUMULATED_SNOW_SHADER_PATH = "shaders/snow_accumulation.agsl" private const val COLOR_GRADING_SHADER_PATH = "shaders/color_grading_lut.agsl" private const val NOISE_TEXTURE_PATH = "textures/clouds.png" private const val LOOKUP_TABLE_TEXTURE_PATH = "textures/lut_rain_and_fog.png" private const val BLUR_RADIUS = 20f private const val DEFAULT_INTENSITY = 1f Loading Loading
weathereffects/graphics/assets/shaders/snow.agsl +54 −17 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ const mat2 rot45 = mat2( uniform half intensity; const float farthestSnowLayerWiggleSpeed = 5.8; const float closestSnowLayerWiggleSpeed = 2.6; /** * Generates snow flakes. * Loading @@ -33,7 +36,10 @@ uniform half intensity; * @param screenAspectRatio the aspect ratio of the fragment where we will display the effect. * @param time the elapsed time. * @param snowGridSize the size of the grid, where each cell contains a snow flake. * @param layerNumber the layer of snow that we want to draw. Higher is farther from camera. * @param layerIndex the index of the current layer of snow that we want to draw. (Higher index * indicates that it's farther away from camera). * @param minLayerIndex the index of the minimum layer. * @param maxLayerIndex the index of the maximum layers. * * @returns Snow with the snow info. */ Loading @@ -43,13 +49,18 @@ Snow generateSnow( in float screenAspectRatio, in float time, in vec2 snowGridSize, in float layerNumber in float layerIndex, in float minLayerIndex, in float maxLayerIndex ) { // Normalize the layer index. 0 is closest, 1 is farthest. half normalizedLayerIndex = map(layerIndex, minLayerIndex, maxLayerIndex, 0, 1); /* Grid. */ // Increase the last number to make each layer more separate from the previous one. float depth = 1. + layerNumber * 0.35; float speedAdj = 1. + layerNumber * 0.15; float layerR = idGenerator(layerNumber); float depth = 0.65 + layerIndex * 0.41; float speedAdj = 1. + layerIndex * 0.15; float layerR = idGenerator(layerIndex); snowGridSize *= depth; time += layerR * 58.3; // Number of rows and columns (each one is a cell, a drop). Loading @@ -67,7 +78,8 @@ Snow generateSnow( gridUv.y += verticalGridPos; // Generate column id, to offset columns vertically (so snow flakes are not aligned). float columnId = idGenerator(floor(gridUv.x)); gridUv.y += columnId * 2.6; // Have time affect the position of each column as well. gridUv.y += columnId * 2.6 + time * 0.09 * (1 - columnId); /* Cell. */ // Get the cell ID based on the grid position. Value from 0 to 1. Loading @@ -87,21 +99,43 @@ Snow generateSnow( } /* Cell-id-based variations. */ // Adjust time based on columnId. // Adjusts scale of each snow flake (higher is smaller). float scaleVariation = 2.0 + 2.7 * cellId; float opacityVariation = (1. - 0.9 * cellId); // 0 = snow flake invisible, 1 = snow flake visible. float visibilityFactor = smoothstep( cellIntensity, max(cellIntensity - (0.02 + 0.18 * intensity), 0.0), 1 - intensity); // Adjust the size of each snow flake (higher is smaller) based on cell ID. float decreaseFactor = 2.0 + map(cellId, 0., 1., -0.1, 2.8) + 5. * (1 - visibilityFactor); // Adjust the opacity of the particle based on the cell id and distance from the camera. float farLayerFadeOut = map(normalizedLayerIndex, 0.7, 1, 1, 0.4); float closeLayerFadeOut = map(normalizedLayerIndex, 0, 0.2, 0.6, 1); float opacityVariation = (1. - 0.9 * cellId) * visibilityFactor * closeLayerFadeOut * farLayerFadeOut; /* Cell snow flake. */ // Horizontal movement: Wiggle. float wiggleSpeed = 3.0; // Horizontal movement: Wiggle (Adjust the wiggle speed based on the distance). float wiggleSpeed = map( normalizedLayerIndex, 0.2, 0.7, closestSnowLayerWiggleSpeed, farthestSnowLayerWiggleSpeed); // Adjust wiggle based on layer number (0 = closer to screen => we want less movement). float wiggleAmp = 0.4 + 0.4 * smoothstep(0.5, 2.5, layerNumber); float wiggleAmp = 0.6 + 0.4 * smoothstep(0.5, 2.5, layerIndex); // Define the start based on the cell id. float horizontalStartAmp = 0.5; // Add the wiggle (equation decided by testing in Grapher). float horizontalWiggle = wiggle(uv.y + cellId * 2.1, wiggleSpeed * speedAdj); float horizontalWiggle = wiggle( // Current uv position. uv.y // Adjustment so the shape is not skewed. - cellUv.y / snowGridSize.y // variation based on cell ID. + cellId * 2.1, wiggleSpeed * speedAdj); // Add the start and wiggle and make that when we are closer to the edge, we don't wiggle much // (so the drop doesn't go outside it's cell). Loading @@ -114,8 +148,11 @@ Snow generateSnow( vec2 snowFlakeShape = vec2(1., 1.2); vec2 snowFlakePos = vec2(snowFlakePosUncorrected / cellAspectRatio, cellUv.y); snowFlakePos -= vec2(0., uv.y - 0.5) * cellId; snowFlakePos *= snowFlakeShape * scaleVariation; vec2 snowFlakePosR = 1.016 * abs(rot45 * (snowFlakePos + (cellId * 2. - 1.) * vec2(0.050))); snowFlakePos *= snowFlakeShape * decreaseFactor; vec2 snowFlakeShapeVariation = vec2(0.055) * // max variation vec2((cellId * 2. - 1.), // random A based on cell ID (fract((cellId + 0.03521) * 34.21) * 2. - 1.)); // random B based on cell ID vec2 snowFlakePosR = 1.016 * abs(rot45 * (snowFlakePos + snowFlakeShapeVariation)); snowFlakePos = abs(snowFlakePos); // Create the snowFlake mask. float flakeMask = smoothstep( Loading
weathereffects/graphics/assets/shaders/snow_effect.agsl +71 −45 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ uniform shader foreground; uniform shader background; uniform shader accumulatedSnow; uniform shader noise; uniform float2 uvOffsetFgd; uniform float2 uvScaleFgd; uniform float2 uvOffsetBgd; Loading @@ -29,71 +30,96 @@ uniform float2 screenSize; #include "shaders/utils.agsl" #include "shaders/snow.agsl" /* Constants that can be modified. */ // Snow tint. const vec3 snowColor = vec3(0.9); // Glass tint. const vec3 glassTint = vec3(0.8); // gray const vec4 snowColor = vec4(1., 1., 1., 0.95); // Background tint const vec4 bgdTint = vec4(0.8, 0.8, 0.8, 0.07); // snow opacity (how visible it is). const float snowOpacity = 1.4; // how frosted the glass is. const float frostedGlassIntensity = 0.07; // Indices of the different snow layers. const float farthestSnowLayerIndex = 9; const float midSnowLayerIndex = 3; const float closestSnowLayerIndex = 0; vec4 main(float2 fragCoord) { float2 uv = fragCoord / screenSize; float2 uvAdjusted = vec2(uv.x, uv.y / screenAspectRatio); /** * The effect is consisted of 2 image textures (foreground and background) + 10 layers of * snow + 1 layer of snow accumulation. Below describes the rendering order (back to front): * 1. Background * 2. Background snow layers (from farthest layer to mid layer) * 3. Foreground * 4. Snow accumulation layer (on subject) * 5. Foreground snow layers (from mid layer to closest layer) */ // Adjusts the UVs to have the expected rect of the image. float2 adjustedUvForeground = fragCoord * uvScaleFgd + uvOffsetFgd; vec4 colorForeground = foreground.eval(adjustedUvForeground); vec4 colorBackground = background.eval(fragCoord * uvScaleBgd + uvOffsetBgd); vec4 color = vec4(0., 0., 0., 1.); // 1. Draw background. vec4 color = colorBackground; // Add some slight tint to the frosted glass. // Add slight tint to the background. color.rgb = normalBlendNotPremultiplied(color.rgb, bgdTint.rgb, bgdTint.a); // Get color of the background texture. color.rgb = mix(colorBackground.rgb, glassTint, frostedGlassIntensity); for (half i = 9.; i > 2.; i--) { // Generate snow behind the subject. // Normalized layer index. half idx = (i - 2.) / (9. - 2.); // 2. Generate snow layers behind the subject. for (float i = farthestSnowLayerIndex; i > midSnowLayerIndex; i--) { Snow snow = generateSnow( uv, screenAspectRatio, time * mix(1.25, 5., idx), /* Grid size = */ vec2(mix(3.0, 6.0, idx), mix(1.0, 3.0, idx)), /* layer number = */ i); color.rgb = mix(color.rgb, snowColor, snowOpacity * snow.flakeMask); time, // TODO: adjust grid size based on aspect ratio. /* Grid size = */ vec2(7., 1.5), /* layer number = */ i, closestSnowLayerIndex, farthestSnowLayerIndex); color.rgb = normalBlendNotPremultiplied(color.rgb, snowColor.rgb, snowColor.a * snow.flakeMask); } // Add the foreground. Any effect from here will be in front of the subject. // 3. Add the foreground layer. Any effect from here will be in front of the subject. color.rgb = normalBlend(color.rgb, colorForeground.rgb, colorForeground.a); // Add accumulated snow. vec2 accSnow = accumulatedSnow.eval(adjustedUvForeground).rg; float snowLayer = smoothstep(0.2, 0.8, accSnow.r); float snowTexture = smoothstep(0.2, 0.7, accSnow.g); color.rgb = mix(color.rgb, vec3(0.95), 0.98 * snowLayer * (0.05 + 0.95 * snowTexture)); // 4. Add accumulated snow layer. // Load noise texture to give "fluffy-ness" to the snow. Displace the sampling of the noise. vec3 cloudsNoise = noise.eval(uvAdjusted * 7000 + vec2(fragCoord.y, -fragCoord.x)).rgb; // Add dither to give texture to the snow and ruffle the edges. float dither = abs(triangleNoise(fragCoord * 0.01)); for (half i = 2.; i >= 0.; i--) { // Generate snow behind the subject. // Get the accumulated snow buffer. r contains its mask, g contains some random noise. vec2 accSnow = accumulatedSnow.eval(adjustedUvForeground).rg; // Sharpen the mask of the accumulated snow, but not in excess. float accSnowMask = smoothstep(0.1, 0.9, /* mask= */ accSnow.r); // Makes the edges of the snow layer accumulation rougher. accSnowMask = map(accSnowMask, 1. - cloudsNoise.b - 0.3 * dither, 1., 0., 1.); // Load snow texture and dither. Make it have gray-ish values. float accSnowTexture = smoothstep(0.2, 0.7, /* noise= */ accSnow.g) * 0.7; accSnowTexture = map(accSnowTexture, dither - 1, 1, 0, 1); // Adjust snow texture coverage/shape. accSnowTexture = map(accSnowTexture, 0.67, 0.8, 0, 1); accSnowMask = map(accSnowMask, 0., 1., 0., 1.- 0.6 * accSnowTexture - 0.35 * dither); color.rgb = normalBlendNotPremultiplied(color.rgb, snowColor.rgb, snowColor.a * accSnowMask); // 5. Generate snow in front of the subject. for (float i = midSnowLayerIndex; i >= closestSnowLayerIndex; i--) { Snow snow = generateSnow( uv, screenAspectRatio, time * 1.25, /* Grid size = */ vec2(i + 1., 1.4), /* layer number = */ i); color.rgb = mix(color.rgb, snowColor, snowOpacity * snow.flakeMask); time, // TODO: adjust grid size based on aspect ratio /* Grid size = */ vec2(7., 1.5), /* layer number = */ i, closestSnowLayerIndex, farthestSnowLayerIndex); color.rgb = normalBlendNotPremultiplied(color.rgb, snowColor.rgb, snowColor.a * snow.flakeMask); } /* Debug snow */ // resets color. // color.rgb *= 0.; // color.rgb += snow.flakeMask; // if (snow.cellUv.x > 0.49 || snow.cellUv.y > 0.49) color.r = 1.0; return color; }
weathereffects/graphics/assets/shaders/utils.agsl +14 −0 Original line number Diff line number Diff line Loading @@ -94,6 +94,20 @@ vec3 normalBlendWithWhiteSrc(vec3 b, float o) { return b * (1. - o) + o; } /* * This is the normal blend mode in which the foreground is painted on top of the background based * on the foreground opacity. * * @param b the background color. * @param f the foreground color. * @param o the mask or the foreground alpha. * * Note: this blending function expects the foreground to NOT have premultiplied alpha. */ vec3 normalBlendNotPremultiplied(vec3 b, vec3 f, float o) { return mix(b, f, o); } /** Math Utils */ // function created on Grapher (equation decided by testing in Grapher). float wiggle(float time, float wiggleSpeed) { Loading
weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/snow/SnowEffect.kt +5 −0 Original line number Diff line number Diff line Loading @@ -153,6 +153,11 @@ class SnowEffect( "background", BitmapShader(snowConfig.background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR) ) snowConfig.shader.setInputBuffer( "noise", BitmapShader(snowConfig.noiseTexture, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT) ) } private fun prepareColorGrading() { Loading
weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/snow/SnowEffectConfig.kt +8 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,11 @@ data class SnowEffectConfig( val accumulatedSnowShader: RuntimeShader, /** The color grading shader. */ val colorGradingShader: RuntimeShader, /** * The noise texture, which will be used to add fluffiness to the snow flakes. The texture is * expected to be tileable, and at least 16-bit per channel for render quality. */ val noiseTexture: Bitmap, /** The main lut (color grading) for the effect. */ val lut: Bitmap?, /** A bitmap containing the foreground of the image. */ Loading Loading @@ -64,6 +69,8 @@ data class SnowEffectConfig( accumulatedSnowShader = GraphicsUtils.loadShader(context.assets, ACCUMULATED_SNOW_SHADER_PATH), colorGradingShader = GraphicsUtils.loadShader(context.assets, COLOR_GRADING_SHADER_PATH), noiseTexture = GraphicsUtils.loadTexture(context.assets, NOISE_TEXTURE_PATH) ?: throw RuntimeException("Noise texture is missing."), lut = GraphicsUtils.loadTexture(context.assets, LOOKUP_TABLE_TEXTURE_PATH), foreground, background, Loading @@ -77,6 +84,7 @@ data class SnowEffectConfig( private const val SHADER_PATH = "shaders/snow_effect.agsl" private const val ACCUMULATED_SNOW_SHADER_PATH = "shaders/snow_accumulation.agsl" private const val COLOR_GRADING_SHADER_PATH = "shaders/color_grading_lut.agsl" private const val NOISE_TEXTURE_PATH = "textures/clouds.png" private const val LOOKUP_TABLE_TEXTURE_PATH = "textures/lut_rain_and_fog.png" private const val BLUR_RADIUS = 20f private const val DEFAULT_INTENSITY = 1f Loading