1 /*
2  * Copyright (C) 2022 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 package com.android.systemui.surfaceeffects.turbulencenoise
17 
18 import android.graphics.RuntimeShader
19 import com.android.systemui.surfaceeffects.shaders.SolidColorShader
20 import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary
21 import java.lang.Float.max
22 
23 /**
24  * Shader that renders turbulence simplex noise, by default no octave.
25  *
26  * @param baseType the base [Type] of the shader.
27  */
28 class TurbulenceNoiseShader(val baseType: Type = Type.SIMPLEX_NOISE) :
29     RuntimeShader(getShader(baseType)) {
30     // language=AGSL
31     companion object {
32         /** Uniform name for the background buffer (e.g. image, solid color, etc.). */
33         const val BACKGROUND_UNIFORM = "in_src"
34         private const val UNIFORMS =
35             """
36             uniform shader ${BACKGROUND_UNIFORM};
37             uniform float in_gridNum;
38             uniform vec3 in_noiseMove;
39             uniform vec2 in_size;
40             uniform float in_aspectRatio;
41             uniform float in_opacity;
42             uniform float in_pixelDensity;
43             uniform float in_inverseLuma;
44             uniform half in_lumaMatteBlendFactor;
45             uniform half in_lumaMatteOverallBrightness;
46             layout(color) uniform vec4 in_color;
47             layout(color) uniform vec4 in_screenColor;
48         """
49 
50         private const val SIMPLEX_SHADER =
51             """
52             vec4 main(vec2 p) {
53                 vec2 uv = p / in_size.xy;
54                 uv.x *= in_aspectRatio;
55 
56                 // Compute turbulence effect with the uv distorted with simplex noise.
57                 vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
58                 vec3 color = getColorTurbulenceMask(simplex3d(noiseP) * in_inverseLuma);
59 
60                 // Blend the result with the background color.
61                 color = in_src.eval(p).rgb + color * 0.6;
62 
63                 // Add dither with triangle distribution to avoid color banding. Dither in the
64                 // shader here as we are in gamma space.
65                 float dither = triangleNoise(p * in_pixelDensity) / 255.;
66                 color += dither.rrr;
67 
68                 // Return the pre-multiplied alpha result, i.e. [R*A, G*A, B*A, A].
69                 return vec4(color * in_opacity, in_opacity);
70             }
71         """
72 
73         private const val FRACTAL_SHADER =
74             """
75             vec4 main(vec2 p) {
76                 vec2 uv = p / in_size.xy;
77                 uv.x *= in_aspectRatio;
78 
79                 vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
80                 vec3 color = getColorTurbulenceMask(simplex3d_fractal(noiseP) * in_inverseLuma);
81 
82                 // Blend the result with the background color.
83                 color = in_src.eval(p).rgb + color * 0.6;
84 
85                 // Skip dithering.
86                 return vec4(color * in_opacity, in_opacity);
87             }
88         """
89 
90         /**
91          * This effect has two layers: color turbulence effect with sparkles on top.
92          * 1. Gets the luma matte using Simplex noise.
93          * 2. Generate a colored turbulence layer with the luma matte.
94          * 3. Generate a colored sparkle layer with the same luma matter.
95          * 4. Apply a screen color to the background image.
96          * 5. Composite the previous result with the color turbulence.
97          * 6. Composite the latest result with the sparkles.
98          */
99         private const val SIMPLEX_SPARKLE_SHADER =
100             """
101             vec4 main(vec2 p) {
102                 vec2 uv = p / in_size.xy;
103                 uv.x *= in_aspectRatio;
104 
105                 vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
106                 // Luma is used for both color and sparkle masks.
107                 float luma = simplex3d(noiseP) * in_inverseLuma;
108 
109                 // Get color layer (color mask with in_color applied)
110                 vec3 colorLayer = getColorTurbulenceMask(simplex3d(noiseP) * in_inverseLuma);
111                 float dither = triangleNoise(p * in_pixelDensity) / 255.;
112                 colorLayer += dither.rrr;
113 
114                 // Get sparkle layer (sparkle mask with particles & in_color applied)
115                 vec3 sparkleLayer = getSparkleTurbulenceMask(luma, p);
116 
117                 // Composite with the background.
118                 half4 bgColor = in_src.eval(p);
119                 half sparkleOpacity = smoothstep(0, 0.75, in_opacity);
120 
121                 half3 effect = screen(bgColor.rgb, in_screenColor.rgb);
122                 effect = screen(effect, colorLayer * 0.22);
123                 effect += sparkleLayer * sparkleOpacity;
124 
125                 return mix(bgColor, vec4(effect, 1.), in_opacity);
126             }
127         """
128 
129         private const val COMMON_FUNCTIONS =
130             /**
131              * Below two functions generate turbulence layers (color or sparkles applied) with the
132              * given luma matte. They both return a mask with in_color applied.
133              */
134             """
135             vec3 getColorTurbulenceMask(float luma) {
136                 // Bring it to [0, 1] range.
137                 luma = luma * 0.5 + 0.5;
138 
139                 half colorLuma =
140                     saturate(luma * in_lumaMatteBlendFactor + in_lumaMatteOverallBrightness)
141                     * in_opacity;
142                 vec3 colorLayer = maskLuminosity(in_color.rgb, colorLuma);
143 
144                 return colorLayer;
145             }
146 
147             vec3 getSparkleTurbulenceMask(float luma, vec2 p) {
148                 half lumaIntensity = 1.75;
149                 half lumaBrightness = -1.3;
150                 half sparkleLuma = max(luma * lumaIntensity + lumaBrightness, 0.);
151 
152                 float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_noiseMove.z);
153                 vec3 sparkleLayer = maskLuminosity(in_color.rgb * sparkle, sparkleLuma);
154 
155                 return sparkleLayer;
156             }
157         """
158         private const val SIMPLEX_NOISE_SHADER =
159             ShaderUtilLibrary.SHADER_LIB + UNIFORMS + COMMON_FUNCTIONS + SIMPLEX_SHADER
160         private const val FRACTAL_NOISE_SHADER =
161             ShaderUtilLibrary.SHADER_LIB + UNIFORMS + COMMON_FUNCTIONS + FRACTAL_SHADER
162         private const val SPARKLE_NOISE_SHADER =
163             ShaderUtilLibrary.SHADER_LIB + UNIFORMS + COMMON_FUNCTIONS + SIMPLEX_SPARKLE_SHADER
164 
165         enum class Type {
166             /** Effect with a simple color noise turbulence. */
167             SIMPLEX_NOISE,
168             /** Effect with a simple color noise turbulence, with fractal. */
169             SIMPLEX_NOISE_FRACTAL,
170             /** Effect with color & sparkle turbulence with screen color layer. */
171             SIMPLEX_NOISE_SPARKLE
172         }
173 
getShadernull174         fun getShader(type: Type): String {
175             return when (type) {
176                 Type.SIMPLEX_NOISE -> SIMPLEX_NOISE_SHADER
177                 Type.SIMPLEX_NOISE_FRACTAL -> FRACTAL_NOISE_SHADER
178                 Type.SIMPLEX_NOISE_SPARKLE -> SPARKLE_NOISE_SHADER
179             }
180         }
181     }
182 
183     /** Convenient way for updating multiple uniform values via config object. */
applyConfignull184     fun applyConfig(config: TurbulenceNoiseAnimationConfig) {
185         setGridCount(config.gridCount)
186         setPixelDensity(config.pixelDensity)
187         setColor(config.color)
188         setScreenColor(config.screenColor)
189         setSize(config.width, config.height)
190         setLumaMatteFactors(config.lumaMatteBlendFactor, config.lumaMatteOverallBrightness)
191         setInverseNoiseLuminosity(config.shouldInverseNoiseLuminosity)
192         setNoiseMove(config.noiseOffsetX, config.noiseOffsetY, config.noiseOffsetZ)
193     }
194 
195     /** Sets the number of grid for generating noise. */
setGridCountnull196     fun setGridCount(gridNumber: Float = 1.0f) {
197         setFloatUniform("in_gridNum", gridNumber)
198     }
199 
200     /**
201      * Sets the pixel density of the screen.
202      *
203      * Used it for noise dithering.
204      */
setPixelDensitynull205     fun setPixelDensity(pixelDensity: Float) {
206         setFloatUniform("in_pixelDensity", pixelDensity)
207     }
208 
209     /** Sets the noise color of the effect. Alpha is ignored. */
setColornull210     fun setColor(color: Int) {
211         setColorUniform("in_color", color)
212     }
213 
214     /**
215      * Sets the color that is used for blending on top of the background color/image. Only relevant
216      * to [Type.SIMPLEX_NOISE_SPARKLE].
217      */
setScreenColornull218     fun setScreenColor(color: Int) {
219         setColorUniform("in_screenColor", color)
220     }
221 
222     /**
223      * Sets the background color of the effect. Alpha is ignored. If you are using [RenderEffect],
224      * no need to call this function since the background image of the View will be used.
225      */
setBackgroundColornull226     fun setBackgroundColor(color: Int) {
227         setInputShader(BACKGROUND_UNIFORM, SolidColorShader(color))
228     }
229 
230     /**
231      * Sets the opacity of the effect. Not intended to set by the client as it is used for
232      * ease-in/out animations.
233      *
234      * Expected value range is [1, 0].
235      */
setOpacitynull236     fun setOpacity(opacity: Float) {
237         setFloatUniform("in_opacity", opacity)
238     }
239 
240     /** Sets the size of the shader. */
setSizenull241     fun setSize(width: Float, height: Float) {
242         setFloatUniform("in_size", width, height)
243         setFloatUniform("in_aspectRatio", width / max(height, 0.001f))
244     }
245 
246     /**
247      * Sets blend and brightness factors of the luma matte.
248      *
249      * @param lumaMatteBlendFactor increases or decreases the amount of variance in noise. Setting
250      *   this a lower number removes variations. I.e. the turbulence noise will look more blended.
251      *   Expected input range is [0, 1].
252      * @param lumaMatteOverallBrightness adds the overall brightness of the turbulence noise.
253      *   Expected input range is [0, 1].
254      *
255      * Example usage: You may want to apply a small number to [lumaMatteBlendFactor], such as 0.2,
256      * which makes the noise look softer. However it makes the overall noise look dim, so you want
257      * offset something like 0.3 for [lumaMatteOverallBrightness] to bring back its overall
258      * brightness.
259      */
setLumaMatteFactorsnull260     fun setLumaMatteFactors(
261         lumaMatteBlendFactor: Float = 1f,
262         lumaMatteOverallBrightness: Float = 0f
263     ) {
264         setFloatUniform("in_lumaMatteBlendFactor", lumaMatteBlendFactor)
265         setFloatUniform("in_lumaMatteOverallBrightness", lumaMatteOverallBrightness)
266     }
267 
268     /**
269      * Sets whether to inverse the luminosity of the noise.
270      *
271      * By default noise will be used as a luma matte as is. This means that you will see color in
272      * the brighter area. If you want to invert it, meaning blend color onto the darker side, set to
273      * true.
274      */
setInverseNoiseLuminositynull275     fun setInverseNoiseLuminosity(inverse: Boolean) {
276         setFloatUniform("in_inverseLuma", if (inverse) -1f else 1f)
277     }
278 
279     /** Current noise movements in x, y, and z axes. */
280     var noiseOffsetX: Float = 0f
281         private set
282     var noiseOffsetY: Float = 0f
283         private set
284     var noiseOffsetZ: Float = 0f
285         private set
286 
287     /** Sets noise move offset in x, y, and z direction. */
setNoiseMovenull288     fun setNoiseMove(x: Float, y: Float, z: Float) {
289         noiseOffsetX = x
290         noiseOffsetY = y
291         noiseOffsetZ = z
292         setFloatUniform("in_noiseMove", noiseOffsetX, noiseOffsetY, noiseOffsetZ)
293     }
294 }
295