yum/3ner

A toon shader for Unity's BIRP.

git clone https://git.yummers.dev/yum/3ner

yumFold: more animation bugfixese7e681b

master
22.6 KiB701 linesraw
1#ifndef __CUSTOM31_INC
2#define __CUSTOM31_INC
3
4#define PI      3.14159265f
5#define PI_RCP  0.31830988f
6#define SQRT_2      1.41421356f
7#define RCP_SQRT_2  0.70710678f
8#define TAU     (2.0f * PI)
9#define HALF_PI (0.5f * PI)
10#define RCP_PI  (1.0f / PI)
11#define RCP_TAU (1.0f / TAU)
12
13#define glsl_mod(x,y) (((x)-(y)*floor((x)/(y))))
14
15// Differentiable versions of common operators.
16#define dabs(x) sqrt((x) * (x) + 1e-6f)
17#define dmin(a, b) (0.5f * ((a) + (b) - dabs((a) - (b))))
18#define dmax(a, b) (0.5f * ((a) + (b) + dabs((a) - (b))))
19#define dlerp(x, y, t) ((x) * (1.0f-t) + (y) * t)
20// This was derived using fourier analysis. See Scripts/approximate.py.
21#define dfrac(x) \
22  4.997559e-01 - \
23  3.183096e-01 * sin(6.283185e+00*(x)) - \
24  1.591544e-01 * sin(1.256637e+01*(x)) - \
25  1.061025e-01 * sin(1.884956e+01*(x)) - \
26  7.957647e-02 * sin(2.513274e+01*(x))
27
28// Quintic interpolation for C2 continuity (smoother derivatives)
29#define quintic(t) ((t) * (t) * (t) * ((t) * ((t) * 6.0f - 15.0f) + 10.0f))
30#define d_quintic(t) (30.0f * (t) * (t) * ((t) * ((t) - 2.0f) + 1.0f))
31
32// Macros for transforming normal and tangent using autodiff.
33// r3r3 refers to "r3 to r3 transform", aka a mapping between 3d real-valued
34// spaces.
35#define R3R3_DECLARE_BASIS_VECTORS(xyz) \
36  DifferentialPair<float3> dp_x = diffPair(xyz, float3(1, 0, 0)); \
37  DifferentialPair<float3> dp_y = diffPair(xyz, float3(0, 1, 0)); \
38  DifferentialPair<float3> dp_z = diffPair(xyz, float3(0, 0, 1))
39
40#define R3R3_AUTODIFF_BASIS_VECTORS(fun, ...) \
41  DifferentialPair<float3> dp_x_out = fwd_diff(fun)(dp_x, __VA_ARGS__); \
42  DifferentialPair<float3> dp_y_out = fwd_diff(fun)(dp_y, __VA_ARGS__); \
43  DifferentialPair<float3> dp_z_out = fwd_diff(fun)(dp_z, __VA_ARGS__)
44
45#define R3R3_DEFORM_NORMAL_AND_TANGENT(normal, tangent)   \
46  float3x3 jacobian = float3x3(                           \
47    float3(dp_x_out.d.x, dp_y_out.d.x, dp_z_out.d.x),     \
48    float3(dp_x_out.d.y, dp_y_out.d.y, dp_z_out.d.y),     \
49    float3(dp_x_out.d.z, dp_y_out.d.z, dp_z_out.d.z)      \
50  );                                                      \
51  float jac_det = determinant(jacobian);                  \
52  float3x3 itjac = inverse(transpose(jacobian), jac_det); \
53  normal = mul(itjac, normal) * jac_det;            \
54  tangent = mul(jacobian, tangent) * jac_det
55
56// Syntactic sugar - wraps the previous three macros.
57#define R3R3_NORMALS(xyz, normal, tangent, fun, ...)  \
58  R3R3_DECLARE_BASIS_VECTORS(xyz);                    \
59  R3R3_AUTODIFF_BASIS_VECTORS(fun, __VA_ARGS__);      \
60  R3R3_DEFORM_NORMAL_AND_TANGENT(normal, tangent);    \
61  xyz = fun(xyz, __VA_ARGS__)
62
63[Differentiable]
64float3x3 inverse(no_diff float3x3 m, no_diff float det) {
65  det = (det < 0.0f ? -1.0f : 1.0f) * max(1e-6f, abs(det));
66
67  float invDet = 1.0f / det;
68  float3x3 inv;
69
70  inv._11 = (m._22 * m._33 - m._23 * m._32) * invDet;
71  inv._12 = (m._13 * m._32 - m._12 * m._33) * invDet;
72  inv._13 = (m._12 * m._23 - m._13 * m._22) * invDet;
73  inv._21 = (m._23 * m._31 - m._21 * m._33) * invDet;
74  inv._22 = (m._11 * m._33 - m._13 * m._31) * invDet;
75  inv._23 = (m._13 * m._21 - m._11 * m._23) * invDet;
76  inv._31 = (m._21 * m._32 - m._22 * m._31) * invDet;
77  inv._32 = (m._31 * m._12 - m._11 * m._32) * invDet;
78  inv._33 = (m._11 * m._22 - m._12 * m._21) * invDet;
79
80  return inv;
81}
82
83float2 project_x_onto_y(float2 x, float2 y) {
84  return (dot(x, y) / dot(y, y)) * y;
85}
86
87// Maps a 2x2 quad to a tube.
88// `s_cart` is the axis along which the tube is rolled.
89// `r_cart` is an axis along which points are not transformed. It is assumed to
90// be orthogonal `s_cart`.
91[Differentiable]
92public float3 plane_to_tube(float3 xyz,
93    no_diff float3 p, no_diff float3 r_cart, no_diff float3 s_cart,
94    no_diff float t) {
95  // Convert from cartesian to (r, s, r x s) space.
96  // Ensure orthonormal basis vectors.
97  // TODO remove normalize, do at higher level of stack.
98  r_cart = normalize(r_cart);
99  s_cart = normalize(s_cart);
100  float3 rxs_cart = cross(s_cart, r_cart);
101  float3x3 to_rsrxs = float3x3(r_cart, s_cart, rxs_cart);
102  // Inverse of orthonormal matrix is just the transpose.
103  float3x3 to_cart  = transpose(to_rsrxs);
104
105  // Translate origin to `p` then change into (r, s, r x s) basis.
106  xyz = mul(to_rsrxs, xyz - p);
107
108  // Components in pivot basis: n (neutral axis), b (tangential), h (axial s).
109  float epsilon = 1e-4f;
110  float r = xyz.x;     // Lr
111  float s = xyz.y;     // Lv0 x Lr
112  float rxs = xyz.z;   // Lv0
113
114  // Blend between two vectors:
115  //  v0: vector of length ||Lr + Lv0|| at angle atan2(Lv0, Lr)
116  //  v1: vector of length ||Lr|| at angle ||Lv0|| / ||Lr||
117  // Interpolate in polar coordinates to make it wrap nicely.
118  float r0 = length(float2(r, rxs));
119  float r1 = max(abs(r), epsilon);
120
121  float theta0 = atan2(rxs, r);
122  float theta1 = rxs / r1;
123
124  // Interpolate polar coordinates.
125  float radius = dlerp(r0, r1, t);
126  float theta = dlerp(theta0, theta1, t);
127
128  // Map into (r, rxs) basis.
129  float2 nb_t = float2(cos(theta), sin(theta)) * radius;
130
131  // Un-project from (r, rxs) plane to full (r, s, rxs) basis.
132  xyz = float3(nb_t.x, s, nb_t.y);
133
134  // Map back to cartesian basis.
135  xyz = mul(to_cart, xyz) + p;
136
137  return xyz;
138}
139
140public void plane_to_tube_normal(inout float3 xyz, inout float3 normal,
141    inout float3 tangent, float3 p, float3 r, float3 s, float t) {
142  R3R3_NORMALS(xyz, normal, tangent, plane_to_tube, p, r, s, t);
143}
144
145[Differentiable]
146public float3 axis_align(float3 xyz, no_diff float3 po, no_diff float3 pp, no_diff float3 r, no_diff float t) {
147  const float3 xyz0 = xyz;
148
149  // We assume that `s` is orthogonal to `r`, and that `r` is normalized.
150  float3 s = normalize(pp - po);
151  float3 sxr = cross(s, r);
152
153  float3x3 to_rsrxs = float3x3(r, s, sxr);
154  // Inverse of orthonormal matrix is just the transpos.
155  float3x3 to_cart  = transpose(to_rsrxs);
156
157  // Move key vectors into (r, s, sxr) basis centered at `po`, per the derivation.
158  float3 xyz_rsrxs = mul(to_rsrxs, xyz - po);
159
160  float vr = xyz_rsrxs[0];
161  float vs = xyz_rsrxs[1];
162  float2 v0 = float2(vr, vs);
163  float3 pp_rsrxs = mul(to_rsrxs, pp - po);
164  float qr = pp_rsrxs[1] * vr / vs;  // TODO epsilon
165  float2 q = float2(qr, pp_rsrxs[1]);
166
167  // Translate to `q`.
168  v0 -= q;
169
170  // Project onto `s`.
171  v0.x = 0;
172
173  // Translate back to `po`.
174  v0 += q;
175
176  xyz_rsrxs[0] = v0[0];
177  xyz_rsrxs[1] = v0[1];
178
179  // Move back into cartesian space.
180  xyz = mul(to_cart, xyz_rsrxs) + po;
181
182  return dlerp(xyz0, xyz, t);
183}
184
185public void axis_align_normal(inout float3 xyz, inout float3 normal,
186    inout float3 tangent, float3 po, float3 pp, float3 r, float t) {
187  R3R3_NORMALS(xyz, normal, tangent, axis_align, po, pp, r, t);
188}
189
190// Maps a tube with circular cross section on the xz plane to a quad on the xy
191// plane.
192[Differentiable]
193public float3 tube_to_plane(float3 xyz,
194    no_diff float3 p, no_diff float3 r_cart, no_diff float3 s_cart,
195    no_diff float t) {
196  // Convert from cartesian to (r, s, r x s) space.
197  // Ensure orthonormal basis vectors.
198  // TODO remove normalize, do at higher level of stack.
199  r_cart = normalize(r_cart);
200  s_cart = normalize(s_cart);
201  float3 rxs_cart = cross(s_cart, r_cart);
202  float3x3 to_rsrxs = float3x3(r_cart, s_cart, rxs_cart);
203  float3x3 to_cart  = inverse(to_rsrxs, determinant(to_rsrxs));
204
205  // Translate origin to `p` then change into (r, s, r x s) basis.
206  xyz = mul(to_rsrxs, xyz - p);
207
208  // Components in pivot basis: n (neutral axis), b (tangential), h (axial s).
209  float epsilon = 1e-4f;
210  float r = xyz.x;     // Lr
211  float s = xyz.y;     // Lv0 x Lr
212  float rxs = xyz.z;   // Lv0
213  float2 v0 = float2(r, rxs);
214
215  float theta0 = atan2(rxs, r);
216  float Lr  = length(v0);
217  float Lv0 = theta0 * Lr;
218  float phi = atan2(Lv0, Lr);
219  float rr = length(float2(Lr, Lv0));
220
221  // Blend between two vectors:
222  //  v0: vector of length ||Lr + Lv0|| at angle atan2(Lv0, Lr)
223  //  v1: vector of length ||Lr|| at angle ||Lv0|| / ||Lr||
224  // Interpolate in polar coordinates to make it wrap nicely.
225  float r0 = Lr;
226  float r1 = rr;
227
228  float theta1 = phi;
229
230  // Interpolate polar coordinates.
231  float radius = dlerp(r0, r1, t);
232  float theta = dlerp(theta0, theta1, t);
233
234  // Map into (r, rxs) basis.
235  float2 nb_t = float2(cos(theta), sin(theta)) * radius;
236
237  // Un-project from (r, rxs) plane to full (r, s, rxs) basis.
238  xyz = float3(nb_t.x, s, nb_t.y);
239
240  // Map back to cartesian basis.
241  xyz = mul(to_cart, xyz) + p;
242
243  return xyz;
244}
245
246public void tube_to_plane_normal(inout float3 xyz, inout float3 normal,
247    inout float3 tangent, float3 p, float3 r, float3 s, float t) {
248  R3R3_NORMALS(xyz, normal, tangent, tube_to_plane, p, r, s, t);
249}
250
251[Differentiable]
252public float3 point_align(float3 xyz, no_diff float3 po, no_diff float3 pp, no_diff float3 r, no_diff float t) {
253  const float3 xyz0 = xyz;
254
255  // We assume that `s` is orthogonal to `r`, and that `r` is normalized.
256  float3 s = normalize(pp - po);
257  float3 sxr = cross(s, r);
258
259  float3x3 to_rsrxs = float3x3(r, s, sxr);
260  // Inverse of orthonormal matrix is just the transpose.
261  float3x3 to_cart  = transpose(to_rsrxs);
262
263  // Move key vectors into (r, s, sxr) basis centered at `po`, per the derivation.
264  float3 xyz_rsrxs = mul(to_rsrxs, xyz - po);
265  float3 pp_rsrxs  = mul(to_rsrxs, pp - po);
266
267  float2 v1 = xyz_rsrxs.xy;
268  float v1r = v1.x;
269  float v1s = v1.y;
270
271  float vs = v1.y;
272  float qs = pp_rsrxs.y;
273  float qr = v1.x;
274  float2 q = float2(qr, qs);
275  q *= vs / qs;
276
277  xyz_rsrxs.xy = q;
278
279  // Move back into cartesian space.
280  xyz = mul(to_cart, xyz_rsrxs) + po;
281
282  return dlerp(xyz0, xyz, t);
283}
284
285public void point_align_normal(inout float3 xyz, inout float3 normal, inout float3 tangent, float3 po, float3 pp, float3 r, float t) {
286  R3R3_NORMALS(xyz, normal, tangent, point_align, po, pp, r, t);
287}
288
289[Differentiable]
290public float3 seal(float3 xyz, no_diff float A, no_diff float k, no_diff float t) {
291  float x = xyz.x;
292  float y = xyz.y;
293  float z = xyz.z;
294
295  float x0 = x + sin(y * k) * 0.1;
296  float y0 = y + sin(x * k * 2) * 0.5 + cos(z);
297  float z0 = (z + sin(x * y * k * PI + t)) * A * (1.0 + sin(y * k) * sin(x * k));
298
299  x0 += z0 * 0.1 * sin(z0 * PI + 1.5);
300  y0 += z0 * 0.1 * sin(z0 * PI + 1.5);
301
302  x0 -= 0.0;
303  y0 -= 1.2;
304
305  return float3(
306      x0, y0, z0
307  );
308}
309
310public void seal_normal(inout float3 xyz, inout float3 normal,
311    inout float3 tangent, float A, float k, float t) {
312  R3R3_NORMALS(xyz, normal, tangent, seal, A, k, t);
313}
314
315[Differentiable]
316public float3 sine_wave(float3 xyz,
317    no_diff float3 amplitude,
318    no_diff float3 direction,
319    no_diff float3 k,
320    no_diff float3 omega,
321    no_diff float t) {
322  xyz += amplitude * sin(k * dot(xyz, direction) + omega * t);
323  return xyz;
324}
325
326public void sine_wave_normal(inout float3 xyz, inout float3 normal, inout float3 tangent,
327    float3 amplitude, float3 direction, float3 k, float3 omega, float t) {
328  R3R3_NORMALS(xyz, normal, tangent, sine_wave, amplitude, direction, k, omega, t);
329}
330
331[Differentiable]
332public float3 norm_conversion(float3 xyz, no_diff float input_k, no_diff float output_k, no_diff float t) {
333  float3 xyz_abs = dabs(xyz)+1e-4f;
334  float lin = pow(pow(xyz_abs.x, input_k) + pow(xyz_abs.y, input_k) + pow(xyz_abs.z, input_k), 1.0f / input_k);
335  float lout = pow(pow(xyz_abs.x, output_k) + pow(xyz_abs.y, output_k) + pow(xyz_abs.z, output_k), 1.0f / output_k);
336  float scale = dlerp(1.0f, lout / lin, t);
337  return xyz * scale;
338}
339
340public void norm_conversion_normal(inout float3 xyz, inout float3 normal, inout float3 tangent, float input_k, float output_k, float t) {
341  R3R3_NORMALS(xyz, normal, tangent, norm_conversion, input_k, output_k, t);
342}
343
344[Differentiable]
345float3 rand3_hash3(float3 p)
346{
347    // Improved Murmurhash3 by Squirrel Eiserloh (GDC 2017)
348    p = float3(dot(p, float3(127.1, 311.7, 74.7)),
349               dot(p, float3(269.5, 183.3, 246.1)),
350               dot(p, float3(113.5, 271.9, 124.6)));
351    return frac(sin(p) * 43758.5453123);
352}
353
354[Differentiable]
355float rand3_hash1(float3 p)
356{
357    // Improved Murmurhash3 by Squirrel Eiserloh (GDC 2017)
358    float p0 = dot(p, float3(127.1, 311.7, 74.7));
359    return frac(sin(p0) * 43758.5453123);
360}
361
362// Calculate value noise and jacobian in one shot.
363// Based on https://iquilezles.org/articles/morenoise/
364float3 value_noise(float3 xyz, out float3 dx, out float3 dy, out float3 dz) {
365  float3 cell = floor(xyz);
366  float3 f = xyz - cell;
367
368  float ux = quintic(f.x);
369  float uy = quintic(f.y);
370  float uz = quintic(f.z);
371
372  float dux = d_quintic(f.x);
373  float duy = d_quintic(f.y);
374  float duz = d_quintic(f.z);
375
376  float3 n000 = rand3_hash3(cell + float3(0.0f, 0.0f, 0.0f));
377  float3 n001 = rand3_hash3(cell + float3(0.0f, 0.0f, 1.0f));
378  float3 n010 = rand3_hash3(cell + float3(0.0f, 1.0f, 0.0f));
379  float3 n011 = rand3_hash3(cell + float3(0.0f, 1.0f, 1.0f));
380  float3 n100 = rand3_hash3(cell + float3(1.0f, 0.0f, 0.0f));
381  float3 n101 = rand3_hash3(cell + float3(1.0f, 0.0f, 1.0f));
382  float3 n110 = rand3_hash3(cell + float3(1.0f, 1.0f, 0.0f));
383  float3 n111 = rand3_hash3(cell + float3(1.0f, 1.0f, 1.0f));
384
385  float3 n00 = lerp(n000, n001, uz);
386  float3 n01 = lerp(n010, n011, uz);
387  float3 n10 = lerp(n100, n101, uz);
388  float3 n11 = lerp(n110, n111, uz);
389
390  float3 n0 = lerp(n00, n01, uy);
391  float3 n1 = lerp(n10, n11, uy);
392
393  float oneMinusUx = 1.0f - ux;
394  float oneMinusUy = 1.0f - uy;
395
396  float3 noise_half = lerp(n0, n1, ux);
397
398  float3 dnoise_half_dx = (n1 - n0) * dux;
399
400  float3 dn0_dy = (n01 - n00) * duy;
401  float3 dn1_dy = (n11 - n10) * duy;
402  float3 dnoise_half_dy = dn0_dy * oneMinusUx + dn1_dy * ux;
403
404  float3 dn00_dz = (n001 - n000) * duz;
405  float3 dn01_dz = (n011 - n010) * duz;
406  float3 dn10_dz = (n101 - n100) * duz;
407  float3 dn11_dz = (n111 - n110) * duz;
408  float3 dn0_dz = dn00_dz * oneMinusUy + dn01_dz * uy;
409  float3 dn1_dz = dn10_dz * oneMinusUy + dn11_dz * uy;
410  float3 dnoise_half_dz = dn0_dz * oneMinusUx + dn1_dz * ux;
411
412  dx = 2.0f * dnoise_half_dx;
413  dy = 2.0f * dnoise_half_dy;
414  dz = 2.0f * dnoise_half_dz;
415
416  return -1.0f + 2.0f * noise_half;
417}
418
419float3 fbm_with_derivatives(float3 xyz,
420    no_diff float t,
421    no_diff float3 amplitude,
422    no_diff float gain,
423    no_diff float lacunarity,
424    no_diff float3 period,
425    no_diff float octaves,
426    no_diff float3 velocity,
427    out float3 dx,
428    out float3 dy,
429    out float3 dz) {
430  float3 noise = float3(0.0f, 0.0f, 0.0f);
431  float3 gain_i = amplitude;
432  float3 freq_i = 1.0f / period;
433
434  dx = float3(0.0f, 0.0f, 0.0f);
435  dy = float3(0.0f, 0.0f, 0.0f);
436  dz = float3(0.0f, 0.0f, 0.0f);
437
438  for (uint i = 0; i < octaves; ++i) {
439    float3 dx_i, dy_i, dz_i;
440    float3 octave_noise = value_noise((xyz - velocity * t) * freq_i, dx_i, dy_i, dz_i);
441    noise += gain_i * octave_noise;
442    dx += gain_i * freq_i.x * dx_i;
443    dy += gain_i * freq_i.y * dy_i;
444    dz += gain_i * freq_i.z * dz_i;
445    freq_i *= lacunarity;
446    gain_i *= gain;
447  }
448
449  dx += float3(1.0f, 0.0f, 0.0f);
450  dy += float3(0.0f, 1.0f, 0.0f);
451  dz += float3(0.0f, 0.0f, 1.0f);
452
453  return xyz + noise;
454}
455
456public float3 fbm(float3 xyz,
457    no_diff float t,
458    no_diff float3 amplitude,
459    no_diff float gain,
460    no_diff float lacunarity,
461    no_diff float3 period,
462    no_diff float octaves,
463    no_diff float3 velocity) {
464  float3 dx_unused, dy_unused, dz_unused;
465  return fbm_with_derivatives(xyz, t, amplitude, gain, lacunarity, period, octaves,
466      velocity, dx_unused, dy_unused, dz_unused);
467}
468
469public void fbm_normal(inout float3 xyz, inout float3 normal, inout float3 tangent,
470    no_diff float t,
471    no_diff float3 amplitude, no_diff float gain, no_diff float lacunarity,
472    no_diff float3 period, no_diff float octaves, no_diff float3 velocity) {
473  float3 dx, dy, dz;
474  xyz = fbm_with_derivatives(xyz, t, amplitude, gain, lacunarity, period, octaves,
475      velocity, dx, dy, dz);
476  float3x3 jac = float3x3(
477      dx.x, dy.x, dz.x,
478      dx.y, dy.y, dz.y,
479      dx.z, dy.z, dz.z);
480  float jac_det = determinant(jac);
481  float3x3 itjac = inverse(transpose(jac), jac_det);
482  normal = mul(itjac, normal) * jac_det;
483  tangent = mul(jac, tangent);
484}
485
486// Maps a plane on [-1, 1] on xz plane to a hemi-octahedron with radius 1.
487[Differentiable]
488public float3 plane_to_hemi_octahedron(float3 xyz,
489    no_diff float3 p, no_diff float3 r_cart, no_diff float3 s_cart,
490    no_diff float t) {
491  // Convert from cartesian to (r, s, r x s) space.
492  r_cart = normalize(r_cart);
493  s_cart = normalize(s_cart);
494  float3 rxs_cart = cross(s_cart, r_cart);
495  float3x3 to_rsrxs = float3x3(r_cart, s_cart, rxs_cart);
496  float3x3 to_cart = transpose(to_rsrxs);
497
498  // Translate origin to `p` then change into (r, s, r x s) basis.
499  xyz = mul(to_rsrxs, xyz - p);
500
501  float3 xyz0 = xyz;
502
503  // Extract planar coordinates: x and z form the 2D plane
504  float x = xyz.x;
505  float z = xyz.z;
506
507  // Rotate 45° and scale to fit square into diamond
508  float x_rot = (x + z) * 0.5;
509  float z_rot = (z - x) * 0.5;
510
511  // Octahedral decode: y = 1 - |x| - |z|, clamped to hemisphere
512  // Use differentiable abs and max for smooth autodiff
513  float y = dmax(0.0f, 1.0f - dabs(x_rot) - dabs(z_rot));
514
515  float3 oct_pos = float3(x_rot, y, z_rot);
516  float len = dot(oct_pos, oct_pos);
517  oct_pos = oct_pos * (1.0f + xyz.y);
518
519  // Rotate back by -45° around y to undo input rotation
520  float x_unrot = (oct_pos.x - oct_pos.z) * RCP_SQRT_2;
521  float z_unrot = (oct_pos.x + oct_pos.z) * RCP_SQRT_2;
522  oct_pos = float3(x_unrot, oct_pos.y, z_unrot);
523
524  // Interpolate between original position and sphere position
525  float3 result = dlerp(xyz0, oct_pos, t);
526
527  // Map back to cartesian basis
528  xyz = mul(to_cart, result) + p;
529
530  return xyz;
531}
532
533public void plane_to_hemi_octahedron_normal(inout float3 xyz, inout float3 normal,
534    inout float3 tangent, float3 p, float3 r, float3 s, float t) {
535  R3R3_NORMALS(xyz, normal, tangent, plane_to_hemi_octahedron, p, r, s, t);
536}
537
538// Maps a hemi-octahedron with raidus 1 to a quad on [-1, 1] on the (r, rxs) plane.
539[Differentiable]
540public float3 hemi_octahedron_to_plane(float3 xyz,
541    no_diff float3 p, no_diff float3 r_cart, no_diff float3 s_cart,
542    no_diff float t) {
543  // Convert from cartesian to (r, s, r x s) space.
544  r_cart = normalize(r_cart);
545  s_cart = normalize(s_cart);
546  float3 rxs_cart = cross(s_cart, r_cart);
547  float3x3 to_rsrxs = float3x3(r_cart, s_cart, rxs_cart);
548  float3x3 to_cart = transpose(to_rsrxs);
549
550  // Translate origin to `p` then change into (r, s, r x s) basis.
551  xyz = mul(to_rsrxs, xyz - p);
552
553  float3 xyz0 = xyz;
554
555  // Undo the -45° unrotation from forward pass (rotate by +45°)
556  float x_rot = (xyz.x + xyz.z) * RCP_SQRT_2;
557  float z_rot = (xyz.z - xyz.x) * RCP_SQRT_2;
558
559  // The forward pass scales by (1 + s_original), and the unscaled hemi-octahedron
560  // has L1 norm = 1, so L1 of the scaled point recovers s_original.
561  float L1_scaled = dabs(x_rot) + dabs(xyz.y) + dabs(z_rot);
562  float s_original = L1_scaled - 1.0f;
563
564  // Remove (1 + s) scale to get the unit hemi-octahedron point, then decode.
565  float L1_inv = 1.0f / L1_scaled;
566  float x_oct = x_rot * L1_inv;
567  float z_oct = z_rot * L1_inv;
568
569  // Undo the initial 45° rotation (x_rot = (x+z)*0.5, z_rot = (z-x)*0.5)
570  // Inverse: x = x_rot - z_rot, z = x_rot + z_rot
571  float x_plane = x_oct - z_oct;
572  float z_plane = x_oct + z_oct;
573
574  float3 plane_pos = float3(x_plane, s_original, z_plane);
575
576  // Interpolate between original position and plane position
577  float3 result = dlerp(xyz0, plane_pos, t);
578
579  // Map back to cartesian basis
580  xyz = mul(to_cart, result) + p;
581
582  return xyz;
583}
584
585public void hemi_octahedron_to_plane_normal(inout float3 xyz, inout float3 normal,
586    inout float3 tangent, float3 p, float3 r, float3 s, float t) {
587  R3R3_NORMALS(xyz, normal, tangent, hemi_octahedron_to_plane, p, r, s, t);
588}
589
590// Maps [-1, 1] on (r, rxs) plane to a unit sphere using octahedral mapping.
591[Differentiable]
592public float3 plane_to_octahedron(float3 xyz,
593    no_diff float3 p, no_diff float3 r_cart, no_diff float3 s_cart,
594    no_diff float t) {
595  r_cart = normalize(r_cart);
596  s_cart = normalize(s_cart);
597  float3 rxs_cart = cross(s_cart, r_cart);
598  float3x3 to_rsrxs = float3x3(r_cart, s_cart, rxs_cart);
599  float3x3 to_cart = transpose(to_rsrxs);
600
601  xyz = mul(to_rsrxs, xyz - p);
602  float3 xyz0 = xyz;
603
604  float l1_norm = dabs(xyz.x) + dabs(xyz.z);
605  if (l1_norm > 1) {
606    xyz.x = sign(xyz0.x) * (1 - dabs(xyz0.z));
607    xyz.z = sign(xyz0.z) * (1 - dabs(xyz0.x));
608  }
609  xyz.y = 1 - l1_norm;
610
611  xyz *= (1 + xyz0.y);
612
613  float3 result = dlerp(xyz0, xyz, t);
614
615  xyz = mul(to_cart, result) + p;
616  return xyz;
617}
618
619public void plane_to_octahedron_normal(inout float3 xyz, inout float3 normal,
620    inout float3 tangent, float3 p, float3 r, float3 s, float t) {
621  R3R3_NORMALS(xyz, normal, tangent, plane_to_octahedron, p, r, s, t);
622}
623
624// Maps a unit sphere to a plane using octahedral mapping.
625[Differentiable]
626public float3 octahedron_to_plane(float3 xyz,
627    no_diff float3 p, no_diff float3 r_cart, no_diff float3 s_cart,
628    no_diff float t) {
629  r_cart = normalize(r_cart);
630  s_cart = normalize(s_cart);
631  float3 rxs_cart = cross(s_cart, r_cart);
632  float3x3 to_rsrxs = float3x3(r_cart, s_cart, rxs_cart);
633  float3x3 to_cart = transpose(to_rsrxs);
634
635  xyz = mul(to_rsrxs, xyz - p);
636  float3 xyz0 = xyz;
637
638  // The forward pass scales by (1 + s_original), and the unscaled octahedron
639  // has L1 norm = 1, so L1 of the scaled point recovers s_original.
640  float l1_norm = dabs(xyz.x) + dabs(xyz.y) + dabs(xyz.z);
641  float s_original = l1_norm - 1.0f;
642  xyz /= l1_norm;
643  float2 xz_tmp = xyz.xz;
644  if (xyz.y < 0) {
645    xyz.x = sign(xz_tmp[0]) * (1 - dabs(xz_tmp[1]));
646    xyz.z = sign(xz_tmp[1]) * (1 - dabs(xz_tmp[0]));
647  }
648  xyz.y = s_original;
649
650  float3 result = dlerp(xyz0, xyz, t);
651  xyz = mul(to_cart, result) + p;
652  return xyz;
653}
654
655public void octahedron_to_plane_normal(inout float3 xyz, inout float3 normal,
656    inout float3 tangent, float3 p, float3 r, float3 s, float t) {
657  R3R3_NORMALS(xyz, normal, tangent, octahedron_to_plane, p, r, s, t);
658}
659
660[Differentiable]
661public float3 scale(float3 xyz,
662    no_diff float3 k, no_diff float t) {
663  return dlerp(xyz, xyz * k, t);
664}
665
666public void scale_normal(inout float3 xyz, inout float3 normal,
667    inout float3 tangent, float3 k, float t) {
668  R3R3_NORMALS(xyz, normal, tangent, scale, k, t);
669}
670
671[Differentiable]
672public float3 translate(float3 xyz,
673    no_diff float3 offset, no_diff float t) {
674  return xyz + offset * t;
675}
676
677public void translate_normal(inout float3 xyz, inout float3 normal,
678    inout float3 tangent, float3 offset, float t) {
679  R3R3_NORMALS(xyz, normal, tangent, translate, offset, t);
680}
681
682[Differentiable]
683public float3 rotate(float3 xyz,
684    no_diff float3 p, no_diff float3 axis, no_diff float angle, no_diff float t) {
685  float theta = angle * t;
686  float3 a = normalize(axis);
687  float c = cos(theta);
688  float s = sin(theta);
689  float3 v = xyz - p;
690  // Rodrigues' rotation formula
691  float3 rotated = v * c + cross(a, v) * s + a * dot(a, v) * (1.0f - c);
692  return rotated + p;
693}
694
695public void rotate_normal(inout float3 xyz, inout float3 normal,
696    inout float3 tangent, float3 p, float3 axis, float angle, float t) {
697  R3R3_NORMALS(xyz, normal, tangent, rotate, p, axis, angle, t);
698}
699
700#endif  // __CUSTOM31_INC
701