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