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