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
17uniform shader foreground;
18uniform shader background;
19uniform shader accumulatedSnow;
20uniform shader noise;
21uniform float2 gridSize;
22uniform float time;
23uniform float screenAspectRatio;
24uniform float2 screenSize;
25uniform mat3 transformMatrixBitmap;
26uniform mat3 transformMatrixWeather;
27
28#include "shaders/constants.agsl"
29#include "shaders/utils.agsl"
30#include "shaders/snow.agsl"
31
32// Snow tint.
33const vec4 snowColor = vec4(1., 1., 1., 0.95);
34// Background tint
35const vec4 bgdTint = vec4(0.8, 0.8, 0.8, 0.07);
36
37// Indices of the different snow layers.
38const float farthestSnowLayerIndex = 9;
39const float midSnowLayerIndex = 3;
40const float closestSnowLayerIndex = 0;
41
42vec4 main(float2 fragCoord) {
43    /**
44     * The effect is consisted of 2 image textures (foreground and background) + 10 layers of
45     * snow + 1 layer of snow accumulation. Below describes the rendering order (back to front):
46     * 1. Background
47     * 2. Background snow layers (from farthest layer to mid layer)
48     * 3. Foreground
49     * 4. Snow accumulation layer (on subject)
50     * 5. Foreground snow layers (from mid layer to closest layer)
51     */
52
53    // Apply transform matrix to fragCoord
54    float2 adjustedUv = transformPoint(transformMatrixBitmap, fragCoord);
55
56    // Calculate uv for snow based on transformed coordinates
57    float2 uv = transformPoint(transformMatrixWeather, fragCoord) / screenSize;
58    float2 uvAdjusted = vec2(uv.x, uv.y / screenAspectRatio);
59
60    vec4 colorForeground = foreground.eval(adjustedUv);
61    vec4 colorBackground = background.eval(adjustedUv);
62
63     // Adjusts contrast and brightness.
64    float noiseT = triangleNoise(fragCoord.xy + vec2(12.31, 1024.1241));
65    colorBackground.rgb =
66        imageRangeConversion(colorBackground.rgb, 0.88, 0.02, noiseT * 0.025, intensity);
67    colorForeground.rgb =
68        imageRangeConversion(colorForeground.rgb, 0.88, 0.02, noiseT * 0.025, intensity);
69
70    // 1. Draw background.
71    vec4 color = colorBackground;
72
73    // Add slight tint to the background.
74    color.rgb = normalBlendNotPremultiplied(color.rgb, bgdTint.rgb, bgdTint.a);
75
76    // 2. Generate snow layers behind the subject.
77    for (float i = farthestSnowLayerIndex; i > midSnowLayerIndex; i--) {
78        Snow snow = generateSnow(
79            uv,
80            screenAspectRatio,
81            time,
82            gridSize,
83            /* layer number = */ i,
84            closestSnowLayerIndex,
85            farthestSnowLayerIndex);
86
87        color.rgb =
88            normalBlendNotPremultiplied(color.rgb, snowColor.rgb, snowColor.a * snow.flakeMask);
89    }
90
91    // 3. Add the foreground layer. Any effect from here will be in front of the subject.
92    color.rgb = normalBlend(color.rgb, colorForeground.rgb, colorForeground.a);
93
94    // 4. Add accumulated snow layer.
95    // Load noise texture to give "fluffy-ness" to the snow. Displace the sampling of the noise.
96    vec3 cloudsNoise = noise.eval(uvAdjusted * 7000 + vec2(fragCoord.y, -fragCoord.x)).rgb;
97    // Add dither to give texture to the snow and ruffle the edges.
98    float dither = abs(triangleNoise(fragCoord * 0.01));
99
100    // Get the accumulated snow buffer. r contains its mask, g contains some random noise.
101    vec2 accSnow = accumulatedSnow.eval(adjustedUv).rg;
102    // Sharpen the mask of the accumulated snow, but not in excess.
103    float accSnowMask = smoothstep(0.1, 0.9, /* mask= */ accSnow.r);
104    // Makes the edges of the snow layer accumulation rougher.
105    accSnowMask = map(accSnowMask, 1. - cloudsNoise.b - 0.3 * dither, 1., 0., 1.);
106    // Load snow texture and dither. Make it have gray-ish values.
107    float accSnowTexture = smoothstep(0.2, 0.7, /* noise= */ accSnow.g) * 0.7;
108    accSnowTexture = map(accSnowTexture, dither - 1, 1, 0, 1);
109    // Adjust snow texture coverage/shape.
110    accSnowTexture = map(accSnowTexture, 0.67, 0.8, 0, 1);
111    accSnowMask = map(accSnowMask, 0., 1., 0., 1.- 0.6 * accSnowTexture - 0.35 * dither);
112
113    color.rgb = normalBlendNotPremultiplied(color.rgb, snowColor.rgb, snowColor.a * accSnowMask);
114
115    // 5. Generate snow in front of the subject.
116    for (float i = midSnowLayerIndex; i >= closestSnowLayerIndex; i--) {
117        Snow snow = generateSnow(
118            uv,
119            screenAspectRatio,
120            time,
121            gridSize,
122            /* layer number = */ i,
123            closestSnowLayerIndex,
124            farthestSnowLayerIndex);
125
126        color.rgb =
127            normalBlendNotPremultiplied(color.rgb, snowColor.rgb, snowColor.a * snow.flakeMask);
128    }
129
130    return color;
131}
132