summaryrefslogtreecommitdiffstats
path: root/math.cginc
blob: 6b24a55ba3835051e5841aeadecb5296d774a25f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#ifndef __MATH_INC
#define __MATH_INC

#define PI              3.14159265358979323846264f
#define TAU             (2.0f * PI)
#define HALF_PI         (PI * 0.5f)
#define RCP_PI          (1.0f / PI)
#define RCP_TAU         (1.0f / TAU)
#define PHI             1.618033989f
#define RCP_PHI         0.618033989f
#define SQRT_2          1.414213562f
#define SQRT_2_RCP      0.707106781f
#define RCP_SQRT_2      0.707106781f
#define RCP_SQRT_3      0.577350269f
#define TWO_OVER_THREE  0.6666666666666666f
#define SQRT_3          1.73205081f
#define SQRT_3_OVER_2   0.8660254037844386f
#define EULERS_CONSTANT 2.718281828f

float sin_noise_3d(float3 uvw) {
  return sin(uvw[0]) * sin(uvw[1]) * sin(uvw[2]);
}

float sin_noise_3d_fbm(float3 uvw, uint octaves, float k, float strength) {
  float result = 0;
  float factor = 1.0f;
  for (uint i = 0; i < octaves; i++) {
    result += sin_noise_3d(uvw) * factor * strength;
    uvw *= k;
    factor /= k;
  }
  return result;
}

// Wrap NoL. Assume it's already clamped.
// At k=0, you get standard lambertian shading.
// At k=0.5, you get half-lambertian shading.
// At k=1.0, you get flat shading.
// k must be on [0, 1].
// Energy preserving, within some small bound.
float wrapNoL(float NoL, float k) {
  float lambertian = NoL;
  float tmp = (NoL + 0.5f) / (1.0f + 0.5f);
  float half_lambertian = tmp * tmp;
  float flat = RCP_PI;

  if (k < 0.5) {
    return lerp(lambertian, half_lambertian, k * 2.0f);
  } else {
    return lerp(half_lambertian, flat, k * 2.0f - 1.0f);
  }
}

// Vector rotation with a quaternion
// https://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/
float3 rotate_vector(float3 v, float4 q)
{
  float3 t = 2.0 * cross(q.xyz, v);
  return v + q.w * t + cross(q.xyz, t);
}

// Cartesian to cube hexagonal coordinates.
// Based on this: https://backdrifting.net/post/064_hex_grids
float3 cart_to_hex(float2 cart) {
  float p = cart.x;
  float q = dot(cart, float2(0.5f, SQRT_3_OVER_2));
  float r = dot(cart, float2(0.5f, -SQRT_3_OVER_2));

  return float3(p, q, r) * 0.5f;
}

float2 hex_to_cart(float3 cart) {
  return float2(
      cart[0] + (cart[1] + cart[2]) * 0.5f,
      (cart[1] - cart[2]) * SQRT_3_OVER_2);
}


float3 round_hex(float3 hex_coord) {
    float3 rounded = round(hex_coord);
    float3 diff = abs(rounded - hex_coord);
    float error = rounded.x - rounded.y - rounded.z;

    float3 max_mask = float3(
        diff.x > diff.y && diff.x > diff.z,
        diff.y > diff.x && diff.y > diff.z,
        diff.z > diff.x && diff.z > diff.y);

    rounded += error * float3(-1, 1, 1) * max_mask;
    return rounded;
}

// Reoriented normal mapping
// https://blog.selfshadow.com/publications/blending-in-detail/
// Inputs are in tangent space.
float3 blendNormalsHill12(float3 n0, float3 n1) {
  n0.z += 1.0;
  n1.xy = -n1.xy;
  
  return normalize(n0 * dot(n0, n1) - n1 * n0.z);
}

// https://en.wikipedia.org/wiki/Relative_luminance
float luminance(float3 rgb) {
  return dot(float3(0.2126, 0.7152, 0.0722), rgb);
}

float4 alpha_blend(float4 front, float4 back) {
  return float4(front.rgb * front.a + back.rgb * (1 - front.a), front.a + back.a * (1 - front.a));
}

// Cheap procedural 3D hash -> [0,1]^3. Based on the "hashwithoutsine" family.
float3 hash33_fast(float3 p) {
  p = frac(p * float3(0.1031, 0.1030, 0.0973));
  p += dot(p, p.yxz + 33.33);
  return frac((p.xxy + p.yxx) * p.zyx);
}

// Procedural value noise in [0,1]^3 — trilinear interpolation of hashed corners.
float3 value_noise3(float3 p) {
  float3 i = floor(p);
  float3 f = frac(p);
  float3 u = f * f * (3.0 - 2.0 * f);

  return lerp(
    lerp(lerp(hash33_fast(i + float3(0, 0, 0)), hash33_fast(i + float3(1, 0, 0)), u.x),
         lerp(hash33_fast(i + float3(0, 1, 0)), hash33_fast(i + float3(1, 1, 0)), u.x), u.y),
    lerp(lerp(hash33_fast(i + float3(0, 0, 1)), hash33_fast(i + float3(1, 0, 1)), u.x),
         lerp(hash33_fast(i + float3(0, 1, 1)), hash33_fast(i + float3(1, 1, 1)), u.x), u.y),
    u.z);
}

// Domain warping using procedural value noise.
float3 domain_warp_procedural(float3 uvw, float strength,
    uint octaves, float lacunarity, float gain) {
  float3 noise = 0;
  float g = 1;

  for (uint ii = 0; ii < octaves; ++ii) {
    noise += value_noise3(uvw + noise * strength) * g;
    uvw *= lacunarity;
    g *= gain;
  }

  noise *= (1 - gain) / (1 - pow(gain, octaves));

  return noise;
}

// Domain warping using a 3D noise texture. Texture should have an EV of
// 0.5.
float3 domain_warp_3d_tex(texture3D noise_tex, float3 uvw, float strength,
    uint octaves, float lacunarity, float gain) {
  float3 noise = 0;
  float g = 1;

  float3 uvw_dx = ddx(uvw);
  float3 uvw_dy = ddy(uvw);

  for (uint ii = 0; ii < octaves; ++ii) {
    noise += noise_tex.SampleGrad(aniso4_trilinear_repeat_s, uvw + noise * strength, uvw_dx, uvw_dy).rgb * g;
    uvw *= lacunarity;
    g *= gain;
    uvw_dx *= lacunarity;
    uvw_dy *= lacunarity;
  }

  // Normalize: geometric series 1 + r + ... + r^{n-1} = (1 - r^n) / (1 - r)
  noise *= (1 - gain) / (1 - pow(gain, octaves));

  return noise;
}

// Return distance to the nearest cell edge in a Voronoi pattern.
float voronoi_edge_distance(float3 x) {
  float3 p = floor(x);
  float3 f = frac(x);
  float d1 = 1e6;
  float d2 = 1e6;
  float3 p1 = 0;
  float3 p2 = 0;

  for (int k = -1; k <= 1; k++) {
    for (int j = -1; j <= 1; j++) {
      for (int i = -1; i <= 1; i++) {
        float3 b = float3(i, j, k);
        float3 r = b + hash33_fast(p + b) - f;
        float d = dot(r, r);
        if (d < d1) {
          d2 = d1;
          p2 = p1;
          d1 = d;
          p1 = r;
        } else if (d < d2) {
          d2 = d;
          p2 = r;
        }
      }
    }
  }
  return (d2 - d1) / (2.0 * max(1e-4, length(p2 - p1)));
}

#endif  // __MATH_INC