GeometricTools.hlsl 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. #ifndef UNITY_GEOMETRICTOOLS_INCLUDED
  2. #define UNITY_GEOMETRICTOOLS_INCLUDED
  3. //-----------------------------------------------------------------------------
  4. // Transform functions
  5. //-----------------------------------------------------------------------------
  6. // Rotate around a pivot point and an axis
  7. float3 Rotate(float3 pivot, float3 position, float3 rotationAxis, float angle)
  8. {
  9. rotationAxis = normalize(rotationAxis);
  10. float3 cpa = pivot + rotationAxis * dot(rotationAxis, position - pivot);
  11. return cpa + ((position - cpa) * cos(angle) + cross(rotationAxis, (position - cpa)) * sin(angle));
  12. }
  13. float3x3 RotationFromAxisAngle(float3 A, float sinAngle, float cosAngle)
  14. {
  15. float c = cosAngle;
  16. float s = sinAngle;
  17. return float3x3(A.x * A.x * (1 - c) + c, A.x * A.y * (1 - c) - A.z * s, A.x * A.z * (1 - c) + A.y * s,
  18. A.x * A.y * (1 - c) + A.z * s, A.y * A.y * (1 - c) + c, A.y * A.z * (1 - c) - A.x * s,
  19. A.x * A.z * (1 - c) - A.y * s, A.y * A.z * (1 - c) + A.x * s, A.z * A.z * (1 - c) + c);
  20. }
  21. //-----------------------------------------------------------------------------
  22. // Solver
  23. //-----------------------------------------------------------------------------
  24. // Solves the quadratic equation of the form: a*t^2 + b*t + c = 0.
  25. // Returns 'false' if there are no real roots, 'true' otherwise.
  26. // Numerically stable.
  27. // Ref: Numerical Recipes in C++ (3rd Edition)
  28. bool SolveQuadraticEquation(float a, float b, float c, out float2 roots)
  29. {
  30. float d = b * b - 4 * a * c;
  31. float q = -0.5 * (b + CopySign(sqrt(d), b));
  32. roots = float2(q / a, c / q);
  33. return (d >= 0);
  34. }
  35. //-----------------------------------------------------------------------------
  36. // Intersection functions
  37. //-----------------------------------------------------------------------------
  38. bool IntersectRayAABB(float3 rayOrigin, float3 rayDirection,
  39. float3 boxMin, float3 boxMax,
  40. float tMin, float tMax,
  41. out float tEntr, out float tExit)
  42. {
  43. // Could be precomputed. Clamp to avoid INF. clamp() is a single ALU on GCN.
  44. // rcp(FLT_EPS) = 16,777,216, which is large enough for our purposes,
  45. // yet doesn't cause a lot of numerical issues associated with FLT_MAX.
  46. float3 rayDirInv = clamp(rcp(rayDirection), -rcp(FLT_EPS), rcp(FLT_EPS));
  47. // Perform ray-slab intersection (component-wise).
  48. float3 t0 = boxMin * rayDirInv - (rayOrigin * rayDirInv);
  49. float3 t1 = boxMax * rayDirInv - (rayOrigin * rayDirInv);
  50. // Find the closest/farthest distance (component-wise).
  51. float3 tSlabEntr = min(t0, t1);
  52. float3 tSlabExit = max(t0, t1);
  53. // Find the farthest entry and the nearest exit.
  54. tEntr = Max3(tSlabEntr.x, tSlabEntr.y, tSlabEntr.z);
  55. tExit = Min3(tSlabExit.x, tSlabExit.y, tSlabExit.z);
  56. // Clamp to the range.
  57. tEntr = max(tEntr, tMin);
  58. tExit = min(tExit, tMax);
  59. return tEntr < tExit;
  60. }
  61. // This simplified version assume that we care about the result only when we are inside the box
  62. float IntersectRayAABBSimple(float3 start, float3 dir, float3 boxMin, float3 boxMax)
  63. {
  64. float3 invDir = rcp(dir);
  65. // Find the ray intersection with box plane
  66. float3 rbmin = (boxMin - start) * invDir;
  67. float3 rbmax = (boxMax - start) * invDir;
  68. float3 rbminmax = (dir > 0.0) ? rbmax : rbmin;
  69. return min(min(rbminmax.x, rbminmax.y), rbminmax.z);
  70. }
  71. // Assume Sphere is at the origin (i.e start = position - spherePosition)
  72. bool IntersectRaySphere(float3 start, float3 dir, float radius, out float2 intersections)
  73. {
  74. float a = dot(dir, dir);
  75. float b = dot(dir, start) * 2.0;
  76. float c = dot(start, start) - radius * radius;
  77. float discriminant = b * b - 4.0 * a * c;
  78. bool intersect = false;
  79. intersections = float2(0.0, 0.0);
  80. if (discriminant < 0.0 || a == 0.0)
  81. {
  82. intersections.x = 0.0;
  83. intersections.y = 0.0;
  84. }
  85. else
  86. {
  87. float sqrtDiscriminant = sqrt(discriminant);
  88. intersections.x = (-b - sqrtDiscriminant) / (2.0 * a);
  89. intersections.y = (-b + sqrtDiscriminant) / (2.0 * a);
  90. intersect = true;
  91. }
  92. return intersect;
  93. }
  94. // This simplified version assume that we care about the result only when we are inside the sphere
  95. // Assume Sphere is at the origin (i.e start = position - spherePosition) and dir is normalized
  96. // Ref: http://http.developer.nvidia.com/GPUGems/gpugems_ch19.html
  97. float IntersectRaySphereSimple(float3 start, float3 dir, float radius)
  98. {
  99. float b = dot(dir, start) * 2.0;
  100. float c = dot(start, start) - radius * radius;
  101. float discriminant = b * b - 4.0 * c;
  102. return abs(sqrt(discriminant) - b) * 0.5;
  103. }
  104. float3 IntersectRayPlane(float3 rayOrigin, float3 rayDirection, float3 planeOrigin, float3 planeNormal)
  105. {
  106. float dist = dot(planeNormal, planeOrigin - rayOrigin) / dot(planeNormal, rayDirection);
  107. return rayOrigin + rayDirection * dist;
  108. }
  109. // Can support cones with an elliptic base: pre-scale 'coneAxisX' and 'coneAxisY' by (h/r_x) and (h/r_y).
  110. // Returns parametric distances 'tEntr' and 'tExit' along the ray,
  111. // subject to constraints 'tMin' and 'tMax'.
  112. bool IntersectRayCone(float3 rayOrigin, float3 rayDirection,
  113. float3 coneOrigin, float3 coneDirection,
  114. float3 coneAxisX, float3 coneAxisY,
  115. float tMin, float tMax,
  116. out float tEntr, out float tExit)
  117. {
  118. // Inverse transform the ray into a coordinate system with the cone at the origin facing along the Z axis.
  119. float3x3 rotMat = float3x3(coneAxisX, coneAxisY, coneDirection);
  120. float3 o = mul(rotMat, rayOrigin - coneOrigin);
  121. float3 d = mul(rotMat, rayDirection);
  122. // Cone equation (facing along Z): (h/r*x)^2 + (h/r*y)^2 - z^2 = 0.
  123. // Cone axes are premultiplied with (h/r).
  124. // Set up the quadratic equation: a*t^2 + b*t + c = 0.
  125. float a = d.x * d.x + d.y * d.y - d.z * d.z;
  126. float b = o.x * d.x + o.y * d.y - o.z * d.z;
  127. float c = o.x * o.x + o.y * o.y - o.z * o.z;
  128. float2 roots;
  129. // Check whether we have at least 1 root.
  130. bool hit = SolveQuadraticEquation(a, 2 * b, c, roots);
  131. tEntr = min(roots.x, roots.y);
  132. tExit = max(roots.x, roots.y);
  133. float3 pEntr = o + tEntr * d;
  134. float3 pExit = o + tExit * d;
  135. // Clip the negative cone.
  136. bool pEntrNeg = pEntr.z < 0;
  137. bool pExitNeg = pExit.z < 0;
  138. if (pEntrNeg && pExitNeg) { hit = false; }
  139. if (pEntrNeg) { tEntr = tExit; tExit = tMax; }
  140. if (pExitNeg) { tExit = tEntr; tEntr = tMin; }
  141. // Clamp using the values passed into the function.
  142. tEntr = clamp(tEntr, tMin, tMax);
  143. tExit = clamp(tExit, tMin, tMax);
  144. // Check for grazing intersections.
  145. if (tEntr == tExit) { hit = false; }
  146. return hit;
  147. }
  148. //-----------------------------------------------------------------------------
  149. // Miscellaneous functions
  150. //-----------------------------------------------------------------------------
  151. // Box is AABB
  152. float DistancePointBox(float3 position, float3 boxMin, float3 boxMax)
  153. {
  154. return length(max(max(position - boxMax, boxMin - position), float3(0.0, 0.0, 0.0)));
  155. }
  156. float3 ProjectPointOnPlane(float3 position, float3 planePosition, float3 planeNormal)
  157. {
  158. return position - (dot(position - planePosition, planeNormal) * planeNormal);
  159. }
  160. // Plane equation: {(a, b, c) = N, d = -dot(N, P)}.
  161. // Returns the distance from the plane to the point 'p' along the normal.
  162. // Positive -> in front (above), negative -> behind (below).
  163. float DistanceFromPlane(float3 p, float4 plane)
  164. {
  165. return dot(float4(p, 1.0), plane);
  166. }
  167. // Returns 'true' if the triangle is outside of the frustum.
  168. // 'epsilon' is the (negative) distance to (outside of) the frustum below which we cull the triangle.
  169. bool CullTriangleFrustum(float3 p0, float3 p1, float3 p2, float epsilon, float4 frustumPlanes[6], int numPlanes)
  170. {
  171. bool outside = false;
  172. for (int i = 0; i < numPlanes; i++)
  173. {
  174. // If all 3 points are behind any of the planes, we cull.
  175. outside = outside || Max3(DistanceFromPlane(p0, frustumPlanes[i]),
  176. DistanceFromPlane(p1, frustumPlanes[i]),
  177. DistanceFromPlane(p2, frustumPlanes[i])) < epsilon;
  178. }
  179. return outside;
  180. }
  181. // Returns 'true' if the edge of the triangle is outside of the frustum.
  182. // The edges are defined s.t. they are on the opposite side of the point with the given index.
  183. // 'epsilon' is the (negative) distance to (outside of) the frustum below which we cull the triangle.
  184. bool3 CullTriangleEdgesFrustum(float3 p0, float3 p1, float3 p2, float epsilon, float4 frustumPlanes[6], int numPlanes)
  185. {
  186. bool3 edgesOutside = false;
  187. for (int i = 0; i < numPlanes; i++)
  188. {
  189. bool3 pointsOutside = bool3(DistanceFromPlane(p0, frustumPlanes[i]) < epsilon,
  190. DistanceFromPlane(p1, frustumPlanes[i]) < epsilon,
  191. DistanceFromPlane(p2, frustumPlanes[i]) < epsilon);
  192. // If both points of the edge are behind any of the planes, we cull.
  193. edgesOutside.x = edgesOutside.x || (pointsOutside.y && pointsOutside.z);
  194. edgesOutside.y = edgesOutside.y || (pointsOutside.x && pointsOutside.z);
  195. edgesOutside.z = edgesOutside.z || (pointsOutside.x && pointsOutside.y);
  196. }
  197. return edgesOutside;
  198. }
  199. bool CullTriangleBackFaceView(float3 p0, float3 p1, float3 p2, float epsilon, float3 V, float winding)
  200. {
  201. float3 edge1 = p1 - p0;
  202. float3 edge2 = p2 - p0;
  203. float3 N = cross(edge1, edge2);
  204. float NdotV = dot(N, V) * winding;
  205. // Optimize:
  206. // NdotV / (length(N) * length(V)) < Epsilon
  207. // NdotV < Epsilon * length(N) * length(V)
  208. // NdotV < Epsilon * sqrt(dot(N, N)) * sqrt(dot(V, V))
  209. // NdotV < Epsilon * sqrt(dot(N, N) * dot(V, V))
  210. return NdotV < epsilon * sqrt(dot(N, N) * dot(V, V));
  211. }
  212. // Returns 'true' if a triangle defined by 3 vertices is back-facing.
  213. // 'epsilon' is the (negative) value of dot(N, V) below which we cull the triangle.
  214. // 'winding' can be used to change the order: pass 1 for (p0 -> p1 -> p2), or -1 for (p0 -> p2 -> p1).
  215. bool CullTriangleBackFace(float3 p0, float3 p1, float3 p2, float epsilon, float3 viewPos, float winding)
  216. {
  217. float3 V = viewPos - p0;
  218. return CullTriangleBackFaceView(p0, p1, p2, epsilon, V, winding);
  219. }
  220. #endif // UNITY_GEOMETRICTOOLS_INCLUDED