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
17const float SKEW = 0.366025404; // (sqrt(3)-1)/2
18const float UNSKEW = 0.211324865; // (3-sqrt(3))/6
19
20half2 hash2d(vec2 p) {
21    p = vec2(dot(p,vec2(157.1, 235.7)), dot(p,vec2(573.5, 13.3)));
22    return fract(sin(p) * 877.343) * 2. -1.; // [-1, 1]
23}
24
25half hash1d(vec2 p) {
26    return fract(sin(dot(p, vec2(343.0, 49.0)))) * 2. -1.; // [-1, 1]
27}
28
29vec2 getVectorFromAngle(float theta) {
30    return vec2(cos(theta), sin(theta));
31}
32
33int imod(int a, int b) {
34    return a - (b * (a / b));
35}
36
37// 2D hash without bit-wise operations
38vec2 ihash2d(vec2 p) {
39    int a = int(p.x + p.y) * 15823;
40    int b = a * 65536; // a << 16
41
42    a = imod((10 - imod((a + b), 10)), 10); // a ^ b = (base - (a + b) % base) % base
43    a = (a * (a * a * 38833 + 683873) + 19734581);
44    a /= 65536; // a >> 16
45
46    float af = float(a);
47    return vec2(cos(af), sin(af));
48}
49
50// Returns kernel summation from the given simplex vertices (v0, v1, v2), and their corresponding
51// gradients (g0, g1, g2).
52float kernel_summation(vec2 v0, vec2 v1, vec2 v2, vec2 g0, vec2 g1, vec2 g2) {
53   vec3 w = max(0.5 - vec3(dot(v0, v0), dot(v1, v1), dot(v2, v2)), 0.0);
54
55    w = w*w*w*w;
56    vec3 n = w * vec3(dot(v0, g0), dot(v1, g1), dot(v2, g2));
57
58    return dot(n, vec3(32.0));
59}
60
61// 2D Simplex noise with dynamic gradient vectors. Return value [-1, 1].
62//
63// This method produces similar visuals to Simplex noise 3D, but at a lower computational cost.
64// The snapshot of the noise is the same as a regular Simplex noise. However, when animated, it
65// creates a swirling motion that is more suitable for flow-y effects.
66//
67// The difference in motion is not noticeable unless the following conditions are met:
68// 1) The rotation offset is identical for all vertex gradients.
69// 2) The noise is moving quickly.
70// 3) The noise is tiled.
71//
72// This method is recommended for use because it is significantly more performant than 3D Simplex
73// noise. It is especially useful for simulating fire, clouds, and fog, which all have advection.
74//
75// rot is an angle in radian that you want to step for each dt.
76float simplex2d_flow(vec2 p, float rot, float time) {
77    // Skew the input coordinate and find the simplex index.
78    vec2 i = floor(p + (p.x + p.y) * SKEW);
79    // First vertex of the triangle.
80    vec2 v0 = p - i + (i.x + i.y) * UNSKEW;
81
82    // Find two other vertices.
83    // Determines which triangle we should walk.
84    // If y>x, m=0 upper triangle, x>y, m=1 lower triangle.
85    float side = step(v0.y, v0.x);
86    vec2 walk = vec2(side, 1.0 - side);
87
88    vec2 v1 = v0 - walk + UNSKEW;
89    vec2 v2 = v0 - 1.0 + 2.*UNSKEW;
90
91    // Get random gradient vector.
92    vec2 g0 = ihash2d(i);
93    vec2 g1 = ihash2d(i+walk);
94    vec2 g2 = ihash2d(i+1.0);
95
96    // Make the gradient vectors dynamic by adding rotations.
97    g0 += getVectorFromAngle(rot * time * hash1d(i));
98    g1 += getVectorFromAngle(rot * time * hash1d(i+walk));
99    g2 += getVectorFromAngle(rot * time * hash1d(i+1.));
100
101    return kernel_summation(v0, v1, v2, g0, g1, g2);
102}
103
104// 2D Simplex noise
105float simplex2d(vec2 p) {
106    // Skew the input coordinate and find the simplex index.
107    vec2 i = floor(p + (p.x + p.y) * SKEW);
108    // First vertex of the triangle.
109    vec2 v0 = p - i + (i.x + i.y) * UNSKEW;
110
111    // Find two other vertices.
112    // Determines which triangle we should walk.
113    // If y>x, m=0 upper triangle, x>y, m=1 lower triangle.
114    float side = step(v0.y, v0.x);
115    vec2 walk = vec2(side, 1.0 - side);
116
117    vec2 v1 = v0 - walk + UNSKEW;
118    vec2 v2 = v0 - 1.0 + 2.*UNSKEW;
119
120    // Get random gradient vector.
121    vec2 g0 = ihash2d(i);
122    vec2 g1 = ihash2d(i+walk);
123    vec2 g2 = ihash2d(i+1.0);
124
125    return kernel_summation(v0, v1, v2, g0, g1, g2);
126}
127