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