No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

svd.cs 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. using System.Runtime.CompilerServices;
  2. using Unity.IL2CPP.CompilerServices;
  3. namespace Unity.Mathematics
  4. {
  5. // SVD algorithm as described in:
  6. // Computing the singular value decomposition of 3x3 matrices with minimal branching and elementary floating point operations,
  7. // A.McAdams, A.Selle, R.Tamstorf, J.Teran and E.Sifakis, University of Wisconsin - Madison technical report TR1690, May 2011
  8. [Il2CppEagerStaticClassConstruction]
  9. static class svd
  10. {
  11. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  12. static void condSwap(bool c, ref float x, ref float y)
  13. {
  14. var tmp = x;
  15. x = math.select(x, y, c);
  16. y = math.select(y, tmp, c);
  17. }
  18. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  19. static void condNegSwap(bool c, ref float3 x, ref float3 y)
  20. {
  21. var tmp = -x;
  22. x = math.select(x, y, c);
  23. y = math.select(y, tmp, c);
  24. }
  25. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  26. static quaternion condNegSwapQuat(bool c, quaternion q, float4 mask)
  27. {
  28. const float halfSqrt2 = 0.707106781186548f;
  29. return math.mul(q, math.select(quaternion.identity.value, mask * halfSqrt2, c));
  30. }
  31. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  32. static void sortSingularValues(ref float3x3 b, ref quaternion v)
  33. {
  34. var l0 = math.lengthsq(b.c0);
  35. var l1 = math.lengthsq(b.c1);
  36. var l2 = math.lengthsq(b.c2);
  37. var c = l0 < l1;
  38. condNegSwap(c, ref b.c0, ref b.c1);
  39. v = condNegSwapQuat(c, v, math.float4(0f, 0f, 1f, 1f));
  40. condSwap(c, ref l0, ref l1);
  41. c = l0 < l2;
  42. condNegSwap(c, ref b.c0, ref b.c2);
  43. v = condNegSwapQuat(c, v, math.float4(0f, -1f, 0f, 1f));
  44. condSwap(c, ref l0, ref l2);
  45. c = l1 < l2;
  46. condNegSwap(c, ref b.c1, ref b.c2);
  47. v = condNegSwapQuat(c, v, math.float4(1f, 0f, 0f, 1f));
  48. }
  49. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  50. static quaternion approxGivensQuat(float3 pq, float4 mask)
  51. {
  52. const float c8 = 0.923879532511287f; // cos(pi/8)
  53. const float s8 = 0.38268343236509f; // sin(pi/8)
  54. const float g = 5.82842712474619f; // 3 + 2 * sqrt(2)
  55. var ch = 2f * (pq.x - pq.y); // approx cos(a/2)
  56. var sh = pq.z; // approx sin(a/2)
  57. var r = math.select(math.float4(s8, s8, s8, c8), math.float4(sh, sh, sh, ch), g * sh * sh < ch * ch) * mask;
  58. return math.normalize(r);
  59. }
  60. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  61. static quaternion qrGivensQuat(float2 pq, float4 mask)
  62. {
  63. var l = math.sqrt(pq.x * pq.x + pq.y * pq.y);
  64. var sh = math.select(0f, pq.y, l > k_EpsilonNormalSqrt);
  65. var ch = math.abs(pq.x) + math.max(l, k_EpsilonNormalSqrt);
  66. condSwap(pq.x < 0f, ref sh, ref ch);
  67. return math.normalize(math.float4(sh, sh, sh, ch) * mask);
  68. }
  69. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  70. static quaternion givensQRFactorization(float3x3 b, out float3x3 r)
  71. {
  72. var u = qrGivensQuat(math.float2(b.c0.x, b.c0.y), math.float4(0f, 0f, 1f, 1f));
  73. var qmt = math.float3x3(math.conjugate(u));
  74. r = math.mul(qmt, b);
  75. var q = qrGivensQuat(math.float2(r.c0.x, r.c0.z), math.float4(0f, -1f, 0f, 1f));
  76. u = math.mul(u, q);
  77. qmt = math.float3x3(math.conjugate(q));
  78. r = math.mul(qmt, r);
  79. q = qrGivensQuat(math.float2(r.c1.y, r.c1.z), math.float4(1f, 0f, 0f, 1f));
  80. u = math.mul(u, q);
  81. qmt = math.float3x3(math.conjugate(q));
  82. r = math.mul(qmt, r);
  83. return u;
  84. }
  85. static quaternion jacobiIteration(ref float3x3 s, int iterations = 5)
  86. {
  87. float3x3 qm;
  88. quaternion q;
  89. quaternion v = quaternion.identity;
  90. for (int i = 0; i < iterations; ++i)
  91. {
  92. q = approxGivensQuat(math.float3(s.c0.x, s.c1.y, s.c0.y), math.float4(0f, 0f, 1f, 1f));
  93. v = math.mul(v, q);
  94. qm = math.float3x3(q);
  95. s = math.mul(math.mul(math.transpose(qm), s), qm);
  96. q = approxGivensQuat(math.float3(s.c1.y, s.c2.z, s.c1.z), math.float4(1f, 0f, 0f, 1f));
  97. v = math.mul(v, q);
  98. qm = math.float3x3(q);
  99. s = math.mul(math.mul(math.transpose(qm), s), qm);
  100. q = approxGivensQuat(math.float3(s.c2.z, s.c0.x, s.c2.x), math.float4(0f, 1f, 0f, 1f));
  101. v = math.mul(v, q);
  102. qm = math.float3x3(q);
  103. s = math.mul(math.mul(math.transpose(qm), s), qm);
  104. }
  105. return v;
  106. }
  107. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  108. static float3 singularValuesDecomposition(float3x3 a, out quaternion u, out quaternion v)
  109. {
  110. u = quaternion.identity;
  111. v = quaternion.identity;
  112. var s = math.mul(math.transpose(a), a);
  113. v = jacobiIteration(ref s);
  114. var b = math.float3x3(v);
  115. b = math.mul(a, b);
  116. sortSingularValues(ref b, ref v);
  117. u = givensQRFactorization(b, out var e);
  118. return math.float3(e.c0.x, e.c1.y, e.c2.z);
  119. }
  120. public const float k_EpsilonDeterminant = 1e-6f;
  121. public const float k_EpsilonRCP = 1e-9f;
  122. public const float k_EpsilonNormalSqrt = 1e-15f;
  123. public const float k_EpsilonNormal = 1e-30f;
  124. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  125. static float3 rcpsafe(float3 x, float epsilon = k_EpsilonRCP) =>
  126. math.select(math.rcp(x), float3.zero, math.abs(x) < epsilon);
  127. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  128. internal static float3x3 svdInverse(float3x3 a)
  129. {
  130. var e = singularValuesDecomposition(a, out var u, out var v);
  131. var um = math.float3x3(u);
  132. var vm = math.float3x3(v);
  133. return math.mul(vm, math.scaleMul(rcpsafe(e, k_EpsilonDeterminant), math.transpose(um)));
  134. }
  135. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  136. internal static quaternion svdRotation(float3x3 a)
  137. {
  138. singularValuesDecomposition(a, out var u, out var v);
  139. return math.mul(u, math.conjugate(v));
  140. }
  141. }
  142. }