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