ShadowUtils.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. using System;
  2. using UnityEngine.Scripting.APIUpdating;
  3. namespace UnityEngine.Rendering.Universal
  4. {
  5. [MovedFrom("UnityEngine.Rendering.LWRP")] public struct ShadowSliceData
  6. {
  7. public Matrix4x4 viewMatrix;
  8. public Matrix4x4 projectionMatrix;
  9. public Matrix4x4 shadowTransform;
  10. public int offsetX;
  11. public int offsetY;
  12. public int resolution;
  13. public void Clear()
  14. {
  15. viewMatrix = Matrix4x4.identity;
  16. projectionMatrix = Matrix4x4.identity;
  17. shadowTransform = Matrix4x4.identity;
  18. offsetX = offsetY = 0;
  19. resolution = 1024;
  20. }
  21. }
  22. [MovedFrom("UnityEngine.Rendering.LWRP")] public static class ShadowUtils
  23. {
  24. private static readonly RenderTextureFormat m_ShadowmapFormat;
  25. private static readonly bool m_ForceShadowPointSampling;
  26. static ShadowUtils()
  27. {
  28. m_ShadowmapFormat = RenderingUtils.SupportsRenderTextureFormat(RenderTextureFormat.Shadowmap) && (SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLES2)
  29. ? RenderTextureFormat.Shadowmap
  30. : RenderTextureFormat.Depth;
  31. m_ForceShadowPointSampling = SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal &&
  32. GraphicsSettings.HasShaderDefine(Graphics.activeTier, BuiltinShaderDefine.UNITY_METAL_SHADOWS_USE_POINT_FILTERING);
  33. }
  34. public static bool ExtractDirectionalLightMatrix(ref CullingResults cullResults, ref ShadowData shadowData, int shadowLightIndex, int cascadeIndex, int shadowmapWidth, int shadowmapHeight, int shadowResolution, float shadowNearPlane, out Vector4 cascadeSplitDistance, out ShadowSliceData shadowSliceData, out Matrix4x4 viewMatrix, out Matrix4x4 projMatrix)
  35. {
  36. ShadowSplitData splitData;
  37. bool success = cullResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(shadowLightIndex,
  38. cascadeIndex, shadowData.mainLightShadowCascadesCount, shadowData.mainLightShadowCascadesSplit, shadowResolution, shadowNearPlane, out viewMatrix, out projMatrix,
  39. out splitData);
  40. cascadeSplitDistance = splitData.cullingSphere;
  41. shadowSliceData.offsetX = (cascadeIndex % 2) * shadowResolution;
  42. shadowSliceData.offsetY = (cascadeIndex / 2) * shadowResolution;
  43. shadowSliceData.resolution = shadowResolution;
  44. shadowSliceData.viewMatrix = viewMatrix;
  45. shadowSliceData.projectionMatrix = projMatrix;
  46. shadowSliceData.shadowTransform = GetShadowTransform(projMatrix, viewMatrix);
  47. // If we have shadow cascades baked into the atlas we bake cascade transform
  48. // in each shadow matrix to save shader ALU and L/S
  49. if (shadowData.mainLightShadowCascadesCount > 1)
  50. ApplySliceTransform(ref shadowSliceData, shadowmapWidth, shadowmapHeight);
  51. return success;
  52. }
  53. public static bool ExtractSpotLightMatrix(ref CullingResults cullResults, ref ShadowData shadowData, int shadowLightIndex, out Matrix4x4 shadowMatrix, out Matrix4x4 viewMatrix, out Matrix4x4 projMatrix)
  54. {
  55. ShadowSplitData splitData;
  56. bool success = cullResults.ComputeSpotShadowMatricesAndCullingPrimitives(shadowLightIndex, out viewMatrix, out projMatrix, out splitData);
  57. shadowMatrix = GetShadowTransform(projMatrix, viewMatrix);
  58. return success;
  59. }
  60. public static void RenderShadowSlice(CommandBuffer cmd, ref ScriptableRenderContext context,
  61. ref ShadowSliceData shadowSliceData, ref ShadowDrawingSettings settings,
  62. Matrix4x4 proj, Matrix4x4 view)
  63. {
  64. cmd.SetViewport(new Rect(shadowSliceData.offsetX, shadowSliceData.offsetY, shadowSliceData.resolution, shadowSliceData.resolution));
  65. cmd.SetViewProjectionMatrices(view, proj);
  66. context.ExecuteCommandBuffer(cmd);
  67. cmd.Clear();
  68. context.DrawShadows(ref settings);
  69. cmd.DisableScissorRect();
  70. context.ExecuteCommandBuffer(cmd);
  71. cmd.Clear();
  72. }
  73. public static void RenderShadowSlice(CommandBuffer cmd, ref ScriptableRenderContext context,
  74. ref ShadowSliceData shadowSliceData, ref ShadowDrawingSettings settings)
  75. {
  76. RenderShadowSlice(cmd, ref context, ref shadowSliceData, ref settings,
  77. shadowSliceData.projectionMatrix, shadowSliceData.viewMatrix);
  78. }
  79. public static int GetMaxTileResolutionInAtlas(int atlasWidth, int atlasHeight, int tileCount)
  80. {
  81. int resolution = Mathf.Min(atlasWidth, atlasHeight);
  82. int currentTileCount = atlasWidth / resolution * atlasHeight / resolution;
  83. while (currentTileCount < tileCount)
  84. {
  85. resolution = resolution >> 1;
  86. currentTileCount = atlasWidth / resolution * atlasHeight / resolution;
  87. }
  88. return resolution;
  89. }
  90. public static void ApplySliceTransform(ref ShadowSliceData shadowSliceData, int atlasWidth, int atlasHeight)
  91. {
  92. Matrix4x4 sliceTransform = Matrix4x4.identity;
  93. float oneOverAtlasWidth = 1.0f / atlasWidth;
  94. float oneOverAtlasHeight = 1.0f / atlasHeight;
  95. sliceTransform.m00 = shadowSliceData.resolution * oneOverAtlasWidth;
  96. sliceTransform.m11 = shadowSliceData.resolution * oneOverAtlasHeight;
  97. sliceTransform.m03 = shadowSliceData.offsetX * oneOverAtlasWidth;
  98. sliceTransform.m13 = shadowSliceData.offsetY * oneOverAtlasHeight;
  99. // Apply shadow slice scale and offset
  100. shadowSliceData.shadowTransform = sliceTransform * shadowSliceData.shadowTransform;
  101. }
  102. public static Vector4 GetShadowBias(ref VisibleLight shadowLight, int shadowLightIndex, ref ShadowData shadowData, Matrix4x4 lightProjectionMatrix, float shadowResolution)
  103. {
  104. if (shadowLightIndex < 0 || shadowLightIndex >= shadowData.bias.Count)
  105. {
  106. Debug.LogWarning(string.Format("{0} is not a valid light index.", shadowLightIndex));
  107. return Vector4.zero;
  108. }
  109. float frustumSize;
  110. if (shadowLight.lightType == LightType.Directional)
  111. {
  112. // Frustum size is guaranteed to be a cube as we wrap shadow frustum around a sphere
  113. frustumSize = 2.0f / lightProjectionMatrix.m00;
  114. }
  115. else if (shadowLight.lightType == LightType.Spot)
  116. {
  117. // For perspective projections, shadow texel size varies with depth
  118. // It will only work well if done in receiver side in the pixel shader. Currently UniversalRP
  119. // do bias on caster side in vertex shader. When we add shader quality tiers we can properly
  120. // handle this. For now, as a poor approximation we do a constant bias and compute the size of
  121. // the frustum as if it was orthogonal considering the size at mid point between near and far planes.
  122. // Depending on how big the light range is, it will be good enough with some tweaks in bias
  123. frustumSize = Mathf.Tan(shadowLight.spotAngle * 0.5f * Mathf.Deg2Rad) * shadowLight.range;
  124. }
  125. else
  126. {
  127. Debug.LogWarning("Only spot and directional shadow casters are supported in universal pipeline");
  128. frustumSize = 0.0f;
  129. }
  130. // depth and normal bias scale is in shadowmap texel size in world space
  131. float texelSize = frustumSize / shadowResolution;
  132. float depthBias = -shadowData.bias[shadowLightIndex].x * texelSize;
  133. float normalBias = -shadowData.bias[shadowLightIndex].y * texelSize;
  134. if (shadowData.supportsSoftShadows)
  135. {
  136. // TODO: depth and normal bias assume sample is no more than 1 texel away from shadowmap
  137. // This is not true with PCF. Ideally we need to do either
  138. // cone base bias (based on distance to center sample)
  139. // or receiver place bias based on derivatives.
  140. // For now we scale it by the PCF kernel size (5x5)
  141. const float kernelRadius = 2.5f;
  142. depthBias *= kernelRadius;
  143. normalBias *= kernelRadius;
  144. }
  145. return new Vector4(depthBias, normalBias, 0.0f, 0.0f);
  146. }
  147. public static void SetupShadowCasterConstantBuffer(CommandBuffer cmd, ref VisibleLight shadowLight, Vector4 shadowBias)
  148. {
  149. Vector3 lightDirection = -shadowLight.localToWorldMatrix.GetColumn(2);
  150. cmd.SetGlobalVector("_ShadowBias", shadowBias);
  151. cmd.SetGlobalVector("_LightDirection", new Vector4(lightDirection.x, lightDirection.y, lightDirection.z, 0.0f));
  152. }
  153. public static RenderTexture GetTemporaryShadowTexture(int width, int height, int bits)
  154. {
  155. var shadowTexture = RenderTexture.GetTemporary(width, height, bits, m_ShadowmapFormat);
  156. shadowTexture.filterMode = m_ForceShadowPointSampling ? FilterMode.Point : FilterMode.Bilinear;
  157. shadowTexture.wrapMode = TextureWrapMode.Clamp;
  158. return shadowTexture;
  159. }
  160. static Matrix4x4 GetShadowTransform(Matrix4x4 proj, Matrix4x4 view)
  161. {
  162. // Currently CullResults ComputeDirectionalShadowMatricesAndCullingPrimitives doesn't
  163. // apply z reversal to projection matrix. We need to do it manually here.
  164. if (SystemInfo.usesReversedZBuffer)
  165. {
  166. proj.m20 = -proj.m20;
  167. proj.m21 = -proj.m21;
  168. proj.m22 = -proj.m22;
  169. proj.m23 = -proj.m23;
  170. }
  171. Matrix4x4 worldToShadow = proj * view;
  172. var textureScaleAndBias = Matrix4x4.identity;
  173. textureScaleAndBias.m00 = 0.5f;
  174. textureScaleAndBias.m11 = 0.5f;
  175. textureScaleAndBias.m22 = 0.5f;
  176. textureScaleAndBias.m03 = 0.5f;
  177. textureScaleAndBias.m23 = 0.5f;
  178. textureScaleAndBias.m13 = 0.5f;
  179. // Apply texture scale and offset to save a MAD in shader.
  180. return textureScaleAndBias * worldToShadow;
  181. }
  182. }
  183. }