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