// Naive approach: 4 passes. (Reference version) private void DrawCoreNaive(RenderDrawContext context) { var originalTexture = GetSafeInput(0); var outputTexture = GetSafeOutput(0); var tapNumber = 2 * tapCount - 1; directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurKeys.Count, tapCount); directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurKeys.TotalTap, tapNumber); directionalBlurEffect.EffectInstance.UpdateEffect(context.GraphicsDevice); directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Radius, Radius); directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.TapWeights, tapWeights); // Blur in one direction var blurAngle = Phase; directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Direction, new Vector2((float)Math.Cos(blurAngle), (float)Math.Sin(blurAngle))); var firstBlurTexture = NewScopedRenderTarget2D(originalTexture.Description); directionalBlurEffect.SetInput(0, originalTexture); directionalBlurEffect.SetOutput(firstBlurTexture); directionalBlurEffect.Draw((RenderDrawContext)context, "McIntoshBokehPass1_tap{0}_radius{1}", tapNumber, (int)Radius); // Diagonal blur A blurAngle = MathUtil.Pi / 3f + Phase; directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Direction, new Vector2((float)Math.Cos(blurAngle), (float)Math.Sin(blurAngle))); var diagonalBlurA = NewScopedRenderTarget2D(originalTexture.Description); directionalBlurEffect.SetInput(0, firstBlurTexture); directionalBlurEffect.SetOutput(diagonalBlurA); directionalBlurEffect.Draw((RenderDrawContext)context, "McIntoshBokehPass2A_tap{0}_radius{1}", tapNumber, (int)Radius); // Diagonal blur B blurAngle = -MathUtil.Pi / 3f + Phase; directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Direction, new Vector2((float)Math.Cos(blurAngle), (float)Math.Sin(blurAngle))); var diagonalBlurB = NewScopedRenderTarget2D(originalTexture.Description); directionalBlurEffect.SetInput(0, firstBlurTexture); directionalBlurEffect.SetOutput(diagonalBlurB); directionalBlurEffect.Draw((RenderDrawContext)context, "McIntoshBokehPass2B_tap{0}_radius{1}", tapNumber, (int)Radius); // Final pass outputting the min of A and B finalCombineEffect.SetInput(0, diagonalBlurA); finalCombineEffect.SetInput(1, diagonalBlurB); finalCombineEffect.SetOutput(outputTexture); finalCombineEffect.Draw(context, name: "McIntoshBokehPassCombine"); }
public void Draw(RenderDrawContext context, float sigmaRatio, Texture inputTexture, Texture outputTexture) { // Check if we need to regenerate offsetsWeights if (offsetsWeights == null || this.sigmaRatio != sigmaRatio) { offsetsWeights = GaussianUtil.Calculate1D(Radius, sigmaRatio); // Update parameters blurH.Parameters.Set(GaussianBlurShaderKeys.OffsetsWeights, offsetsWeights); blurV.Parameters.Set(GaussianBlurShaderKeys.OffsetsWeights, offsetsWeights); this.sigmaRatio = sigmaRatio; } // Get a temporary texture for the intermediate pass // This texture will be allocated only in the scope of this draw and returned to the pool at the exit of this method var desc = inputTexture.Description; desc.MultisampleCount = MultisampleCount.None; // TODO we should have a method to get a non-multisampled RT var outputTextureH = gaussianBlur.NewScopedRenderTarget2D(desc); // Horizontal pass blurH.SetInput(inputTexture); blurH.SetOutput(outputTextureH); blurH.Draw(context, nameGaussianBlurH); // Vertical pass blurV.SetInput(outputTextureH); blurV.SetOutput(outputTexture); blurV.Draw(context, nameGaussianBlurV); }
protected override void DrawCore(RenderDrawContext context) { // Update the weight array if necessary if (weightsDirty || tapCount == 0) { weightsDirty = false; Vector2[] gaussianWeights = GaussianUtil.Calculate1D((int)Radius, 2f, true); tapCount = gaussianWeights.Length; tapWeights = new float[tapCount]; for (int i = 0; i < tapCount; i++) { tapWeights[i] = gaussianWeights[i].Y; } } var originalTexture = GetSafeInput(0); var outputTexture = GetSafeOutput(0); var tapNumber = 2 * tapCount - 1; directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurKeys.Count, tapCount); directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurKeys.TotalTap, tapNumber); directionalBlurEffect.EffectInstance.UpdateEffect(context.GraphicsDevice); directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Radius, Radius); directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.TapWeights, tapWeights); // Blur in one direction var blurAngle = 0f; directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Direction, new Vector2((float)Math.Cos(blurAngle), (float)Math.Sin(blurAngle))); var firstBlurTexture = NewScopedRenderTarget2D(originalTexture.Description); directionalBlurEffect.SetInput(0, originalTexture); directionalBlurEffect.SetOutput(firstBlurTexture); directionalBlurEffect.Draw(context, "GaussianBokehPass1_tap{0}_radius{1}", tapNumber, (int)Radius); // Second blur pass to ouput the final result blurAngle = MathUtil.PiOverTwo; directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Direction, new Vector2((float)Math.Cos(blurAngle), (float)Math.Sin(blurAngle))); directionalBlurEffect.SetInput(0, firstBlurTexture); directionalBlurEffect.SetOutput(outputTexture); directionalBlurEffect.Draw(context, "GaussianBokehPass2_tap{0}_radius{1}", tapNumber, (int)Radius); }
protected override void DrawCore(RenderDrawContext context) { var input = GetInput(0); var output = GetOutput(0); if (FadeOutSpeed == 0f) { // Nothing to do if (input != output) { context.CommandList.Copy(input, output); } return; } if (input == output) { var newInput = NewScopedRenderTarget2D(input.Description); context.CommandList.Copy(input, newInput); input = newInput; } // Check we have a render target to hold the persistence over a few frames if (persistenceTexture == null || persistenceTexture.Description != output.Description) { // We need to re-allocate the texture if (persistenceTexture != null) { Context.Allocator.ReleaseReference(persistenceTexture); } persistenceTexture = Context.Allocator.GetTemporaryTexture2D(output.Description); // Initializes to black context.CommandList.Clear(persistenceTexture, Color.Black); } var accumulationPersistence = NewScopedRenderTarget2D(persistenceTexture.Description); // For persistence, we combine the current brightness with the one of the previous frames. bloomAfterimageShader.Parameters.Set(BloomAfterimageShaderKeys.FadeOutSpeed, FadeOutSpeed); bloomAfterimageShader.Parameters.Set(BloomAfterimageShaderKeys.Sensitivity, Sensitivity / 100f); bloomAfterimageShader.SetInput(0, input); bloomAfterimageShader.SetInput(1, persistenceTexture); bloomAfterimageShader.SetOutput(accumulationPersistence); bloomAfterimageShader.Draw(context, "Afterimage persistence accumulation"); // Keep the final brightness buffer for the following frames context.CommandList.Copy(accumulationPersistence, persistenceTexture); // Merge persistence and current bloom into the final result bloomAfterimageCombineShader.SetInput(0, input); bloomAfterimageCombineShader.SetInput(1, persistenceTexture); bloomAfterimageCombineShader.SetOutput(output); bloomAfterimageCombineShader.Draw(context, "Afterimage persistence combine"); }
protected override void DrawCore(RenderDrawContext context) { // Updates the weight array if necessary if (weightsDirty || tapCount == 0) { weightsDirty = false; Vector2[] gaussianWeights = GaussianUtil.Calculate1D((int)radius, 2f, true); tapCount = gaussianWeights.Length; tapWeights = gaussianWeights; } var originalTexture = GetSafeInput(0); var outputTexture = GetSafeOutput(0); cocBlurEffect.Parameters.Set(DepthAwareDirectionalBlurKeys.Count, tapCount); cocBlurEffect.EffectInstance.UpdateEffect(context.GraphicsDevice); cocBlurEffect.Parameters.Set(CoCMapBlurShaderKeys.Radius, radius); cocBlurEffect.Parameters.Set(CoCMapBlurShaderKeys.OffsetsWeights, tapWeights); var tapNumber = 2 * tapCount - 1; // Blur in one direction var blurAngle = 0f; cocBlurEffect.Parameters.Set(CoCMapBlurShaderKeys.Direction, new Vector2(MathF.Cos(blurAngle), MathF.Sin(blurAngle))); var firstBlurTexture = NewScopedRenderTarget2D(originalTexture.Description); cocBlurEffect.SetInput(0, originalTexture); cocBlurEffect.SetOutput(firstBlurTexture); cocBlurEffect.Draw(context, "CoCMapBlurPass1_tap{0}_radius{1}", tapNumber, (int)radius); // Second blur pass to ouput the final result blurAngle = MathUtil.PiOverTwo; cocBlurEffect.Parameters.Set(CoCMapBlurShaderKeys.Direction, new Vector2(MathF.Cos(blurAngle), MathF.Sin(blurAngle))); cocBlurEffect.SetInput(0, firstBlurTexture); cocBlurEffect.SetOutput(outputTexture); cocBlurEffect.Draw(context, "CoCMapBlurPass2_tap{0}_radius{1}", tapNumber, (int)radius); }
// Optimized approach: 2 passes. private void DrawCoreOptimized(RenderDrawContext context) { var originalTexture = GetSafeInput(0); var outputTexture = GetSafeOutput(0); var tapNumber = 2 * tapCount - 1; directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurKeys.Count, tapCount); directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurKeys.TotalTap, tapNumber); directionalBlurEffect.EffectInstance.UpdateEffect(context.GraphicsDevice); directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Radius, Radius); directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.TapWeights, tapWeights); // Blur in one direction var blurAngle = Phase; directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Direction, new Vector2((float)Math.Cos(blurAngle), (float)Math.Sin(blurAngle))); var firstBlurTexture = NewScopedRenderTarget2D(originalTexture.Description); directionalBlurEffect.SetInput(0, originalTexture); directionalBlurEffect.SetOutput(firstBlurTexture); directionalBlurEffect.Draw((RenderDrawContext)context, "McIntoshBokehPass1_tap{0}_radius{1}", tapNumber, (int)Radius); // Calculates the 2 diagonal blurs and keep the min of them var diagonalBlurAngleA = +MathUtil.Pi / 3f + Phase; var diagonalBlurAngleB = -MathUtil.Pi / 3f + Phase; optimizedEffect.SetInput(0, firstBlurTexture); optimizedEffect.SetOutput(outputTexture); optimizedEffect.Parameters.Set(DepthAwareDirectionalBlurKeys.Count, tapCount); optimizedEffect.Parameters.Set(DepthAwareDirectionalBlurKeys.TotalTap, tapNumber); optimizedEffect.EffectInstance.UpdateEffect(context.GraphicsDevice); optimizedEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Radius.ComposeWith("directionalBlurA"), Radius); optimizedEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Direction.ComposeWith("directionalBlurA"), new Vector2((float)Math.Cos(diagonalBlurAngleA), (float)Math.Sin(diagonalBlurAngleA))); optimizedEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.TapWeights.ComposeWith("directionalBlurA"), tapWeights); optimizedEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Radius.ComposeWith("directionalBlurB"), Radius); optimizedEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Direction.ComposeWith("directionalBlurB"), new Vector2((float)Math.Cos(diagonalBlurAngleB), (float)Math.Sin(diagonalBlurAngleB))); optimizedEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.TapWeights.ComposeWith("directionalBlurB"), tapWeights); optimizedEffect.Draw((RenderDrawContext)context, "McIntoshBokehPass2_BlurABCombine_tap{0}_radius{1}", tapNumber, (int)Radius); }
protected override void DrawCore(RenderDrawContext context) { Texture color = GetInput(0); Texture output = GetOutput(0); if (color is null || output is null || depthTexture is null) { return; } fogFilter.Parameters.Set(FogEffectKeys.FogColor, Color); fogFilter.Parameters.Set(FogEffectKeys.Density, Density); fogFilter.Parameters.Set(FogEffectKeys.DepthTexture, depthTexture); fogFilter.Parameters.Set(FogEffectKeys.zFar, zMax); fogFilter.Parameters.Set(FogEffectKeys.zNear, zMin); fogFilter.Parameters.Set(FogEffectKeys.FogStart, FogStart); fogFilter.Parameters.Set(FogEffectKeys.skipBackground, SkipBackground); fogFilter.SetInput(0, color); fogFilter.SetOutput(output); fogFilter.Draw(context); }
protected override void DrawCore(RenderDrawContext context) { Texture color = GetInput(0); Texture output = GetOutput(0); if (color is null || output is null || depthTexture is null) { return; } outlineFilter.Parameters.Set(OutlineEffectKeys.ScreenDiffs, new Vector2(0.5f / color.Width, 0.5f / color.Height)); outlineFilter.Parameters.Set(OutlineEffectKeys.DepthTexture, depthTexture); outlineFilter.Parameters.Set(OutlineEffectKeys.zFar, zMax); outlineFilter.Parameters.Set(OutlineEffectKeys.zNear, zMin); outlineFilter.Parameters.Set(OutlineEffectKeys.NormalWeight, NormalWeight); outlineFilter.Parameters.Set(OutlineEffectKeys.DepthWeight, DepthWeight); outlineFilter.Parameters.Set(OutlineEffectKeys.NormalNearCutoff, NormalNearCutoff); outlineFilter.SetInput(0, color); outlineFilter.SetOutput(output); outlineFilter.Draw(context); }
protected override void DrawCore(RenderDrawContext context) { var originalColorBuffer = GetSafeInput(0); var originalDepthBuffer = GetSafeInput(1); var outputTexture = GetSafeOutput(0); if (configurationDirty) { SetupTechnique(); } // Preparation phase: create different downscaled versions of the original image, later needed by the bokeh blur shaders. // TODO use ImageMultiScaler instead? downscaledSources.Clear(); // First we linearize the depth and compute the CoC map based on the user lens configuration. // Render target will contain "CoC"(16 bits) "Linear depth"(16bits). var cocLinearDepthTexture = GetScopedRenderTarget(originalColorBuffer.Description, 1f, PixelFormat.R16G16_Float); var renderView = context.RenderContext.RenderView; var farPlane = renderView.FarClipPlane; var depthAreas = DOFAreas; if (AutoFocus) { // TODO replace this by physical camera parameters (aperture, focus distance...) var diffToTarget = (autoFocusDistanceTarget - autoFocusDistanceCurrent); var maxAmplitude = farPlane * 0.2f; diffToTarget = MathUtil.Clamp(diffToTarget, -maxAmplitude, maxAmplitude); autoFocusDistanceCurrent = autoFocusDistanceCurrent + 0.1f * diffToTarget; if (autoFocusDistanceCurrent < renderView.NearClipPlane * 2.0f) { autoFocusDistanceCurrent = renderView.NearClipPlane * 2.0f; } depthAreas = new Vector4(renderView.NearClipPlane, autoFocusDistanceCurrent, autoFocusDistanceCurrent, autoFocusDistanceCurrent + farPlane * 0.5f); } coclinearDepthMapEffect.SetInput(0, originalDepthBuffer); coclinearDepthMapEffect.SetOutput(cocLinearDepthTexture); coclinearDepthMapEffect.Parameters.Set(CircleOfConfusionKeys.depthAreas, ref depthAreas); coclinearDepthMapEffect.Parameters.Set(CameraKeys.ZProjection, CameraKeys.ZProjectionACalculate(renderView.NearClipPlane, renderView.FarClipPlane)); coclinearDepthMapEffect.Draw(context, "CoC_LinearDepth"); if (AutoFocus) { // Reads the center depth of the previous frame and use it as a new target // TODO single pixel is really small, average some disk area instead? pointDepthShader.Parameters.Set(PointDepthKeys.Coordinate, new Vector2(0.5f, 0.5f)); pointDepthShader.SetInput(cocLinearDepthTexture); pointDepthShader.SetOutput(depthCenter1x1); pointDepthShader.Draw(context, "Center Depth"); depthReadBack.SetInput(depthCenter1x1); depthReadBack.Draw(context, "Center_Depth_Readback"); var centerDepth = depthReadBack.Result[0]; autoFocusDistanceTarget = centerDepth; } // Find the smallest downscale we should go down to. var maxDownscale = 0; foreach (var cocLevel in cocLevels) { if (cocLevel.DownscaleFactor > maxDownscale) { maxDownscale = cocLevel.DownscaleFactor; } } // Create a series of downscale, with anti-bleeding treatment for (int i = 0; i <= maxDownscale; i++) { var downSizedTexture = originalColorBuffer; if (i > 0) { downSizedTexture = GetScopedRenderTarget(originalColorBuffer.Description, 1f / (float)Math.Pow(2f, i), originalColorBuffer.Description.Format); textureScaler.SetInput(0, downscaledSources[i - 1]); textureScaler.SetOutput(downSizedTexture); textureScaler.Draw(context, "DownScale_Factor{0}", i); } downscaledSources[i] = downSizedTexture; } // We create a blurred version of the CoC map. // This is useful to avoid silhouettes appearing when the CoC changes abruptly. var blurredCoCTexture = NewScopedRenderTarget2D(cocLinearDepthTexture.Description); cocMapBlur.Radius = 6f / 720f * cocLinearDepthTexture.Description.Height; // 6 pixels at 720p cocMapBlur.SetInput(0, cocLinearDepthTexture); cocMapBlur.SetOutput(blurredCoCTexture); cocMapBlur.Draw(context, "CoC_BlurredMap"); // Creates all the levels with different CoC strengths. // (Skips level with CoC 0 which is always the original buffer.) combineLevelsEffect.Parameters.Set(CombineLevelsFromCoCKeys.LevelCount, cocLevels.Count); combineLevelsEffect.SetInput(0, cocLinearDepthTexture); combineLevelsEffect.SetInput(1, blurredCoCTexture); combineLevelsEffect.SetInput(2, originalColorBuffer); combineLevelsFrontEffect.Parameters.Set(CombineLevelsFromCoCKeys.LevelCount, cocLevels.Count); combineLevelsFrontEffect.SetInput(0, cocLinearDepthTexture); combineLevelsFrontEffect.SetInput(1, blurredCoCTexture); combineLevelsFrontEffect.SetInput(2, originalColorBuffer); float previousCoC = 0f; for (int i = 1; i < cocLevels.Count; i++) { // We render a blurred version of the original scene into a downscaled render target. // Blur strength depends on the current level CoC value. var levelConfig = cocLevels[i]; var textureToBlur = downscaledSources[levelConfig.DownscaleFactor]; float downscaleFactor = 1f / (float)(Math.Pow(2f, levelConfig.DownscaleFactor)); var blurOutput = GetScopedRenderTarget(originalColorBuffer.Description, downscaleFactor, originalColorBuffer.Description.Format); var blurOutputFront = NewScopedRenderTarget2D(blurOutput.Description); float blurRadius = (MaxBokehSize * BokehSizeFactor) * levelConfig.CoCValue * downscaleFactor * originalColorBuffer.Width; if (blurRadius < 1f) { blurRadius = 1f; } //--------------------------------- // Far out-of-focus //--------------------------------- // Pre-process the layer for the current CoC // This removes areas which might wrongly bleed into our image when blurring. var alphaTextureToBlur = NewScopedRenderTarget2D(textureToBlur.Description); thresholdAlphaCoC.Parameters.Set(ThresholdAlphaCoCKeys.CoCReference, previousCoC); thresholdAlphaCoC.Parameters.Set(ThresholdAlphaCoCKeys.CoCCurrent, levelConfig.CoCValue); thresholdAlphaCoC.SetInput(0, textureToBlur); thresholdAlphaCoC.SetInput(1, cocLinearDepthTexture); thresholdAlphaCoC.SetOutput(alphaTextureToBlur); thresholdAlphaCoC.Draw(context, "Alphaize_Far_{0}", i); textureToBlur = alphaTextureToBlur; // TODO Quality up: make the opaque areas "bleed" into the areas we just made transparent // Apply the bokeh blur effect BokehBlur levelBlur = levelConfig.BlurEffect; levelBlur.CoCStrength = levelConfig.CoCValue; levelBlur.Radius = blurRadius; // This doesn't generate garbage if the radius value doesn't change. levelBlur.SetInput(0, textureToBlur); levelBlur.SetOutput(blurOutput); levelBlur.Draw(context, "CoC_LoD_Layer_Far_{0}", i); combineLevelsEffect.SetInput(i + 2, blurOutput); //--------------------------------- // Near out-of-focus //--------------------------------- // Negates CoC values and makes background objects transparent thresholdAlphaCoCFront.Parameters.Set(ThresholdAlphaCoCFrontKeys.CoCReference, previousCoC); thresholdAlphaCoCFront.Parameters.Set(ThresholdAlphaCoCFrontKeys.CoCCurrent, levelConfig.CoCValue); thresholdAlphaCoCFront.SetInput(0, downscaledSources[levelConfig.DownscaleFactor]); thresholdAlphaCoCFront.SetInput(1, cocLinearDepthTexture); thresholdAlphaCoCFront.SetOutput(alphaTextureToBlur); thresholdAlphaCoCFront.Draw(context, "Alphaize_Near_{0}", i); textureToBlur = alphaTextureToBlur; // Apply the bokeh blur effect levelBlur.SetInput(0, textureToBlur); levelBlur.SetOutput(blurOutputFront); levelBlur.Draw(context, "CoC_LoD_Layer_Near_{0}", i); combineLevelsFrontEffect.SetInput(i + 2, blurOutputFront); previousCoC = levelConfig.CoCValue; } // Far out-of-focus: each pixel, depending on its CoC, interpolates its color from // the original color buffer and blurred buffer(s). combineLevelsEffect.Parameters.Set(CombineLevelsFromCoCShaderKeys.CoCLevelValues, combineShaderCocLevelValues); combineLevelsEffect.SetOutput(outputTexture); combineLevelsEffect.Draw(context, "CoCLevelCombineInterpolation"); // Finally add front out-of-focus objects on the top of the scene // TODO Quality up: instead of merging all the layers for each pixel, merge only // the relevant layer(s) closest to the pixel CoC. combineLevelsFrontEffect.SetOutput(outputTexture); combineLevelsFrontEffect.Draw(context, "CoCLevelCombineInterpolationFront"); // Release any reference downscaledSources.Clear(); }
protected override void DrawCore(RenderDrawContext context) { // Inputs: Texture colorBuffer = GetSafeInput(0); Texture depthBuffer = GetSafeInput(1); Texture normalsBuffer = GetSafeInput(2); Texture specularRoughnessBuffer = GetSafeInput(3); // Output: Texture outputBuffer = GetSafeOutput(0); // Prepare var temporalCache = Prepare(context, outputBuffer); FlushCache(context.RenderContext.Time.FrameCount); // Get temporary buffers var rayTraceBuffersSize = GetBufferResolution(outputBuffer, RayTracePassResolution); var resolveBuffersSize = GetBufferResolution(outputBuffer, ResolvePassResolution); Texture rayTraceBuffer = NewScopedRenderTarget2D(rayTraceBuffersSize.Width, rayTraceBuffersSize.Height, RayTraceTargetFormat, 1); Texture resolveBuffer = NewScopedRenderTarget2D(resolveBuffersSize.Width, resolveBuffersSize.Height, ReflectionsFormat, 1); // Check if resize depth Texture smallerDepthBuffer = depthBuffer; if (DepthResolution != ResolutionMode.Full) { // Smaller depth buffer improves ray tracing performance. var depthBuffersSize = GetBufferResolution(depthBuffer, DepthResolution); smallerDepthBuffer = NewScopedRenderTarget2D(depthBuffersSize.Width, depthBuffersSize.Height, PixelFormat.R32_Float, 1); depthPassShader.SetInput(0, depthBuffer); depthPassShader.SetOutput(smallerDepthBuffer); depthPassShader.Draw(context, "Downscale Depth"); } // Blur Pass Texture blurPassBuffer; if (UseColorBufferMips) { // Note: using color buffer mips maps helps with reducing artifacts // and improves resolve pass performance (faster color texture lookups, less cache misses) // Also for high surface roughness values it adds more blur to the reflection tail which looks more realistic. // Get temp targets var colorBuffersSize = new Size2(outputBuffer.Width / 2, outputBuffer.Height / 2); int colorBuffersMips = Texture.CalculateMipMapCount(MipMapCount.Auto, colorBuffersSize.Width, colorBuffersSize.Height); Texture colorBuffer0 = NewScopedRenderTarget2D(colorBuffersSize.Width, colorBuffersSize.Height, ReflectionsFormat, colorBuffersMips); Texture colorBuffer1 = NewScopedRenderTarget2D(colorBuffersSize.Width / 2, colorBuffersSize.Height / 2, ReflectionsFormat, colorBuffersMips - 1); int colorBuffer1MipOffset = 1; // For colorBuffer1 we could use one mip less (optimized) // Cache per color buffer mip views int colorBuffer0Mips = colorBuffer0.MipLevels; if (cachedColorBuffer0Mips == null || cachedColorBuffer0Mips.Length != colorBuffer0Mips || cachedColorBuffer0Mips[0].ParentTexture != colorBuffer0) { cachedColorBuffer0Mips?.ForEach(view => view?.Dispose()); cachedColorBuffer0Mips = new Texture[colorBuffer0Mips]; for (int mipIndex = 0; mipIndex < colorBuffer0Mips; mipIndex++) { cachedColorBuffer0Mips[mipIndex] = colorBuffer0.ToTextureView(ViewType.Single, 0, mipIndex); } } int colorBuffer1Mips = colorBuffer1.MipLevels; if (cachedColorBuffer1Mips == null || cachedColorBuffer1Mips.Length != colorBuffer1Mips || cachedColorBuffer1Mips[0].ParentTexture != colorBuffer1) { cachedColorBuffer1Mips?.ForEach(view => view?.Dispose()); cachedColorBuffer1Mips = new Texture[colorBuffer1Mips]; for (int mipIndex = 0; mipIndex < colorBuffer1Mips; mipIndex++) { cachedColorBuffer1Mips[mipIndex] = colorBuffer1.ToTextureView(ViewType.Single, 0, mipIndex); } } // Clone scene frame to mip 0 of colorBuffer0 Scaler.SetInput(0, colorBuffer); Scaler.SetOutput(cachedColorBuffer0Mips[0]); Scaler.Draw(context, "Copy frame"); // Downscale with gaussian blur for (int mipLevel = 1; mipLevel < colorBuffersMips; mipLevel++) { // Blur H var srcMip = cachedColorBuffer0Mips[mipLevel - 1]; var dstMip = cachedColorBuffer1Mips[mipLevel - colorBuffer1MipOffset]; blurPassShaderH.SetInput(0, srcMip); blurPassShaderH.SetOutput(dstMip); blurPassShaderH.Draw(context, "Blur H"); // Blur V srcMip = dstMip; dstMip = cachedColorBuffer0Mips[mipLevel]; blurPassShaderV.SetInput(0, srcMip); blurPassShaderV.SetOutput(dstMip); blurPassShaderV.Draw(context, "Blur V"); } blurPassBuffer = colorBuffer0; } else { // Don't use color buffer with mip maps blurPassBuffer = colorBuffer; cachedColorBuffer0Mips?.ForEach(view => view?.Dispose()); cachedColorBuffer1Mips?.ForEach(view => view?.Dispose()); } // Ray Trace Pass rayTracePassShader.SetInput(0, colorBuffer); rayTracePassShader.SetInput(1, smallerDepthBuffer); rayTracePassShader.SetInput(2, normalsBuffer); rayTracePassShader.SetInput(3, specularRoughnessBuffer); rayTracePassShader.SetOutput(rayTraceBuffer); rayTracePassShader.Draw(context, "Ray Trace"); // Resolve Pass resolvePassShader.SetInput(0, blurPassBuffer); resolvePassShader.SetInput(1, ResolvePassResolution == ResolutionMode.Full ? depthBuffer : smallerDepthBuffer); resolvePassShader.SetInput(2, normalsBuffer); resolvePassShader.SetInput(3, specularRoughnessBuffer); resolvePassShader.SetInput(4, rayTraceBuffer); resolvePassShader.SetOutput(resolveBuffer); resolvePassShader.Draw(context, "Resolve"); // Temporal Pass Texture reflectionsBuffer = resolveBuffer; if (TemporalEffect) { var temporalSize = outputBuffer.Size; temporalCache.Resize(GraphicsDevice, ref temporalSize); Texture temporalBuffer0 = NewScopedRenderTarget2D(temporalSize.Width, temporalSize.Height, ReflectionsFormat, 1); temporalPassShader.SetInput(0, resolveBuffer); temporalPassShader.SetInput(1, temporalCache.TemporalBuffer); temporalPassShader.SetInput(2, depthBuffer); temporalPassShader.SetOutput(temporalBuffer0); temporalPassShader.Draw(context, "Temporal"); context.CommandList.Copy(temporalBuffer0, temporalCache.TemporalBuffer); // TODO: use Texture.Swap from ContentStreaming branch to make it faster! reflectionsBuffer = temporalCache.TemporalBuffer; } // Combine Pass combinePassShader.SetInput(0, colorBuffer); combinePassShader.SetInput(1, depthBuffer); combinePassShader.SetInput(2, normalsBuffer); combinePassShader.SetInput(3, specularRoughnessBuffer); combinePassShader.SetInput(4, reflectionsBuffer); combinePassShader.SetOutput(outputBuffer); combinePassShader.Draw(context, "Combine"); #if SSLR_DEBUG if (DebugMode != DebugModes.None) { // Debug preview of temp targets switch (DebugMode) { case DebugModes.RayTrace: Scaler.SetInput(0, rayTraceBuffer); break; case DebugModes.Resolve: Scaler.SetInput(0, resolveBuffer); break; case DebugModes.Temporal: if (temporalCache != null) { Scaler.SetInput(0, temporalCache.TemporalBuffer); } break; } Scaler.SetOutput(outputBuffer); Scaler.Draw(context); } #endif }
// Naive approach: 6 passes protected void DrawCoreNaive(RenderDrawContext context) { var originalTexture = GetSafeInput(0); var outputTexture = GetSafeOutput(0); if (rhombiTapOffsetsDirty) { CalculateRhombiOffsets(); } var tapNumber = 2 * tapCount - 1; directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurKeys.Count, tapCount); directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurKeys.TotalTap, tapNumber); directionalBlurEffect.EffectInstance.UpdateEffect(context.GraphicsDevice); directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Radius, Radius); directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.TapWeights, tapWeights); directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.CoCReference, CoCStrength); // Vertical blur var blurAngle = MathUtil.PiOverTwo + Phase; directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Direction, new Vector2((float)Math.Cos(blurAngle), (float)Math.Sin(blurAngle))); var verticalBlurTexture = NewScopedRenderTarget2D(originalTexture.Description); directionalBlurEffect.SetInput(0, originalTexture); directionalBlurEffect.SetOutput(verticalBlurTexture); directionalBlurEffect.Draw(context, "TripleRhombiBokeh_RhombiABVertical_tap{0}_radius{1}", tapNumber, (int)Radius); // Rhombi A (top left) blurAngle = 7f * MathUtil.Pi / 6f + Phase; directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Direction, new Vector2((float)Math.Cos(blurAngle), (float)Math.Sin(blurAngle))); var rhombiA = NewScopedRenderTarget2D(originalTexture.Description); directionalBlurEffect.SetInput(0, verticalBlurTexture); directionalBlurEffect.SetOutput(rhombiA); directionalBlurEffect.Draw(context, "TripleRhombiBokeh_RhombiA_tap{0}_radius{1}", tapNumber, (int)Radius); // Rhombi B (top right) blurAngle = -MathUtil.Pi / 6f + Phase; directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Direction, new Vector2((float)Math.Cos(blurAngle), (float)Math.Sin(blurAngle))); var rhombiB = NewScopedRenderTarget2D(originalTexture.Description); directionalBlurEffect.SetInput(0, verticalBlurTexture); directionalBlurEffect.SetOutput(rhombiB); directionalBlurEffect.Draw(context, "TripleRhombiBokeh_RhombiB_tap{0}_radius{1}", tapNumber, (int)Radius); //Rhombi C (bottom) blurAngle = 7f * MathUtil.Pi / 6f + Phase; directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Direction, new Vector2((float)Math.Cos(blurAngle), (float)Math.Sin(blurAngle))); var rhombiCTmp = NewScopedRenderTarget2D(originalTexture.Description); directionalBlurEffect.SetInput(0, originalTexture); directionalBlurEffect.SetOutput(rhombiCTmp); directionalBlurEffect.Draw(context, "TripleRhombiBokeh_RhombiCTmp_tap{0}_radius{1}", tapNumber, (int)Radius); blurAngle = -MathUtil.Pi / 6f + Phase; directionalBlurEffect.Parameters.Set(DepthAwareDirectionalBlurUtilKeys.Direction, new Vector2((float)Math.Cos(blurAngle), (float)Math.Sin(blurAngle))); var rhombiC = NewScopedRenderTarget2D(originalTexture.Description); directionalBlurEffect.SetInput(0, rhombiCTmp); directionalBlurEffect.SetOutput(rhombiC); directionalBlurEffect.Draw(context, "TripleRhombiBokeh_RhombiC_tap{0}_radius{1}", tapNumber, (int)Radius); // Final pass outputting the average of the 3 blurs finalCombineEffect.SetInput(0, rhombiA); finalCombineEffect.SetInput(1, rhombiB); finalCombineEffect.SetInput(2, rhombiC); finalCombineEffect.SetOutput(outputTexture); finalCombineEffect.Parameters.Set(TripleRhombiCombineShaderKeys.RhombiTapOffsets, rhombiTapOffsets); finalCombineEffect.Draw(context, name: "TripleRhombiBokehCombine"); }
protected override void DrawCore(RenderDrawContext context) { var input = GetInput(0); var output = GetOutput(0); if (input is null || output is null) { return; } var inputDepthTexture = GetInput(1); // Depth // Update the parameters for this post effect if (!Enabled) { if (input != output) { Scaler.SetInput(input); Scaler.SetOutput(output); Scaler.Draw(context); } return; } // If input == output, copy the input to a temporary texture if (input == output) { var newInput = NewScopedRenderTarget2D(input.Width, input.Height, input.Format); context.CommandList.Copy(input, newInput); input = newInput; } var currentInput = input; // Draw outline before AA if (Outline.Enabled && inputDepthTexture != null) { // Outline var outlineOutput = NewScopedRenderTarget2D(input.Width, input.Height, input.Format); Outline.SetColorDepthInput(currentInput, inputDepthTexture, context.RenderContext.RenderView.NearClipPlane, context.RenderContext.RenderView.FarClipPlane); Outline.SetOutput(outlineOutput); Outline.Draw(context); currentInput = outlineOutput; } var fxaa = Antialiasing as FXAAEffect; bool aaFirst = Bloom != null && Bloom.StableConvolution; bool needAA = Antialiasing != null && Antialiasing.Enabled; // Do AA here, first. (hybrid method from Karis2013) if (aaFirst && needAA) { // do AA: if (fxaa != null) { fxaa.InputLuminanceInAlpha = true; } var bufferIndex = 1; if (Antialiasing.RequiresDepthBuffer) { Antialiasing.SetInput(bufferIndex++, inputDepthTexture); } bool requiresVelocityBuffer = Antialiasing.RequiresVelocityBuffer; if (requiresVelocityBuffer) { Antialiasing.SetInput(bufferIndex++, GetInput(6)); } var aaSurface = NewScopedRenderTarget2D(input.Width, input.Height, input.Format); if (Antialiasing.NeedRangeDecompress) { // Explanation: // The Karis method (Unreal Engine 4.1x), uses a hybrid pipeline to execute AA. // The AA is usually done at the end of the pipeline, but we don't benefit from // AA for the posteffects, which is a shame. // The Karis method, executes AA at the beginning, but for AA to be correct, it // must work post tonemapping, and even more in fact, in gamma space too. Plus, // it waits for the alpha=luma to be a "perceptive luma" so also gamma space. // In our case, working in gamma space created monstruous outlining artefacts // around exageratedly strong constrasted objects (way in HDR range). // So AA works in linear space, but still with gamma luma, as a light tradeoff // to supress artefacts. // Create a 16 bits target for FXAA: // Render range compression & perceptual luma to alpha channel: rangeCompress.SetInput(currentInput); rangeCompress.SetOutput(aaSurface); rangeCompress.Draw(context); Antialiasing.SetInput(0, aaSurface); Antialiasing.SetOutput(currentInput); Antialiasing.Draw(context); // Reverse tone LDR to HDR: rangeDecompress.SetInput(currentInput); rangeDecompress.SetOutput(aaSurface); rangeDecompress.Draw(context); } else { Antialiasing.SetInput(0, currentInput); Antialiasing.SetOutput(aaSurface); Antialiasing.Draw(context); } currentInput = aaSurface; } if (AmbientOcclusion.Enabled && inputDepthTexture != null) { // Ambient Occlusion var aoOutput = NewScopedRenderTarget2D(input.Width, input.Height, input.Format); AmbientOcclusion.SetColorDepthInput(currentInput, inputDepthTexture); AmbientOcclusion.SetOutput(aoOutput); AmbientOcclusion.Draw(context); currentInput = aoOutput; } if (LocalReflections.Enabled && inputDepthTexture != null) { var normalsBuffer = GetInput(2); var specularRoughnessBuffer = GetInput(3); if (normalsBuffer != null && specularRoughnessBuffer != null) { // Local reflections var rlrOutput = NewScopedRenderTarget2D(input.Width, input.Height, input.Format); LocalReflections.SetInputSurfaces(currentInput, inputDepthTexture, normalsBuffer, specularRoughnessBuffer); LocalReflections.SetOutput(rlrOutput); LocalReflections.Draw(context); currentInput = rlrOutput; } } if (DepthOfField.Enabled && inputDepthTexture != null) { // DoF var dofOutput = NewScopedRenderTarget2D(input.Width, input.Height, input.Format); DepthOfField.SetColorDepthInput(currentInput, inputDepthTexture); DepthOfField.SetOutput(dofOutput); DepthOfField.Draw(context); currentInput = dofOutput; } if (Fog.Enabled && inputDepthTexture != null) { // Fog var fogOutput = NewScopedRenderTarget2D(input.Width, input.Height, input.Format); Fog.SetColorDepthInput(currentInput, inputDepthTexture, context.RenderContext.RenderView.NearClipPlane, context.RenderContext.RenderView.FarClipPlane); Fog.SetOutput(fogOutput); Fog.Draw(context); currentInput = fogOutput; } // Luminance pass (only if tone mapping is enabled) // TODO: This is not super pluggable to have this kind of dependencies. Check how to improve this var toneMap = colorTransformsGroup.Transforms.Get <ToneMap>(); if (colorTransformsGroup.Enabled && toneMap != null && toneMap.Enabled) { Texture luminanceTexture = null; if (toneMap.UseLocalLuminance) { const int localLuminanceDownScale = 3; // The luminance chain uses power-of-two intermediate targets, so it expects to output to one as well var lumWidth = Math.Min(MathUtil.NextPowerOfTwo(currentInput.Size.Width), MathUtil.NextPowerOfTwo(currentInput.Size.Height)); lumWidth = Math.Max(1, lumWidth / 2); var lumSize = new Size3(lumWidth, lumWidth, 1).Down2(localLuminanceDownScale); luminanceTexture = NewScopedRenderTarget2D(lumSize.Width, lumSize.Height, PixelFormat.R16_Float, 1); luminanceEffect.SetOutput(luminanceTexture); } luminanceEffect.EnableLocalLuminanceCalculation = toneMap.UseLocalLuminance; luminanceEffect.SetInput(currentInput); luminanceEffect.Draw(context); // Set this parameter that will be used by the tone mapping colorTransformsGroup.Parameters.Set(LuminanceEffect.LuminanceResult, new LuminanceResult(luminanceEffect.AverageLuminance, luminanceTexture)); } if (BrightFilter.Enabled && (Bloom.Enabled || LightStreak.Enabled || LensFlare.Enabled)) { Texture brightTexture = NewScopedRenderTarget2D(currentInput.Width, currentInput.Height, currentInput.Format, 1); // Bright filter pass BrightFilter.SetInput(currentInput); BrightFilter.SetOutput(brightTexture); BrightFilter.Draw(context); // Bloom pass if (Bloom.Enabled) { Bloom.SetInput(brightTexture); Bloom.SetOutput(currentInput); Bloom.Draw(context); } // Light streak pass if (LightStreak.Enabled) { LightStreak.SetInput(brightTexture); LightStreak.SetOutput(currentInput); LightStreak.Draw(context); } // Lens flare pass if (LensFlare.Enabled) { LensFlare.SetInput(brightTexture); LensFlare.SetOutput(currentInput); LensFlare.Draw(context); } } bool aaLast = needAA && !aaFirst; var toneOutput = aaLast ? NewScopedRenderTarget2D(input.Width, input.Height, input.Format) : output; // When FXAA is enabled we need to detect whether the ColorTransformGroup should output the Luminance into the alpha or not var luminanceToChannelTransform = colorTransformsGroup.PostTransforms.Get <LuminanceToChannelTransform>(); if (fxaa != null) { if (luminanceToChannelTransform is null) { luminanceToChannelTransform = new LuminanceToChannelTransform { ColorChannel = ColorChannel.A }; colorTransformsGroup.PostTransforms.Add(luminanceToChannelTransform); } // Only enabled when FXAA is enabled and InputLuminanceInAlpha is true luminanceToChannelTransform.Enabled = fxaa.Enabled && fxaa.InputLuminanceInAlpha; } else if (luminanceToChannelTransform != null) { luminanceToChannelTransform.Enabled = false; } // Color transform group pass (tonemap, color grading) var lastEffect = colorTransformsGroup.Enabled ? (ImageEffect)colorTransformsGroup : Scaler; lastEffect.SetInput(currentInput); lastEffect.SetOutput(toneOutput); lastEffect.Draw(context); // Do AA here, last, if not already done. if (aaLast) { Antialiasing.SetInput(toneOutput); Antialiasing.SetOutput(output); Antialiasing.Draw(context); } }
protected override void DrawCore(RenderDrawContext context) { var originalColorBuffer = GetSafeInput(0); var originalDepthBuffer = GetSafeInput(1); var outputTexture = GetSafeOutput(0); var renderView = context.RenderContext.RenderView; //--------------------------------- // Ambient Occlusion //--------------------------------- var tempWidth = (originalColorBuffer.Width * (int)TempSize) / (int)TemporaryBufferSize.SizeFull; var tempHeight = (originalColorBuffer.Height * (int)TempSize) / (int)TemporaryBufferSize.SizeFull; var aoTexture1 = NewScopedRenderTarget2D(tempWidth, tempHeight, PixelFormat.R8_UNorm, 1); var aoTexture2 = NewScopedRenderTarget2D(tempWidth, tempHeight, PixelFormat.R8_UNorm, 1); aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOKeys.Count, NumberOfSamples > 0 ? NumberOfSamples : 9); // check whether the projection matrix is orthographic var isOrthographic = renderView.Projection.M44 == 1; aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOKeys.IsOrthographic, isOrthographic); blurH.Parameters.Set(AmbientOcclusionBlurKeys.IsOrthographic, isOrthographic); blurV.Parameters.Set(AmbientOcclusionBlurKeys.IsOrthographic, isOrthographic); Vector2 zProj; if (isOrthographic) { zProj = new Vector2(renderView.NearClipPlane, renderView.FarClipPlane - renderView.NearClipPlane); } else { zProj = CameraKeys.ZProjectionACalculate(renderView.NearClipPlane, renderView.FarClipPlane); } // Set Near/Far pre-calculated factors to speed up the linear depth reconstruction aoRawImageEffect.Parameters.Set(CameraKeys.ZProjection, ref zProj); Vector4 screenSize = new Vector4(originalColorBuffer.Width, originalColorBuffer.Height, 0, 0); screenSize.Z = screenSize.X / screenSize.Y; aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOShaderKeys.ScreenInfo, screenSize); Vector4 projInfo; if (isOrthographic) { // The orthographic scale to map the xy coordinates float scaleX = 1 / renderView.Projection.M11; float scaleY = 1 / renderView.Projection.M22; // Constant factor to map the ProjScale parameter to the orthographic scale float projZScale = System.Math.Max(scaleX, scaleY) * 4; projInfo = new Vector4(scaleX, scaleY, projZScale, 0); } else { // Projection info used to reconstruct the View space position from linear depth var p00 = renderView.Projection.M11; var p11 = renderView.Projection.M22; var p02 = renderView.Projection.M13; var p12 = renderView.Projection.M23; projInfo = new Vector4( -2.0f / (screenSize.X * p00), -2.0f / (screenSize.Y * p11), (1.0f - p02) / p00, (1.0f + p12) / p11); } aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOShaderKeys.ProjInfo, ref projInfo); //********************************** // User parameters aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOShaderKeys.ParamProjScale, ParamProjScale); aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOShaderKeys.ParamIntensity, ParamIntensity); aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOShaderKeys.ParamBias, ParamBias); aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOShaderKeys.ParamRadius, ParamRadius); aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOShaderKeys.ParamRadiusSquared, ParamRadius * ParamRadius); aoRawImageEffect.SetInput(0, originalDepthBuffer); aoRawImageEffect.SetOutput(aoTexture1); aoRawImageEffect.Draw(context, "AmbientOcclusionRawAO"); for (int bounces = 0; bounces < NumberOfBounces; bounces++) { if (offsetsWeights == null) { offsetsWeights = new[] { // 0.356642f, 0.239400f, 0.072410f, 0.009869f, // 0.398943f, 0.241971f, 0.053991f, 0.004432f, 0.000134f, // stddev = 1.0 0.153170f, 0.144893f, 0.122649f, 0.092902f, 0.062970f, // stddev = 2.0 // 0.111220f, 0.107798f, 0.098151f, 0.083953f, 0.067458f, 0.050920f, 0.036108f, // stddev = 3.0 }; nameGaussianBlurH = string.Format("AmbientOcclusionBlurH{0}x{0}", offsetsWeights.Length); nameGaussianBlurV = string.Format("AmbientOcclusionBlurV{0}x{0}", offsetsWeights.Length); } // Set Near/Far pre-calculated factors to speed up the linear depth reconstruction blurH.Parameters.Set(CameraKeys.ZProjection, ref zProj); blurV.Parameters.Set(CameraKeys.ZProjection, ref zProj); // Update permutation parameters blurH.Parameters.Set(AmbientOcclusionBlurKeys.Count, offsetsWeights.Length); blurH.Parameters.Set(AmbientOcclusionBlurKeys.BlurScale, BlurScale); blurH.Parameters.Set(AmbientOcclusionBlurKeys.EdgeSharpness, EdgeSharpness); blurH.EffectInstance.UpdateEffect(context.GraphicsDevice); blurV.Parameters.Set(AmbientOcclusionBlurKeys.Count, offsetsWeights.Length); blurV.Parameters.Set(AmbientOcclusionBlurKeys.BlurScale, BlurScale); blurV.Parameters.Set(AmbientOcclusionBlurKeys.EdgeSharpness, EdgeSharpness); blurV.EffectInstance.UpdateEffect(context.GraphicsDevice); // Update parameters blurH.Parameters.Set(AmbientOcclusionBlurShaderKeys.Weights, offsetsWeights); blurV.Parameters.Set(AmbientOcclusionBlurShaderKeys.Weights, offsetsWeights); // Horizontal pass blurH.SetInput(0, aoTexture1); blurH.SetInput(1, originalDepthBuffer); blurH.SetOutput(aoTexture2); blurH.Draw(context, nameGaussianBlurH); // Vertical pass blurV.SetInput(0, aoTexture2); blurV.SetInput(1, originalDepthBuffer); blurV.SetOutput(aoTexture1); blurV.Draw(context, nameGaussianBlurV); } aoApplyImageEffect.SetInput(0, originalColorBuffer); aoApplyImageEffect.SetInput(1, aoTexture1); aoApplyImageEffect.SetOutput(outputTexture); aoApplyImageEffect.Draw(context, "AmbientOcclusionApply"); }
protected override void DrawCore(RenderDrawContext contextParameters) { var input = GetInput(0); var output = GetOutput(0) ?? input; if (input == null || StreakCount == 0 || IterationCount == 0) { return; } // Downscale to 1 / 4 var halfSize = input.Size.Down2(); var halfSizeRenderTarget = NewScopedRenderTarget2D(halfSize.Width, halfSize.Height, input.Format); Scaler.SetInput(input); Scaler.SetOutput(halfSizeRenderTarget); Scaler.Draw(contextParameters, "Downsize to 0.5"); var fourthSize = halfSize.Down2(); var fourthSizeRenderTarget = NewScopedRenderTarget2D(fourthSize.Width, fourthSize.Height, input.Format); Scaler.SetInput(halfSizeRenderTarget); Scaler.SetOutput(fourthSizeRenderTarget); Scaler.Draw(contextParameters, "Downsize to 0.25"); var originalDownsize = fourthSizeRenderTarget; // Put all the streaks in an accumulation buffer var accumulationBuffer = NewScopedRenderTarget2D(fourthSizeRenderTarget.Description); // 2 scratch textures to ping-pong between var scratchTextureA = NewScopedRenderTarget2D(fourthSizeRenderTarget.Description); var scratchTextureB = NewScopedRenderTarget2D(fourthSizeRenderTarget.Description); var writeToScratchA = true; Vector2 direction; Texture currentInput = null, currentOutput = null; Vector3 colorAberration; colorAberration.X = (float)MathUtil.Lerp(1.0, ColorAberrationCoefficients.X, ColorAberrationStrength); colorAberration.Y = (float)MathUtil.Lerp(1.0, ColorAberrationCoefficients.Y, ColorAberrationStrength); colorAberration.Z = (float)MathUtil.Lerp(1.0, ColorAberrationCoefficients.Z, ColorAberrationStrength); lightStreakEffect.Parameters.Set(LightStreakShaderKeys.ColorAberrationCoefficients, ref colorAberration); for (int streak = 0; streak < StreakCount; streak++) { // Treats one streak // Direction vector float angle = MathUtil.DegreesToRadians(Phase) + streak * MathUtil.TwoPi / StreakCount; direction.X = (float)Math.Cos(angle); direction.Y = (float)Math.Sin(angle); // Extends the length recursively for (int level = 0; level < IterationCount; level++) { // Calculates weights and attenuation factors for all the taps float totalWeight = 0; float passLength = (float)Math.Pow(TapsPerIteration, level); for (int i = 0; i < TapsPerIteration; i++) { tapOffsetsWeights[i].X = i * passLength; tapOffsetsWeights[i].Y = (float)Math.Pow(MathUtil.Lerp(0.7f, 1.0f, Attenuation), i * passLength); totalWeight += tapOffsetsWeights[i].Y; } // Normalizes the weights for (int i = 0; i < TapsPerIteration; i++) { tapOffsetsWeights[i].Y /= totalWeight; } currentInput = writeToScratchA ? scratchTextureB : scratchTextureA; if (level == 0) { currentInput = originalDownsize; } currentOutput = writeToScratchA ? scratchTextureA : scratchTextureB; lightStreakEffect.Parameters.Set(LightStreakKeys.Count, TapsPerIteration); lightStreakEffect.Parameters.Set(LightStreakKeys.AnamorphicCount, AnamorphicOffsetsWeights.Length); lightStreakEffect.Parameters.Set(LightStreakShaderKeys.TapOffsetsWeights, tapOffsetsWeights); lightStreakEffect.Parameters.Set(LightStreakShaderKeys.AnamorphicOffsetsWeight, AnamorphicOffsetsWeights); lightStreakEffect.Parameters.Set(LightStreakShaderKeys.Direction, direction); lightStreakEffect.SetInput(0, currentInput); lightStreakEffect.SetOutput(currentOutput); lightStreakEffect.Draw(contextParameters, lightStreakDebugStrings[(streak * IterationCount) + level]); writeToScratchA = !writeToScratchA; } // Writes this streak to the accumulation buffer if (streak > 0) { combiner.BlendState = BlendStates.Additive; } combiner.SetInput(0, currentOutput); combiner.Factors[0] = (1f / StreakCount) * 0.2f * Amount; combiner.SetOutput(accumulationBuffer); ((RendererBase)combiner).Draw(contextParameters); combiner.BlendState = BlendStates.Default; } // All the light streaks have been drawn to the accumulation buffer. // Upscales and blurs the accumulation buffer. var accumulationUpscaled = NewScopedRenderTarget2D(halfSizeRenderTarget.Description); Scaler.SetInput(accumulationBuffer); Scaler.SetOutput(accumulationUpscaled); ((RendererBase)Scaler).Draw(contextParameters); blur.Radius = 3; blur.SetInput(accumulationUpscaled); blur.SetOutput(accumulationUpscaled); ((RendererBase)blur).Draw(contextParameters); // Adds the result to the original color buffer. Scaler.BlendState = BlendStates.Additive; Scaler.SetInput(accumulationUpscaled); Scaler.SetOutput(output); ((RendererBase)Scaler).Draw(contextParameters); Scaler.BlendState = BlendStates.Default; }