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.shaderutil.ShaderUtilLibrary
20 import java.lang.Float.max
21 
22 /**
23  * Shader that renders turbulence simplex noise, by default no octave.
24  *
25  * @param useFractal whether to use fractal noise (4 octaves).
26  */
27 class TurbulenceNoiseShader(useFractal: Boolean = false) :
28     RuntimeShader(if (useFractal) FRACTAL_NOISE_SHADER else SIMPLEX_NOISE_SHADER) {
29     // language=AGSL
30     companion object {
31         private const val UNIFORMS =
32             """
33             uniform float in_gridNum;
34             uniform vec3 in_noiseMove;
35             uniform vec2 in_size;
36             uniform float in_aspectRatio;
37             uniform float in_opacity;
38             uniform float in_pixelDensity;
39             uniform float in_inverseLuma;
40             uniform half in_lumaMatteBlendFactor;
41             uniform half in_lumaMatteOverallBrightness;
42             layout(color) uniform vec4 in_color;
43             layout(color) uniform vec4 in_backgroundColor;
44         """
45 
46         private const val SIMPLEX_SHADER =
47             """
48             vec4 main(vec2 p) {
49                 vec2 uv = p / in_size.xy;
50                 uv.x *= in_aspectRatio;
51 
52                 vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
53                 // Bring it to [0, 1] range.
54                 float luma = (simplex3d(noiseP) * in_inverseLuma) * 0.5 + 0.5;
55                 luma = saturate(luma * in_lumaMatteBlendFactor + in_lumaMatteOverallBrightness)
56                         * in_opacity;
57                 vec3 mask = maskLuminosity(in_color.rgb, luma);
58                 vec3 color = in_backgroundColor.rgb + mask * 0.6;
59 
60                 // Add dither with triangle distribution to avoid color banding. Dither in the
61                 // shader here as we are in gamma space.
62                 float dither = triangleNoise(p * in_pixelDensity) / 255.;
63 
64                 // The result color should be pre-multiplied, i.e. [R*A, G*A, B*A, A], thus need to
65                 // multiply rgb with a to get the correct result.
66                 color = (color + dither.rrr) * in_opacity;
67                 return vec4(color, in_opacity);
68             }
69         """
70 
71         private const val FRACTAL_SHADER =
72             """
73             vec4 main(vec2 p) {
74                 vec2 uv = p / in_size.xy;
75                 uv.x *= in_aspectRatio;
76 
77                 vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
78                 // Bring it to [0, 1] range.
79                 float luma = (simplex3d_fractal(noiseP) * in_inverseLuma) * 0.5 + 0.5;
80                 luma = saturate(luma * in_lumaMatteBlendFactor + in_lumaMatteOverallBrightness)
81                         * in_opacity;
82                 vec3 mask = maskLuminosity(in_color.rgb, luma);
83                 vec3 color = in_backgroundColor.rgb + mask * 0.6;
84 
85                 // Skip dithering.
86                 return vec4(color * in_opacity, in_opacity);
87             }
88         """
89 
90         private const val SIMPLEX_NOISE_SHADER =
91             ShaderUtilLibrary.SHADER_LIB + UNIFORMS + SIMPLEX_SHADER
92         private const val FRACTAL_NOISE_SHADER =
93             ShaderUtilLibrary.SHADER_LIB + UNIFORMS + FRACTAL_SHADER
94     }
95 
96     /** Sets the number of grid for generating noise. */
97     fun setGridCount(gridNumber: Float = 1.0f) {
98         setFloatUniform("in_gridNum", gridNumber)
99     }
100 
101     /**
102      * Sets the pixel density of the screen.
103      *
104      * Used it for noise dithering.
105      */
106     fun setPixelDensity(pixelDensity: Float) {
107         setFloatUniform("in_pixelDensity", pixelDensity)
108     }
109 
110     /** Sets the noise color of the effect. */
111     fun setColor(color: Int) {
112         setColorUniform("in_color", color)
113     }
114 
115     /** Sets the background color of the effect. */
116     fun setBackgroundColor(color: Int) {
117         setColorUniform("in_backgroundColor", color)
118     }
119 
120     /**
121      * Sets the opacity to achieve fade in/ out of the animation.
122      *
123      * Expected value range is [1, 0].
124      */
125     fun setOpacity(opacity: Float) {
126         setFloatUniform("in_opacity", opacity)
127     }
128 
129     /** Sets the size of the shader. */
130     fun setSize(width: Float, height: Float) {
131         setFloatUniform("in_size", width, height)
132         setFloatUniform("in_aspectRatio", width / max(height, 0.001f))
133     }
134 
135     /**
136      * Sets blend and brightness factors of the luma matte.
137      *
138      * @param lumaMatteBlendFactor increases or decreases the amount of variance in noise. Setting
139      *   this a lower number removes variations. I.e. the turbulence noise will look more blended.
140      *   Expected input range is [0, 1]. more dimmed.
141      * @param lumaMatteOverallBrightness adds the overall brightness of the turbulence noise.
142      *   Expected input range is [0, 1].
143      *
144      * Example usage: You may want to apply a small number to [lumaMatteBlendFactor], such as 0.2,
145      * which makes the noise look softer. However it makes the overall noise look dim, so you want
146      * offset something like 0.3 for [lumaMatteOverallBrightness] to bring back its overall
147      * brightness.
148      */
149     fun setLumaMatteFactors(
150         lumaMatteBlendFactor: Float = 1f,
151         lumaMatteOverallBrightness: Float = 0f
152     ) {
153         setFloatUniform("in_lumaMatteBlendFactor", lumaMatteBlendFactor)
154         setFloatUniform("in_lumaMatteOverallBrightness", lumaMatteOverallBrightness)
155     }
156 
157     /**
158      * Sets whether to inverse the luminosity of the noise.
159      *
160      * By default noise will be used as a luma matte as is. This means that you will see color in
161      * the brighter area. If you want to invert it, meaning blend color onto the darker side, set to
162      * true.
163      */
164     fun setInverseNoiseLuminosity(inverse: Boolean) {
165         setFloatUniform("in_inverseLuma", if (inverse) -1f else 1f)
166     }
167 
168     /** Current noise movements in x, y, and z axes. */
169     var noiseOffsetX: Float = 0f
170         private set
171     var noiseOffsetY: Float = 0f
172         private set
173     var noiseOffsetZ: Float = 0f
174         private set
175 
176     /** Sets noise move offset in x, y, and z direction. */
177     fun setNoiseMove(x: Float, y: Float, z: Float) {
178         noiseOffsetX = x
179         noiseOffsetY = y
180         noiseOffsetZ = z
181         setFloatUniform("in_noiseMove", noiseOffsetX, noiseOffsetY, noiseOffsetZ)
182     }
183 }
184