BsShadowRendering.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsShadowRendering.h"
  4. #include "BsRendererView.h"
  5. namespace bs { namespace ct
  6. {
  7. ConvexVolume ShadowRendering::getCSMSplitFrustum(const RendererView& view, const Vector3& lightDir, UINT32 cascade,
  8. UINT32 numCascades, Sphere& outBounds)
  9. {
  10. // Determine split range
  11. float splitNear = getCSMSplitDistance(view, cascade, numCascades);
  12. float splitFar = getCSMSplitDistance(view, cascade + 1, numCascades);
  13. // Calculate the eight vertices of the split frustum
  14. auto& viewProps = view.getProperties();
  15. const Matrix4& projMat = viewProps.projTransform;
  16. float aspect;
  17. float nearHalfWidth, nearHalfHeight;
  18. float farHalfWidth, farHalfHeight;
  19. if(viewProps.projType == PT_PERSPECTIVE)
  20. {
  21. aspect = projMat[0][0] / projMat[1][1];
  22. float tanHalfFOV = 1.0f / projMat[0][0];
  23. nearHalfWidth = splitNear * tanHalfFOV;
  24. nearHalfHeight = nearHalfWidth * aspect;
  25. farHalfWidth = splitFar * tanHalfFOV;
  26. farHalfHeight = farHalfWidth * aspect;
  27. }
  28. else
  29. {
  30. aspect = projMat[0][0] / projMat[1][1];
  31. nearHalfWidth = farHalfWidth = projMat[0][0] / 4.0f;
  32. nearHalfHeight = farHalfHeight = projMat[1][1] / 4.0f;
  33. }
  34. const Matrix4& viewMat = viewProps.viewTransform;
  35. Vector3 cameraRight = Vector3(viewMat[0]);
  36. Vector3 cameraUp = Vector3(viewMat[1]);
  37. const Vector3& viewOrigin = viewProps.viewOrigin;
  38. const Vector3& viewDir = viewProps.viewDirection;
  39. Vector3 frustumVerts[] =
  40. {
  41. viewOrigin + viewDir * splitNear - cameraRight * nearHalfWidth + cameraUp * nearHalfHeight, // Near, left, top
  42. viewOrigin + viewDir * splitNear + cameraRight * nearHalfWidth + cameraUp * nearHalfHeight, // Near, right, top
  43. viewOrigin + viewDir * splitNear + cameraRight * nearHalfWidth - cameraUp * nearHalfHeight, // Near, right, bottom
  44. viewOrigin + viewDir * splitNear - cameraRight * nearHalfWidth - cameraUp * nearHalfHeight, // Near, left, bottom
  45. viewOrigin + viewDir * splitFar - cameraRight * farHalfWidth + cameraUp * farHalfHeight, // Far, left, top
  46. viewOrigin + viewDir * splitFar + cameraRight * farHalfWidth + cameraUp * farHalfHeight, // Far, right, top
  47. viewOrigin + viewDir * splitFar + cameraRight * farHalfWidth - cameraUp * farHalfHeight, // Far, right, bottom
  48. viewOrigin + viewDir * splitFar - cameraRight * farHalfWidth - cameraUp * farHalfHeight, // Far, left, bottom
  49. };
  50. // Calculate the bounding sphere of the frustum
  51. float diagonalNearSq = nearHalfWidth * nearHalfWidth + nearHalfHeight * nearHalfHeight;
  52. float diagonalFarSq = farHalfWidth * farHalfWidth + farHalfHeight * farHalfHeight;
  53. float length = splitFar - splitNear;
  54. float offset = (diagonalNearSq - diagonalFarSq) / 2 * length + length * 0.5f;
  55. float distToCenter = Math::clamp(splitFar - offset, splitNear, splitFar);
  56. Vector3 center = viewOrigin + viewDir * distToCenter;
  57. float radius = 0.0f;
  58. for (auto& entry : frustumVerts)
  59. radius = std::max(radius, center.squaredDistance(entry));
  60. radius = std::max(sqrt(radius), 1.0f);
  61. outBounds = Sphere(center, radius);
  62. // Generate light frustum planes
  63. Plane viewPlanes[6];
  64. viewPlanes[FRUSTUM_PLANE_NEAR] = Plane(frustumVerts[0], frustumVerts[1], frustumVerts[2]);
  65. viewPlanes[FRUSTUM_PLANE_FAR] = Plane(frustumVerts[5], frustumVerts[4], frustumVerts[7]);
  66. viewPlanes[FRUSTUM_PLANE_LEFT] = Plane(frustumVerts[4], frustumVerts[0], frustumVerts[3]);
  67. viewPlanes[FRUSTUM_PLANE_RIGHT] = Plane(frustumVerts[1], frustumVerts[5], frustumVerts[6]);
  68. viewPlanes[FRUSTUM_PLANE_TOP] = Plane(frustumVerts[4], frustumVerts[5], frustumVerts[1]);
  69. viewPlanes[FRUSTUM_PLANE_BOTTOM] = Plane(frustumVerts[3], frustumVerts[2], frustumVerts[6]);
  70. Vector<Plane> lightVolume;
  71. //// Add camera's planes facing towards the lights (forming the back of the volume)
  72. for(auto& entry : viewPlanes)
  73. {
  74. if (entry.normal.dot(lightDir) < 0.0f)
  75. lightVolume.push_back(entry);
  76. }
  77. //// Determine edge planes by testing adjacent planes with different facing
  78. ////// Pairs of frustum planes that share an edge
  79. UINT32 adjacentPlanes[][2] =
  80. {
  81. { FRUSTUM_PLANE_NEAR, FRUSTUM_PLANE_LEFT },
  82. { FRUSTUM_PLANE_NEAR, FRUSTUM_PLANE_RIGHT },
  83. { FRUSTUM_PLANE_NEAR, FRUSTUM_PLANE_TOP },
  84. { FRUSTUM_PLANE_NEAR, FRUSTUM_PLANE_BOTTOM },
  85. { FRUSTUM_PLANE_FAR, FRUSTUM_PLANE_LEFT },
  86. { FRUSTUM_PLANE_FAR, FRUSTUM_PLANE_RIGHT },
  87. { FRUSTUM_PLANE_FAR, FRUSTUM_PLANE_TOP },
  88. { FRUSTUM_PLANE_FAR, FRUSTUM_PLANE_BOTTOM },
  89. { FRUSTUM_PLANE_LEFT, FRUSTUM_PLANE_TOP },
  90. { FRUSTUM_PLANE_TOP, FRUSTUM_PLANE_RIGHT },
  91. { FRUSTUM_PLANE_RIGHT, FRUSTUM_PLANE_BOTTOM },
  92. { FRUSTUM_PLANE_BOTTOM, FRUSTUM_PLANE_LEFT },
  93. };
  94. ////// Vertex indices of edges on the boundary between two planes
  95. UINT32 sharedEdges[][2] =
  96. {
  97. { 3, 0 },{ 1, 2 },{ 0, 1 },{ 2, 3 },
  98. { 4, 7 },{ 6, 5 },{ 5, 4 },{ 7, 6 },
  99. { 4, 0 },{ 5, 1 },{ 6, 2 },{ 7, 3 }
  100. };
  101. for(UINT32 i = 0; i < 12; i++)
  102. {
  103. const Plane& planeA = viewPlanes[adjacentPlanes[i][0]];
  104. const Plane& planeB = viewPlanes[adjacentPlanes[i][1]];
  105. float dotA = planeA.normal.dot(lightDir);
  106. float dotB = planeB.normal.dot(lightDir);
  107. if((dotA * dotB) < 0.0f)
  108. {
  109. const Vector3& vertA = frustumVerts[sharedEdges[i][0]];
  110. const Vector3& vertB = frustumVerts[sharedEdges[i][1]];
  111. Vector3 vertC = vertA + lightDir;
  112. if (dotA >= 0.0f)
  113. lightVolume.push_back(Plane(vertA, vertB, vertC));
  114. else
  115. lightVolume.push_back(Plane(vertB, vertA, vertC));
  116. }
  117. }
  118. return ConvexVolume(lightVolume);
  119. }
  120. float ShadowRendering::getCSMSplitDistance(const RendererView& view, UINT32 index, UINT32 numCascades)
  121. {
  122. // Determines the size of each subsequent cascade split. Value of 1 means the cascades will be linearly split.
  123. // Value of 2 means each subsequent split will be twice the size of the previous one. Valid range is roughly
  124. // [1, 4].
  125. // Note: Make this an adjustable property?
  126. const static float DISTRIBUTON_EXPONENT = 1.0f;
  127. // First determine the scale of the split, relative to the entire range
  128. float scaleModifier = 1.0f;
  129. float scale = 0.0f;
  130. float totalScale = 0.0f;
  131. //// Split 0 corresponds to near plane
  132. if (index > 0)
  133. {
  134. for (UINT32 i = 0; i < numCascades; i++)
  135. {
  136. if (i < index)
  137. scale += scaleModifier;
  138. totalScale += scaleModifier;
  139. scaleModifier *= DISTRIBUTON_EXPONENT;
  140. }
  141. }
  142. scale = scale / totalScale;
  143. // Calculate split distance in Z
  144. auto& viewProps = view.getProperties();
  145. float near = viewProps.nearPlane;
  146. float far = viewProps.farPlane;
  147. return near + (far - near) * scale;
  148. }
  149. }}