yum/3ner
A toon shader for Unity's BIRP.
git clone https://git.yummers.dev/yum/3ner
e7e681b
master
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