1/*
2 * Copyright (C) 2023 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
17// Copied from frameworks/base/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/
18// shaderutil/ShaderUtilLibrary.kt
19
20// Integer mod.
21int imod(int a, int b) {
22    return a - (b * (a / b));
23}
24
25ivec3 imod(ivec3 a, int b) {
26    return ivec3(imod(a.x, b), imod(a.y, b), imod(a.z, b));
27}
28
29// Integer based hash function with the return range of [-1, 1].
30vec3 hash(vec3 p) {
31    ivec3 v = ivec3(p);
32    v = v * 1671731 + 10139267;
33
34    v.x += v.y * v.z;
35    v.y += v.z * v.x;
36    v.z += v.x * v.y;
37
38    ivec3 v2 = v / 65536; // v >> 16
39    v = imod((10 - imod((v + v2), 10)), 10); // v ^ v2
40
41    v.x += v.y * v.z;
42    v.y += v.z * v.x;
43    v.z += v.x * v.y;
44
45    // Use sin and cos to map the range to [-1, 1].
46    return vec3(sin(float(v.x)), cos(float(v.y)), sin(float(v.z)));
47}
48
49// Skew factors (non-uniform).
50const float SKEW = 0.3333333;  // 1/3
51const float UNSKEW = 0.1666667;  // 1/6
52
53// Return range roughly [-1,1].
54// It's because the hash function (that returns a random gradient vector) returns
55// different magnitude of vectors. Noise doesn't have to be in the precise range thus
56// skipped normalize.
57half simplex3d(vec3 p) {
58    // Skew the input coordinate, so that we get squashed cubical grid
59    vec3 s = floor(p + (p.x + p.y + p.z) * SKEW);
60
61    // Unskew back
62    vec3 u = s - (s.x + s.y + s.z) * UNSKEW;
63
64    // Unskewed coordinate that is relative to p, to compute the noise contribution
65    // based on the distance.
66    vec3 c0 = p - u;
67
68    // We have six simplices (in this case tetrahedron, since we are in 3D) that we
69    // could possibly in.
70    // Here, we are finding the correct tetrahedron (simplex shape), and traverse its
71    // four vertices (c0..3) when computing noise contribution.
72    // The way we find them is by comparing c0's x,y,z values.
73    // For example in 2D, we can find the triangle (simplex shape in 2D) that we are in
74    // by comparing x and y values. i.e. x>y lower, x<y, upper triangle.
75    // Same applies in 3D.
76    //
77    // Below indicates the offsets (or offset directions) when c0=(x0,y0,z0)
78    // x0>y0>z0: (1,0,0), (1,1,0), (1,1,1)
79    // x0>z0>y0: (1,0,0), (1,0,1), (1,1,1)
80    // z0>x0>y0: (0,0,1), (1,0,1), (1,1,1)
81    // z0>y0>x0: (0,0,1), (0,1,1), (1,1,1)
82    // y0>z0>x0: (0,1,0), (0,1,1), (1,1,1)
83    // y0>x0>z0: (0,1,0), (1,1,0), (1,1,1)
84    //
85    // The rule is:
86    // * For offset1, set 1 at the max component, otherwise 0.
87    // * For offset2, set 0 at the min component, otherwise 1.
88    // * For offset3, set 1 for all.
89    //
90    // Encode x0-y0, y0-z0, z0-x0 in a vec3
91    vec3 en = c0 - c0.yzx;
92
93    // Each represents whether x0>y0, y0>z0, z0>x0
94    en = step(vec3(0.), en);
95
96    // en.zxy encodes z0>x0, x0>y0, y0>x0
97    vec3 offset1 = en * (1. - en.zxy); // find max
98    vec3 offset2 = 1. - en.zxy * (1. - en); // 1-(find min)
99    vec3 offset3 = vec3(1.);
100
101    vec3 c1 = c0 - offset1 + UNSKEW;
102    vec3 c2 = c0 - offset2 + UNSKEW * 2.;
103    vec3 c3 = c0 - offset3 + UNSKEW * 3.;
104
105    // Kernel summation: dot(max(0, r^2-d^2))^4, noise contribution)
106    //
107    // First compute d^2, squared distance to the point.
108    vec4 w; // w = max(0, r^2 - d^2))
109    w.x = dot(c0, c0);
110    w.y = dot(c1, c1);
111    w.z = dot(c2, c2);
112    w.w = dot(c3, c3);
113
114    // Noise contribution should decay to zero before they cross the simplex boundary.
115    // Usually r^2 is 0.5 or 0.6;
116    // 0.5 ensures continuity but 0.6 increases the visual quality for the application
117    // where discontinuity isn't noticeable.
118    w = max(0.6 - w, 0.);
119
120    // Noise contribution from each point.
121    vec4 nc;
122    nc.x = dot(hash(s), c0);
123    nc.y = dot(hash(s + offset1), c1);
124    nc.z = dot(hash(s + offset2), c2);
125    nc.w = dot(hash(s + offset3), c3);
126
127    nc *= w * w * w * w;
128
129    // Add all the noise contributions.
130    // Should multiply by the possible max contribution to adjust the range in [-1,1].
131    return dot(vec4(32.), nc);
132}
133