• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 SIMPLEX_SIMPLE_SHADER =
74             """
75             vec4 main(vec2 p) {
76                 vec2 uv = p / in_size.xy;
77                 uv.x *= in_aspectRatio;
78 
79                 // Compute turbulence effect with the uv distorted with simplex noise.
80                 vec3 noisePos = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
81                 float mixFactor = simplex3d(noisePos) * 0.5 + 0.5;
82                 return mix(in_color, in_screenColor, mixFactor);
83             }
84         """
85 
86         private const val FRACTAL_SHADER =
87             """
88             vec4 main(vec2 p) {
89                 vec2 uv = p / in_size.xy;
90                 uv.x *= in_aspectRatio;
91 
92                 vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
93                 vec3 color = getColorTurbulenceMask(simplex3d_fractal(noiseP) * in_inverseLuma);
94 
95                 // Blend the result with the background color.
96                 color = in_src.eval(p).rgb + color * 0.6;
97 
98                 // Skip dithering.
99                 return vec4(color * in_opacity, in_opacity);
100             }
101         """
102 
103         /**
104          * This effect has two layers: color turbulence effect with sparkles on top.
105          * 1. Gets the luma matte using Simplex noise.
106          * 2. Generate a colored turbulence layer with the luma matte.
107          * 3. Generate a colored sparkle layer with the same luma matter.
108          * 4. Apply a screen color to the background image.
109          * 5. Composite the previous result with the color turbulence.
110          * 6. Composite the latest result with the sparkles.
111          */
112         private const val SIMPLEX_SPARKLE_SHADER =
113             """
114             vec4 main(vec2 p) {
115                 vec2 uv = p / in_size.xy;
116                 uv.x *= in_aspectRatio;
117 
118                 vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
119                 // Luma is used for both color and sparkle masks.
120                 float luma = simplex3d(noiseP) * in_inverseLuma;
121 
122                 // Get color layer (color mask with in_color applied)
123                 vec3 colorLayer = getColorTurbulenceMask(simplex3d(noiseP) * in_inverseLuma);
124                 float dither = triangleNoise(p * in_pixelDensity) / 255.;
125                 colorLayer += dither.rrr;
126 
127                 // Get sparkle layer (sparkle mask with particles & in_color applied)
128                 vec3 sparkleLayer = getSparkleTurbulenceMask(luma, p);
129 
130                 // Composite with the background.
131                 half4 bgColor = in_src.eval(p);
132                 half sparkleOpacity = smoothstep(0, 0.75, in_opacity);
133 
134                 half3 effect = screen(bgColor.rgb, in_screenColor.rgb);
135                 effect = screen(effect, colorLayer * 0.22);
136                 effect += sparkleLayer * sparkleOpacity;
137 
138                 return mix(bgColor, vec4(effect, 1.), in_opacity);
139             }
140         """
141 
142         private const val COMMON_FUNCTIONS =
143             /**
144              * Below two functions generate turbulence layers (color or sparkles applied) with the
145              * given luma matte. They both return a mask with in_color applied.
146              */
147             """
148             vec3 getColorTurbulenceMask(float luma) {
149                 // Bring it to [0, 1] range.
150                 luma = luma * 0.5 + 0.5;
151 
152                 half colorLuma =
153                     saturate(luma * in_lumaMatteBlendFactor + in_lumaMatteOverallBrightness)
154                     * in_opacity;
155                 vec3 colorLayer = maskLuminosity(in_color.rgb, colorLuma);
156 
157                 return colorLayer;
158             }
159 
160             vec3 getSparkleTurbulenceMask(float luma, vec2 p) {
161                 half lumaIntensity = 1.75;
162                 half lumaBrightness = -1.3;
163                 half sparkleLuma = max(luma * lumaIntensity + lumaBrightness, 0.);
164 
165                 float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_noiseMove.z);
166                 vec3 sparkleLayer = maskLuminosity(in_color.rgb * sparkle, sparkleLuma);
167 
168                 return sparkleLayer;
169             }
170         """
171         private const val SIMPLEX_NOISE_SIMPLE_SHADER =
172             ShaderUtilLibrary.SHADER_LIB + UNIFORMS + SIMPLEX_SIMPLE_SHADER
173         private const val SIMPLEX_NOISE_SHADER =
174             ShaderUtilLibrary.SHADER_LIB + UNIFORMS + COMMON_FUNCTIONS + SIMPLEX_SHADER
175         private const val FRACTAL_NOISE_SHADER =
176             ShaderUtilLibrary.SHADER_LIB + UNIFORMS + COMMON_FUNCTIONS + FRACTAL_SHADER
177         private const val SPARKLE_NOISE_SHADER =
178             ShaderUtilLibrary.SHADER_LIB + UNIFORMS + COMMON_FUNCTIONS + SIMPLEX_SPARKLE_SHADER
179 
180         enum class Type {
181             /** Effect with a color noise turbulence with luma matte. */
182             SIMPLEX_NOISE,
183             /** Effect with a noise interpolating between foreground and background colors. */
184             SIMPLEX_NOISE_SIMPLE,
185             /** Effect with a simple color noise turbulence, with fractal. */
186             SIMPLEX_NOISE_FRACTAL,
187             /** Effect with color & sparkle turbulence with screen color layer. */
188             SIMPLEX_NOISE_SPARKLE,
189         }
190 
getShadernull191         fun getShader(type: Type): String {
192             return when (type) {
193                 Type.SIMPLEX_NOISE -> SIMPLEX_NOISE_SHADER
194                 Type.SIMPLEX_NOISE_SIMPLE -> SIMPLEX_NOISE_SIMPLE_SHADER
195                 Type.SIMPLEX_NOISE_FRACTAL -> FRACTAL_NOISE_SHADER
196                 Type.SIMPLEX_NOISE_SPARKLE -> SPARKLE_NOISE_SHADER
197             }
198         }
199     }
200 
201     /** Convenient way for updating multiple uniform values via config object. */
applyConfignull202     fun applyConfig(config: TurbulenceNoiseAnimationConfig) {
203         setGridCount(config.gridCount)
204         setPixelDensity(config.pixelDensity)
205         setColor(config.color)
206         setScreenColor(config.screenColor)
207         setSize(config.width, config.height)
208         setLumaMatteFactors(config.lumaMatteBlendFactor, config.lumaMatteOverallBrightness)
209         setInverseNoiseLuminosity(config.shouldInverseNoiseLuminosity)
210         setNoiseMove(config.noiseOffsetX, config.noiseOffsetY, config.noiseOffsetZ)
211     }
212 
213     /** Sets the number of grid for generating noise. */
setGridCountnull214     fun setGridCount(gridNumber: Float = 1.0f) {
215         setFloatUniform("in_gridNum", gridNumber)
216     }
217 
218     /**
219      * Sets the pixel density of the screen.
220      *
221      * Used it for noise dithering.
222      */
setPixelDensitynull223     fun setPixelDensity(pixelDensity: Float) {
224         setFloatUniform("in_pixelDensity", pixelDensity)
225     }
226 
227     /**
228      * Sets the noise color of the effect. Alpha is ignored for all types except
229      * [Type.SIMPLEX_NOISE_SIMPLE].
230      */
setColornull231     fun setColor(color: Int) {
232         setColorUniform("in_color", color)
233     }
234 
235     /** Sets the color that is used for blending on top of the background color/image. */
setScreenColornull236     fun setScreenColor(color: Int) {
237         setColorUniform("in_screenColor", color)
238     }
239 
240     /**
241      * Sets the background color of the effect. Alpha is ignored. If you are using [RenderEffect],
242      * no need to call this function since the background image of the View will be used.
243      */
setBackgroundColornull244     fun setBackgroundColor(color: Int) {
245         setInputShader(BACKGROUND_UNIFORM, SolidColorShader(color))
246     }
247 
248     /**
249      * Sets the opacity of the effect. Not intended to set by the client as it is used for
250      * ease-in/out animations.
251      *
252      * Expected value range is [1, 0].
253      */
setOpacitynull254     fun setOpacity(opacity: Float) {
255         setFloatUniform("in_opacity", opacity)
256     }
257 
258     /** Sets the size of the shader. */
setSizenull259     fun setSize(width: Float, height: Float) {
260         setFloatUniform("in_size", width, height)
261         setFloatUniform("in_aspectRatio", width / max(height, 0.001f))
262     }
263 
264     /**
265      * Sets blend and brightness factors of the luma matte.
266      *
267      * @param lumaMatteBlendFactor increases or decreases the amount of variance in noise. Setting
268      *   this a lower number removes variations. I.e. the turbulence noise will look more blended.
269      *   Expected input range is [0, 1].
270      * @param lumaMatteOverallBrightness adds the overall brightness of the turbulence noise.
271      *   Expected input range is [0, 1].
272      *
273      * Example usage: You may want to apply a small number to [lumaMatteBlendFactor], such as 0.2,
274      * which makes the noise look softer. However it makes the overall noise look dim, so you want
275      * offset something like 0.3 for [lumaMatteOverallBrightness] to bring back its overall
276      * brightness.
277      */
setLumaMatteFactorsnull278     fun setLumaMatteFactors(
279         lumaMatteBlendFactor: Float = 1f,
280         lumaMatteOverallBrightness: Float = 0f,
281     ) {
282         setFloatUniform("in_lumaMatteBlendFactor", lumaMatteBlendFactor)
283         setFloatUniform("in_lumaMatteOverallBrightness", lumaMatteOverallBrightness)
284     }
285 
286     /**
287      * Sets whether to inverse the luminosity of the noise.
288      *
289      * By default noise will be used as a luma matte as is. This means that you will see color in
290      * the brighter area. If you want to invert it, meaning blend color onto the darker side, set to
291      * true.
292      */
setInverseNoiseLuminositynull293     fun setInverseNoiseLuminosity(inverse: Boolean) {
294         setFloatUniform("in_inverseLuma", if (inverse) -1f else 1f)
295     }
296 
297     /** Current noise movements in x, y, and z axes. */
298     var noiseOffsetX: Float = 0f
299         private set
300 
301     var noiseOffsetY: Float = 0f
302         private set
303 
304     var noiseOffsetZ: Float = 0f
305         private set
306 
307     /** Sets noise move offset in x, y, and z direction. */
setNoiseMovenull308     fun setNoiseMove(x: Float, y: Float, z: Float) {
309         noiseOffsetX = x
310         noiseOffsetY = y
311         noiseOffsetZ = z
312         setFloatUniform("in_noiseMove", noiseOffsetX, noiseOffsetY, noiseOffsetZ)
313     }
314 }
315