static void RenderShadows(RenderShadowsParameters parameters, RTHandle atlasRenderTexture, ShadowDrawingSettings shadowDrawSettings, ScriptableRenderContext renderContext, CommandBuffer cmd) { cmd.SetRenderTarget(atlasRenderTexture, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store); cmd.SetGlobalVector(parameters.atlasSizeShaderID, new Vector4(atlasRenderTexture.rt.width, atlasRenderTexture.rt.height, 1.0f / atlasRenderTexture.rt.width, 1.0f / atlasRenderTexture.rt.height)); // Clear the whole atlas to avoid garbage outside of current request when viewing it. if (parameters.debugClearAtlas) { CoreUtils.DrawFullScreen(cmd, parameters.clearMaterial, null, 0); } var hdrp = RenderPipelineManager.currentPipeline as HDRenderPipeline; foreach (var shadowRequest in parameters.shadowRequests) { if (shadowRequest.shouldUseCachedShadow) { continue; } cmd.SetGlobalDepthBias(1.0f, shadowRequest.slopeBias); cmd.SetViewport(shadowRequest.atlasViewport); cmd.SetGlobalFloat(HDShaderIDs._ZClip, shadowRequest.zClip ? 1.0f : 0.0f); CoreUtils.DrawFullScreen(cmd, parameters.clearMaterial, null, 0); shadowDrawSettings.lightIndex = shadowRequest.lightIndex; shadowDrawSettings.splitData = shadowRequest.splitData; // Setup matrices for shadow rendering: Matrix4x4 viewProjection = shadowRequest.deviceProjectionYFlip * shadowRequest.view; cmd.SetGlobalMatrix(HDShaderIDs._ViewMatrix, shadowRequest.view); cmd.SetGlobalMatrix(HDShaderIDs._InvViewMatrix, shadowRequest.view.inverse); cmd.SetGlobalMatrix(HDShaderIDs._ProjMatrix, shadowRequest.deviceProjectionYFlip); cmd.SetGlobalMatrix(HDShaderIDs._InvProjMatrix, shadowRequest.deviceProjectionYFlip.inverse); cmd.SetGlobalMatrix(HDShaderIDs._ViewProjMatrix, viewProjection); cmd.SetGlobalMatrix(HDShaderIDs._InvViewProjMatrix, viewProjection.inverse); cmd.SetGlobalVectorArray(HDShaderIDs._ShadowClipPlanes, shadowRequest.frustumPlanes); // TODO: remove this execute when DrawShadows will use a CommandBuffer renderContext.ExecuteCommandBuffer(cmd); cmd.Clear(); renderContext.DrawShadows(ref shadowDrawSettings); hdrp?.InvokeShadowMapRender(cmd, shadowRequest.worldTexelSize); } cmd.SetGlobalFloat(HDShaderIDs._ZClip, 1.0f); // Re-enable zclip globally cmd.SetGlobalDepthBias(0.0f, 0.0f); // Reset depth bias. }
RenderShadowsParameters PrepareRenderShadowsParameters() { var parameters = new RenderShadowsParameters(); parameters.shadowRequests = m_ShadowRequests; parameters.clearMaterial = m_ClearMaterial; parameters.debugClearAtlas = m_LightingDebugSettings.clearShadowAtlas; parameters.atlasShaderID = m_AtlasShaderID; parameters.atlasSizeShaderID = m_AtlasSizeShaderID; parameters.blurAlgorithm = m_BlurAlgorithm; // EVSM parameters.evsmShadowBlurMomentsCS = m_RenderPipelineResources.shaders.evsmBlurCS; parameters.momentAtlasShaderID = m_MomentAtlasShaderID; // IM parameters.imShadowBlurMomentsCS = m_RenderPipelineResources.shaders.momentShadowsCS; return(parameters); }
static void IMBlurMoment(RenderShadowsParameters parameters, RTHandle atlas, RTHandle atlasMoment, RTHandle intermediateSummedAreaTexture, RTHandle summedAreaTexture, CommandBuffer cmd) { // If the target kernel is not available ComputeShader momentCS = parameters.imShadowBlurMomentsCS; if (momentCS == null) { return; } using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.RenderMomentShadowMaps))) { int computeMomentKernel = momentCS.FindKernel("ComputeMomentShadows"); int summedAreaHorizontalKernel = momentCS.FindKernel("MomentSummedAreaTableHorizontal"); int summedAreaVerticalKernel = momentCS.FindKernel("MomentSummedAreaTableVertical"); // First of all let's clear the moment shadow map CoreUtils.SetRenderTarget(cmd, atlasMoment, ClearFlag.Color, Color.black); CoreUtils.SetRenderTarget(cmd, intermediateSummedAreaTexture, ClearFlag.Color, Color.black); CoreUtils.SetRenderTarget(cmd, summedAreaTexture, ClearFlag.Color, Color.black); // Alright, so the thing here is that for every sub-shadow map of the atlas, we need to generate the moment shadow map foreach (var shadowRequest in parameters.shadowRequests) { // Let's bind the resources of this cmd.SetComputeTextureParam(momentCS, computeMomentKernel, HDShaderIDs._ShadowmapAtlas, atlas); cmd.SetComputeTextureParam(momentCS, computeMomentKernel, HDShaderIDs._MomentShadowAtlas, atlasMoment); cmd.SetComputeVectorParam(momentCS, HDShaderIDs._MomentShadowmapSlotST, new Vector4(shadowRequest.atlasViewport.width, shadowRequest.atlasViewport.height, shadowRequest.atlasViewport.min.x, shadowRequest.atlasViewport.min.y)); // First of all we need to compute the moments int numTilesX = Math.Max((int)shadowRequest.atlasViewport.width / 8, 1); int numTilesY = Math.Max((int)shadowRequest.atlasViewport.height / 8, 1); cmd.DispatchCompute(momentCS, computeMomentKernel, numTilesX, numTilesY, 1); // Do the horizontal pass of the summed area table cmd.SetComputeTextureParam(momentCS, summedAreaHorizontalKernel, HDShaderIDs._SummedAreaTableInputFloat, atlasMoment); cmd.SetComputeTextureParam(momentCS, summedAreaHorizontalKernel, HDShaderIDs._SummedAreaTableOutputInt, intermediateSummedAreaTexture); cmd.SetComputeFloatParam(momentCS, HDShaderIDs._IMSKernelSize, shadowRequest.kernelSize); cmd.SetComputeVectorParam(momentCS, HDShaderIDs._MomentShadowmapSize, new Vector2((float)atlasMoment.referenceSize.x, (float)atlasMoment.referenceSize.y)); int numLines = Math.Max((int)shadowRequest.atlasViewport.width / 64, 1); cmd.DispatchCompute(momentCS, summedAreaHorizontalKernel, numLines, 1, 1); // Do the horizontal pass of the summed area table cmd.SetComputeTextureParam(momentCS, summedAreaVerticalKernel, HDShaderIDs._SummedAreaTableInputInt, intermediateSummedAreaTexture); cmd.SetComputeTextureParam(momentCS, summedAreaVerticalKernel, HDShaderIDs._SummedAreaTableOutputInt, summedAreaTexture); cmd.SetComputeVectorParam(momentCS, HDShaderIDs._MomentShadowmapSize, new Vector2((float)atlasMoment.referenceSize.x, (float)atlasMoment.referenceSize.y)); cmd.SetComputeFloatParam(momentCS, HDShaderIDs._IMSKernelSize, shadowRequest.kernelSize); int numColumns = Math.Max((int)shadowRequest.atlasViewport.height / 64, 1); cmd.DispatchCompute(momentCS, summedAreaVerticalKernel, numColumns, 1, 1); // Push the global texture cmd.SetGlobalTexture(HDShaderIDs._SummedAreaTableInputInt, summedAreaTexture); } } }
unsafe static void EVSMBlurMoments(RenderShadowsParameters parameters, RTHandle atlasRenderTexture, RTHandle[] momentAtlasRenderTextures, CommandBuffer cmd) { ComputeShader shadowBlurMomentsCS = parameters.evsmShadowBlurMomentsCS; using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.RenderEVSMShadowMaps))) { int generateAndBlurMomentsKernel = shadowBlurMomentsCS.FindKernel("ConvertAndBlur"); int blurMomentsKernel = shadowBlurMomentsCS.FindKernel("Blur"); int copyMomentsKernel = shadowBlurMomentsCS.FindKernel("CopyMoments"); cmd.SetComputeTextureParam(shadowBlurMomentsCS, generateAndBlurMomentsKernel, HDShaderIDs._DepthTexture, atlasRenderTexture); cmd.SetComputeVectorArrayParam(shadowBlurMomentsCS, HDShaderIDs._BlurWeightsStorage, evsmBlurWeights); // We need to store in which of the two moment texture a request will have its last version stored in for a final patch up at the end. var finalAtlasTexture = stackalloc int[parameters.shadowRequests.Count]; int requestIdx = 0; foreach (var shadowRequest in parameters.shadowRequests) { using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.RenderEVSMShadowMapsBlur))) { int downsampledWidth = Mathf.CeilToInt(shadowRequest.atlasViewport.width * 0.5f); int downsampledHeight = Mathf.CeilToInt(shadowRequest.atlasViewport.height * 0.5f); Vector2 DstRectOffset = new Vector2(shadowRequest.atlasViewport.min.x * 0.5f, shadowRequest.atlasViewport.min.y * 0.5f); cmd.SetComputeTextureParam(shadowBlurMomentsCS, generateAndBlurMomentsKernel, HDShaderIDs._OutputTexture, momentAtlasRenderTextures[0]); cmd.SetComputeVectorParam(shadowBlurMomentsCS, HDShaderIDs._SrcRect, new Vector4(shadowRequest.atlasViewport.min.x, shadowRequest.atlasViewport.min.y, shadowRequest.atlasViewport.width, shadowRequest.atlasViewport.height)); cmd.SetComputeVectorParam(shadowBlurMomentsCS, HDShaderIDs._DstRect, new Vector4(DstRectOffset.x, DstRectOffset.y, 1.0f / atlasRenderTexture.rt.width, 1.0f / atlasRenderTexture.rt.height)); cmd.SetComputeFloatParam(shadowBlurMomentsCS, HDShaderIDs._EVSMExponent, shadowRequest.evsmParams.x); int dispatchSizeX = ((int)downsampledWidth + 7) / 8; int dispatchSizeY = ((int)downsampledHeight + 7) / 8; cmd.DispatchCompute(shadowBlurMomentsCS, generateAndBlurMomentsKernel, dispatchSizeX, dispatchSizeY, 1); int currentAtlasMomentSurface = 0; RTHandle GetMomentRT() { return(momentAtlasRenderTextures[currentAtlasMomentSurface]); } RTHandle GetMomentRTCopy() { return(momentAtlasRenderTextures[(currentAtlasMomentSurface + 1) & 1]); } cmd.SetComputeVectorParam(shadowBlurMomentsCS, HDShaderIDs._SrcRect, new Vector4(DstRectOffset.x, DstRectOffset.y, downsampledWidth, downsampledHeight)); for (int i = 0; i < shadowRequest.evsmParams.w; ++i) { currentAtlasMomentSurface = (currentAtlasMomentSurface + 1) & 1; cmd.SetComputeTextureParam(shadowBlurMomentsCS, blurMomentsKernel, HDShaderIDs._InputTexture, GetMomentRTCopy()); cmd.SetComputeTextureParam(shadowBlurMomentsCS, blurMomentsKernel, HDShaderIDs._OutputTexture, GetMomentRT()); cmd.DispatchCompute(shadowBlurMomentsCS, blurMomentsKernel, dispatchSizeX, dispatchSizeY, 1); } finalAtlasTexture[requestIdx++] = currentAtlasMomentSurface; } } // We patch up the atlas with the requests that, due to different count of blur passes, remained in the copy for (int i = 0; i < parameters.shadowRequests.Count; ++i) { if (finalAtlasTexture[i] != 0) { using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.RenderEVSMShadowMapsCopyToAtlas))) { var shadowRequest = parameters.shadowRequests[i]; int downsampledWidth = Mathf.CeilToInt(shadowRequest.atlasViewport.width * 0.5f); int downsampledHeight = Mathf.CeilToInt(shadowRequest.atlasViewport.height * 0.5f); cmd.SetComputeVectorParam(shadowBlurMomentsCS, HDShaderIDs._SrcRect, new Vector4(shadowRequest.atlasViewport.min.x * 0.5f, shadowRequest.atlasViewport.min.y * 0.5f, downsampledWidth, downsampledHeight)); cmd.SetComputeTextureParam(shadowBlurMomentsCS, copyMomentsKernel, HDShaderIDs._InputTexture, momentAtlasRenderTextures[1]); cmd.SetComputeTextureParam(shadowBlurMomentsCS, copyMomentsKernel, HDShaderIDs._OutputTexture, momentAtlasRenderTextures[0]); int dispatchSizeX = ((int)downsampledWidth + 7) / 8; int dispatchSizeY = ((int)downsampledHeight + 7) / 8; cmd.DispatchCompute(shadowBlurMomentsCS, copyMomentsKernel, dispatchSizeX, dispatchSizeY, 1); } } } } }
static void EVSMBlurMoments(RenderShadowsParameters parameters, RTHandle atlasRenderTexture, RTHandle[] momentAtlasRenderTextures, CommandBuffer cmd) { ComputeShader shadowBlurMomentsCS = parameters.evsmShadowBlurMomentsCS; using (new ProfilingSample(cmd, "Render & Blur Moment Shadows", CustomSamplerId.RenderShadowMaps.GetSampler())) { int generateAndBlurMomentsKernel = shadowBlurMomentsCS.FindKernel("ConvertAndBlur"); int blurMomentsKernel = shadowBlurMomentsCS.FindKernel("Blur"); int copyMomentsKernel = shadowBlurMomentsCS.FindKernel("CopyMoments"); Vector4[] blurWeights = new Vector4[2]; // This is a 9 tap filter, a gaussian with std. dev of 3. This standard deviation with this amount of taps probably cuts // the tail of the gaussian a bit too much, and it is a very fat curve, but it seems to work fine for our use case. blurWeights[0].x = 0.1531703f; blurWeights[0].y = 0.1448929f; blurWeights[0].z = 0.1226492f; blurWeights[0].w = 0.0929025f; blurWeights[1].x = 0.06297021f; cmd.SetComputeTextureParam(shadowBlurMomentsCS, generateAndBlurMomentsKernel, HDShaderIDs._DepthTexture, atlasRenderTexture); cmd.SetComputeVectorArrayParam(shadowBlurMomentsCS, HDShaderIDs._BlurWeightsStorage, blurWeights); // We need to store in which of the two moment texture a request will have its last version stored in for a final patch up at the end. int[] finalAtlasTexture = new int[parameters.shadowRequests.Count]; int requestIdx = 0; foreach (var shadowRequest in parameters.shadowRequests) { using (new ProfilingSample(cmd, "EVSM conversion and blur", CustomSamplerId.RenderShadowMaps.GetSampler())) { int downsampledWidth = Mathf.CeilToInt(shadowRequest.atlasViewport.width * 0.5f); int downsampledHeight = Mathf.CeilToInt(shadowRequest.atlasViewport.height * 0.5f); Vector2 DstRectOffset = new Vector2(shadowRequest.atlasViewport.min.x * 0.5f, shadowRequest.atlasViewport.min.y * 0.5f); cmd.SetComputeTextureParam(shadowBlurMomentsCS, generateAndBlurMomentsKernel, HDShaderIDs._OutputTexture, momentAtlasRenderTextures[0]); cmd.SetComputeVectorParam(shadowBlurMomentsCS, HDShaderIDs._SrcRect, new Vector4(shadowRequest.atlasViewport.min.x, shadowRequest.atlasViewport.min.y, shadowRequest.atlasViewport.width, shadowRequest.atlasViewport.height)); cmd.SetComputeVectorParam(shadowBlurMomentsCS, HDShaderIDs._DstRect, new Vector4(DstRectOffset.x, DstRectOffset.y, 1.0f / atlasRenderTexture.rt.width, 1.0f / atlasRenderTexture.rt.height)); cmd.SetComputeFloatParam(shadowBlurMomentsCS, HDShaderIDs._EVSMExponent, shadowRequest.evsmParams.x); int dispatchSizeX = ((int)downsampledWidth + 7) / 8; int dispatchSizeY = ((int)downsampledHeight + 7) / 8; cmd.DispatchCompute(shadowBlurMomentsCS, generateAndBlurMomentsKernel, dispatchSizeX, dispatchSizeY, 1); int currentAtlasMomentSurface = 0; RTHandle GetMomentRT() { return(momentAtlasRenderTextures[currentAtlasMomentSurface]); } RTHandle GetMomentRTCopy() { return(momentAtlasRenderTextures[(currentAtlasMomentSurface + 1) & 1]); } cmd.SetComputeVectorParam(shadowBlurMomentsCS, HDShaderIDs._SrcRect, new Vector4(DstRectOffset.x, DstRectOffset.y, downsampledWidth, downsampledHeight)); for (int i = 0; i < shadowRequest.evsmParams.w; ++i) { currentAtlasMomentSurface = (currentAtlasMomentSurface + 1) & 1; cmd.SetComputeTextureParam(shadowBlurMomentsCS, blurMomentsKernel, HDShaderIDs._InputTexture, GetMomentRTCopy()); cmd.SetComputeTextureParam(shadowBlurMomentsCS, blurMomentsKernel, HDShaderIDs._OutputTexture, GetMomentRT()); cmd.DispatchCompute(shadowBlurMomentsCS, blurMomentsKernel, dispatchSizeX, dispatchSizeY, 1); } finalAtlasTexture[requestIdx++] = currentAtlasMomentSurface; } } // We patch up the atlas with the requests that, due to different count of blur passes, remained in the copy for (int i = 0; i < parameters.shadowRequests.Count; ++i) { if (finalAtlasTexture[i] != 0) { using (new ProfilingSample(cmd, "Copy into main atlas.", CustomSamplerId.RenderShadowMaps.GetSampler())) { var shadowRequest = parameters.shadowRequests[i]; int downsampledWidth = Mathf.CeilToInt(shadowRequest.atlasViewport.width * 0.5f); int downsampledHeight = Mathf.CeilToInt(shadowRequest.atlasViewport.height * 0.5f); cmd.SetComputeVectorParam(shadowBlurMomentsCS, HDShaderIDs._SrcRect, new Vector4(shadowRequest.atlasViewport.min.x * 0.5f, shadowRequest.atlasViewport.min.y * 0.5f, downsampledWidth, downsampledHeight)); cmd.SetComputeTextureParam(shadowBlurMomentsCS, copyMomentsKernel, HDShaderIDs._InputTexture, momentAtlasRenderTextures[1]); cmd.SetComputeTextureParam(shadowBlurMomentsCS, copyMomentsKernel, HDShaderIDs._OutputTexture, momentAtlasRenderTextures[0]); int dispatchSizeX = ((int)downsampledWidth + 7) / 8; int dispatchSizeY = ((int)downsampledHeight + 7) / 8; cmd.DispatchCompute(shadowBlurMomentsCS, copyMomentsKernel, dispatchSizeX, dispatchSizeY, 1); } } } } }