private void AddMaterialToArrayAndDictionary(ScatteringParameters scatteringParameters, uint materialArrayIndex) { // "SetScatteringWidth()" throws an exception if the range is exceeded. // TODO: How to handle this? Don't throw at all? SubsurfaceScatteringBlurEffect.SetScatteringWidth(materialArrayIndex, scatteringParameters.ScatteringWidth); // Add the scattering width to the scattering width array. if (scatteringParameters.ScatteringKernel != null) { // TODO: STABILITY: What to do if the scattering width is present but no kernel? The post-process wouldn't be able to handle that correctly. // Maybe just save a dummy kernel? SubsurfaceScatteringBlurEffect.SetScatteringKernel(materialArrayIndex, scatteringParameters.ScatteringKernel); } scatteringParametersToArrayIndexDictionary[scatteringParameters] = materialArrayIndex; // Add the material to the dictionary and save its associated index in the scattering width array. }
/// <inheritdoc/> public override void Prepare(RenderDrawContext context) { if (DeduplicateMaterialParameters) { scatteringParametersToArrayIndexDictionary.Clear(); } // TODO: Generate a material array per view? This could in some cases limit the number of materials present in the array. // In case every object is visible from every view, it wouldn't save us anything though. uint materialArrayIndexCounter = 1; // We start at index 1 instead of 0 because we use index 0 to flag (and discard) non-scattering materials. // TODO: Not sure if the following line is even necessary, because we don't use any material parameters of material 0 anyway. I mean not even the kernel of material 0 is set. So either set both or none. SubsurfaceScatteringBlurEffect.SetScatteringWidth(0, 0.0f); // This element is unused because a material index of 0 means the material is not a subsurface scattering material and therefore will be skipped by the post-process. // Generate the material dictionary that contains only scattering materials: //Dispatcher.ForEach(((RootEffectRenderFeature)RootRenderFeature).RenderNodes, (ref RenderNode renderNode) => // TODO: PERFORMANCE: Use this instead? foreach (RenderNode renderNode in ((RootEffectRenderFeature)RootRenderFeature).RenderNodes) { var perDrawLayout = renderNode.RenderEffect?.Reflection?.PerDrawLayout; if (perDrawLayout == null) { continue; } var renderMesh = (RenderMesh)renderNode.RenderObject; uint materialArrayIndex = 0; // If the mesh doesn't have a scattering kernel we write index 0 into the constant buffer. if (HasScatteringKernel(renderMesh.MaterialPass)) { materialArrayIndex = AddMaterialToDictionaryAndGetArrayIndex(renderMesh, ref materialArrayIndexCounter); } WriteMaterialIndexIntoRenderNodeConstantBuffer(perDrawLayout, renderNode, materialArrayIndex); } bool scatteringMaterialsAreVisible = (materialArrayIndexCounter > 1); SubsurfaceScatteringBlurEffect.Enabled = scatteringMaterialsAreVisible; // Disable the post-process if no scattering objects are visible (to save performance). }
protected virtual void DrawView(RenderContext context, RenderDrawContext drawContext, int eyeIndex, int eyeCount) { var renderSystem = context.RenderSystem; PrepareVRConstantBuffer(context, eyeIndex, eyeCount); // Z Prepass var lightProbes = LightProbes && GBufferRenderStage != null; if (lightProbes) { // Note: Baking lightprobe before GBuffer prepass because we are updating some cbuffer parameters needed by Opaque pass that GBuffer pass might upload early PrepareLightprobeConstantBuffer(context); // TODO: Temporarily using ShadowMap shader using (drawContext.QueryManager.BeginProfile(Color.Green, CompositingProfilingKeys.GBuffer)) using (drawContext.PushRenderTargetsAndRestore()) { drawContext.CommandList.Clear(drawContext.CommandList.DepthStencilBuffer, DepthStencilClearOptions.DepthBuffer); drawContext.CommandList.SetRenderTarget(drawContext.CommandList.DepthStencilBuffer, null); // Draw [main view | z-prepass stage] renderSystem.Draw(drawContext, context.RenderView, GBufferRenderStage); } // Bake lightprobes against Z-buffer BakeLightProbes(context, drawContext); } using (drawContext.PushRenderTargetsAndRestore()) { // Draw [main view | main stage] if (OpaqueRenderStage != null) { using (drawContext.QueryManager.BeginProfile(Color.Green, CompositingProfilingKeys.Opaque)) { renderSystem.Draw(drawContext, context.RenderView, OpaqueRenderStage); } } Texture depthStencilSRV = null; // Draw [main view | subsurface scattering post process] if (SubsurfaceScatteringBlurEffect != null) { var materialIndex = OpaqueRenderStage?.OutputValidator.Find <MaterialIndexTargetSemantic>() ?? -1; if (materialIndex != -1) { using (drawContext.PushRenderTargetsAndRestore()) { depthStencilSRV = ResolveDepthAsSRV(drawContext); var renderTarget = drawContext.CommandList.RenderTargets[0]; var materialIndexRenderTarget = drawContext.CommandList.RenderTargets[materialIndex]; SubsurfaceScatteringBlurEffect.Draw(drawContext, renderTarget, materialIndexRenderTarget, depthStencilSRV, renderTarget); } } } // Draw [main view | transparent stage] if (TransparentRenderStage != null) { // Some transparent shaders will require the depth as a shader resource - resolve it only once and set it here using (drawContext.QueryManager.BeginProfile(Color.Green, CompositingProfilingKeys.Transparent)) using (drawContext.PushRenderTargetsAndRestore()) { if (depthStencilSRV == null) { depthStencilSRV = ResolveDepthAsSRV(drawContext); } renderSystem.Draw(drawContext, context.RenderView, TransparentRenderStage); } } var colorTargetIndex = OpaqueRenderStage?.OutputValidator.Find(typeof(ColorTargetSemantic)) ?? -1; if (colorTargetIndex == -1) { return; } // Resolve MSAA targets var renderTargets = currentRenderTargets; var depthStencil = currentDepthStencil; if (actualMultisampleCount != MultisampleCount.None) { using (drawContext.QueryManager.BeginProfile(Color.Green, CompositingProfilingKeys.MsaaResolve)) { ResolveMSAA(drawContext); } renderTargets = currentRenderTargetsNonMSAA; depthStencil = currentDepthStencilNonMSAA; } // Shafts if we have them if (LightShafts != null) { using (drawContext.QueryManager.BeginProfile(Color.Green, CompositingProfilingKeys.LightShafts)) { LightShafts.Draw(drawContext, depthStencil, renderTargets[colorTargetIndex]); } } if (PostEffects != null) { // Run post effects // Note: OpaqueRenderStage can't be null otherwise colorTargetIndex would be -1 PostEffects.Draw(drawContext, OpaqueRenderStage.OutputValidator, renderTargets.Items, depthStencil, ViewOutputTarget); } else { if (actualMultisampleCount != MultisampleCount.None) { using (drawContext.QueryManager.BeginProfile(Color.Green, CompositingProfilingKeys.MsaaResolve)) { drawContext.CommandList.Copy(renderTargets[colorTargetIndex], ViewOutputTarget); } } } // Free the depth texture since we won't need it anymore if (depthStencilSRV != null) { drawContext.Resolver.ReleaseDepthStenctilAsShaderResource(depthStencilSRV); } } }