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.shaderutil 17 18 /** Common utility functions that are used for computing shaders. */ 19 object ShaderUtilLibrary { 20 // language=AGSL 21 const val SHADER_LIB = 22 """ 23 float triangleNoise(vec2 n) { 24 n = fract(n * vec2(5.3987, 5.4421)); 25 n += dot(n.yx, n.xy + vec2(21.5351, 14.3137)); 26 float xy = n.x * n.y; 27 // compute in [0..2[ and remap to [-1.0..1.0[ 28 return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0; 29 } 30 31 const float PI = 3.1415926535897932384626; 32 33 float sparkles(vec2 uv, float t) { 34 float n = triangleNoise(uv); 35 float s = 0.0; 36 for (float i = 0; i < 4; i += 1) { 37 float l = i * 0.01; 38 float h = l + 0.1; 39 float o = smoothstep(n - l, h, n); 40 o *= abs(sin(PI * o * (t + 0.55 * i))); 41 s += o; 42 } 43 return s; 44 } 45 46 vec2 distort(vec2 p, float time, float distort_amount_radial, 47 float distort_amount_xy) { 48 float angle = atan(p.y, p.x); 49 return p + vec2(sin(angle * 8 + time * 0.003 + 1.641), 50 cos(angle * 5 + 2.14 + time * 0.00412)) * distort_amount_radial 51 + vec2(sin(p.x * 0.01 + time * 0.00215 + 0.8123), 52 cos(p.y * 0.01 + time * 0.005931)) * distort_amount_xy; 53 } 54 55 // Perceived luminosity (L′), not absolute luminosity. 56 half getLuminosity(vec3 c) { 57 return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; 58 } 59 60 // Creates a luminosity mask and clamp to the legal range. 61 vec3 maskLuminosity(vec3 dest, float lum) { 62 dest.rgb *= vec3(lum); 63 // Clip back into the legal range 64 dest = clamp(dest, vec3(0.), vec3(1.0)); 65 return dest; 66 } 67 68 // Integer mod. GLSL es 1.0 doesn't have integer mod :( 69 int imod(int a, int b) { 70 return a - (b * (a / b)); 71 } 72 73 ivec3 imod(ivec3 a, int b) { 74 return ivec3(imod(a.x, b), imod(a.y, b), imod(a.z, b)); 75 } 76 77 // Integer based hash function with the return range of [-1, 1]. 78 vec3 hash(vec3 p) { 79 ivec3 v = ivec3(p); 80 v = v * 1671731 + 10139267; 81 82 v.x += v.y * v.z; 83 v.y += v.z * v.x; 84 v.z += v.x * v.y; 85 86 ivec3 v2 = v / 65536; // v >> 16 87 v = imod((10 - imod((v + v2), 10)), 10); // v ^ v2 88 89 v.x += v.y * v.z; 90 v.y += v.z * v.x; 91 v.z += v.x * v.y; 92 93 // Use sin and cos to map the range to [-1, 1]. 94 return vec3(sin(float(v.x)), cos(float(v.y)), sin(float(v.z))); 95 } 96 97 // Skew factors (non-uniform). 98 const half SKEW = 0.3333333; // 1/3 99 const half UNSKEW = 0.1666667; // 1/6 100 101 // Return range roughly [-1,1]. 102 // It's because the hash function (that returns a random gradient vector) returns 103 // different magnitude of vectors. Noise doesn't have to be in the precise range thus 104 // skipped normalize. 105 half simplex3d(vec3 p) { 106 // Skew the input coordinate, so that we get squashed cubical grid 107 vec3 s = floor(p + (p.x + p.y + p.z) * SKEW); 108 109 // Unskew back 110 vec3 u = s - (s.x + s.y + s.z) * UNSKEW; 111 112 // Unskewed coordinate that is relative to p, to compute the noise contribution 113 // based on the distance. 114 vec3 c0 = p - u; 115 116 // We have six simplices (in this case tetrahedron, since we are in 3D) that we 117 // could possibly in. 118 // Here, we are finding the correct tetrahedron (simplex shape), and traverse its 119 // four vertices (c0..3) when computing noise contribution. 120 // The way we find them is by comparing c0's x,y,z values. 121 // For example in 2D, we can find the triangle (simplex shape in 2D) that we are in 122 // by comparing x and y values. i.e. x>y lower, x<y, upper triangle. 123 // Same applies in 3D. 124 // 125 // Below indicates the offsets (or offset directions) when c0=(x0,y0,z0) 126 // x0>y0>z0: (1,0,0), (1,1,0), (1,1,1) 127 // x0>z0>y0: (1,0,0), (1,0,1), (1,1,1) 128 // z0>x0>y0: (0,0,1), (1,0,1), (1,1,1) 129 // z0>y0>x0: (0,0,1), (0,1,1), (1,1,1) 130 // y0>z0>x0: (0,1,0), (0,1,1), (1,1,1) 131 // y0>x0>z0: (0,1,0), (1,1,0), (1,1,1) 132 // 133 // The rule is: 134 // * For offset1, set 1 at the max component, otherwise 0. 135 // * For offset2, set 0 at the min component, otherwise 1. 136 // * For offset3, set 1 for all. 137 // 138 // Encode x0-y0, y0-z0, z0-x0 in a vec3 139 vec3 en = c0 - c0.yzx; 140 // Each represents whether x0>y0, y0>z0, z0>x0 141 en = step(vec3(0.), en); 142 // en.zxy encodes z0>x0, x0>y0, y0>x0 143 vec3 offset1 = en * (1. - en.zxy); // find max 144 vec3 offset2 = 1. - en.zxy * (1. - en); // 1-(find min) 145 vec3 offset3 = vec3(1.); 146 147 vec3 c1 = c0 - offset1 + UNSKEW; 148 vec3 c2 = c0 - offset2 + UNSKEW * 2.; 149 vec3 c3 = c0 - offset3 + UNSKEW * 3.; 150 151 // Kernel summation: dot(max(0, r^2-d^2))^4, noise contribution) 152 // 153 // First compute d^2, squared distance to the point. 154 vec4 w; // w = max(0, r^2 - d^2)) 155 w.x = dot(c0, c0); 156 w.y = dot(c1, c1); 157 w.z = dot(c2, c2); 158 w.w = dot(c3, c3); 159 160 // Noise contribution should decay to zero before they cross the simplex boundary. 161 // Usually r^2 is 0.5 or 0.6; 162 // 0.5 ensures continuity but 0.6 increases the visual quality for the application 163 // where discontinuity isn't noticeable. 164 w = max(0.6 - w, 0.); 165 166 // Noise contribution from each point. 167 vec4 nc; 168 nc.x = dot(hash(s), c0); 169 nc.y = dot(hash(s + offset1), c1); 170 nc.z = dot(hash(s + offset2), c2); 171 nc.w = dot(hash(s + offset3), c3); 172 173 nc *= w*w*w*w; 174 175 // Add all the noise contributions. 176 // Should multiply by the possible max contribution to adjust the range in [-1,1]. 177 return dot(vec4(32.), nc); 178 } 179 180 // Random rotations. 181 // The way you create fractal noise is layering simplex noise with some rotation. 182 // To make random cloud looking noise, the rotations should not align. (Otherwise it 183 // creates patterned noise). 184 // Below rotations only rotate in one axis. 185 const mat3 rot1 = mat3(1.0, 0. ,0., 0., 0.15, -0.98, 0., 0.98, 0.15); 186 const mat3 rot2 = mat3(-0.95, 0. ,-0.3, 0., 1., 0., 0.3, 0., -0.95); 187 const mat3 rot3 = mat3(1.0, 0. ,0., 0., -0.44, -0.89, 0., 0.89, -0.44); 188 189 // Octave = 4 190 // Divide each coefficient by 3 to produce more grainy noise. 191 half simplex3d_fractal(vec3 p) { 192 return 0.675 * simplex3d(p * rot1) + 0.225 * simplex3d(2.0 * p * rot2) 193 + 0.075 * simplex3d(4.0 * p * rot3) + 0.025 * simplex3d(8.0 * p); 194 } 195 196 // Screen blend 197 vec3 screen(vec3 dest, vec3 src) { 198 return dest + src - dest * src; 199 } 200 """ 201 } 202