protected override void DrawCore(RenderContext context) { var input = GetInput(0); var output = GetOutput(0); if (FadeOutSpeed == 0f) { // Nothing to do if (input != output) { GraphicsDevice.Copy(input, output); } return; } if (input == output) { var newInput = NewScopedRenderTarget2D(input.Description); GraphicsDevice.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 GraphicsDevice.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("Afterimage persistence accumulation"); // Keep the final brightness buffer for the following frames GraphicsDevice.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("Afterimage persistence combine"); }
protected override void DrawCore(RenderContext contextParameters) { var input = GetInput(0); var output = GetOutput(0) ?? input; if (input == null) { return; } // Downscale to 1/2 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"); // Work on a blurred bright map var blurredBright = NewScopedRenderTarget2D(halfSizeRenderTarget.Description); blur.Radius = 8; blur.SetInput(halfSizeRenderTarget); blur.SetOutput(blurredBright); blur.Draw(contextParameters); // Draws a few artifacts var flareRenderTargetInitial = NewScopedRenderTarget2D(halfSizeRenderTarget.Description); var flareRenderTarget = NewScopedRenderTarget2D(halfSizeRenderTarget.Description); flareArtifactEffect.Parameters.Set(FlareArtifactKeys.Count, ZoomOffsetsDistortions.Length); flareArtifactEffect.Parameters.Set(FlareArtifactShaderKeys.ZoomOffsetsDistortions, ZoomOffsetsDistortions); flareArtifactEffect.Parameters.Set(FlareArtifactShaderKeys.AberrationStrength, ColorAberrationStrength); flareArtifactEffect.Parameters.Set(FlareArtifactShaderKeys.ColorAberrations, ColorAberrations); flareArtifactEffect.Parameters.Set(FlareArtifactShaderKeys.Amount, Amount * 0.0005f); flareArtifactEffect.SetInput(0, blurredBright); flareArtifactEffect.SetOutput(flareRenderTargetInitial); flareArtifactEffect.Draw(contextParameters); // Replicates the artifacts around flareReplicateEffect.Parameters.Set(FlareReplicateKeys.Amount, Amount * 0.0005f); flareReplicateEffect.Parameters.Set(FlareReplicateKeys.HaloFactor, Amount * 0.0005f * HaloFactor); flareReplicateEffect.SetInput(0, flareRenderTargetInitial); flareReplicateEffect.SetInput(1, blurredBright); flareReplicateEffect.SetOutput(flareRenderTarget); flareReplicateEffect.Draw(contextParameters); // Adds the result to the scene GraphicsDevice.SetBlendState(GraphicsDevice.BlendStates.Additive); Scaler.SetInput(flareRenderTarget); Scaler.SetOutput(output); Scaler.Draw(contextParameters); GraphicsDevice.SetBlendState(GraphicsDevice.BlendStates.Default); }
protected override void DrawCore(RenderContext context) { var input = GetInput(0); var output = GetOutput(0); if (input == null || output == null) { return; } brightPassFilter.Parameters.Set(BrightFilterShaderKeys.BrightPassThreshold, Threshold); brightPassFilter.Parameters.Set(BrightFilterShaderKeys.ColorModulator, Color); brightPassFilter.SetInput(input); brightPassFilter.SetOutput(output); brightPassFilter.Draw(context); }
protected override void DrawCore(RenderContext context) { // Input texture var inputTexture = GetSafeInput(0); // 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.MultiSampleLevel = MSAALevel.None; // TODO we should have a method to get a non-MSAA RT var outputTextureH = NewScopedRenderTarget2D(desc); var size = Radius * 2 + 1; if (offsetsWeights == null) { nameGaussianBlurH = string.Format("GaussianBlurH{0}x{0}", size); nameGaussianBlurV = string.Format("GaussianBlurV{0}x{0}", size); // TODO: cache if necessary offsetsWeights = GaussianUtil.Calculate1D(Radius, SigmaRatio); } // Update shared parameters Parameters.Set(GaussianBlurKeys.Count, offsetsWeights.Length); Parameters.Set(GaussianBlurShaderKeys.OffsetsWeights, offsetsWeights); // Horizontal pass blurH.SetInput(inputTexture); blurH.SetOutput(outputTextureH); blurH.Draw(context, nameGaussianBlurH); // Vertical pass blurV.SetInput(outputTextureH); blurV.SetOutput(GetSafeOutput(0)); blurV.Draw(context, nameGaussianBlurV); }
protected override void DrawCore(RenderContext 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, 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, "Light streak {0} iteration {0}", streak, level); writeToScratchA = !writeToScratchA; } // Writes this streak to the accumulation buffer if (streak > 0) { GraphicsDevice.SetBlendState(GraphicsDevice.BlendStates.Additive); } combiner.SetInput(0, currentOutput); combiner.Factors[0] = (1f / StreakCount) * 0.2f * Amount; combiner.SetOutput(accumulationBuffer); combiner.Draw(contextParameters); GraphicsDevice.SetBlendState(GraphicsDevice.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); Scaler.Draw(contextParameters); blur.Radius = 3; blur.SetInput(accumulationUpscaled); blur.SetOutput(accumulationUpscaled); blur.Draw(contextParameters); // Adds the result to the original color buffer. GraphicsDevice.SetBlendState(GraphicsDevice.BlendStates.Additive); Scaler.SetInput(accumulationUpscaled); Scaler.SetOutput(output); Scaler.Draw(contextParameters); GraphicsDevice.SetBlendState(GraphicsDevice.BlendStates.Default); }
protected override void DrawCore(RenderContext context) { var input = GetSafeInput(0); var output = GetSafeOutput(0); // Render the luminance to a power-of-two target, so we preserve energy on downscaling var startWidth = Math.Max(1, Math.Min(MathUtil.NextPowerOfTwo(input.Size.Width), MathUtil.NextPowerOfTwo(input.Size.Height)) / 2); var startSize = new Size3(startWidth, startWidth, 1); var blurTextureSize = startSize.Down2(UpscaleCount); Texture outputTextureDown = null; if (blurTextureSize.Width != 1 && blurTextureSize.Height != 1) { outputTextureDown = NewScopedRenderTarget2D(blurTextureSize.Width, blurTextureSize.Height, luminanceFormat, 1); } var luminanceMap = NewScopedRenderTarget2D(startSize.Width, startSize.Height, luminanceFormat, 1); // Calculate the first luminance map luminanceLogEffect.SetInput(input); luminanceLogEffect.SetOutput(luminanceMap); luminanceLogEffect.Draw(context); // Downscales luminance up to BlurTexture (optional) and 1x1 multiScaler.SetInput(luminanceMap); if (outputTextureDown == null) { multiScaler.SetOutput(luminance1x1); } else { multiScaler.SetOutput(outputTextureDown, luminance1x1); } multiScaler.Draw(); // If we have an output texture if (outputTextureDown != null) { // Blur x2 the intermediate output texture blur.SetInput(outputTextureDown); blur.SetOutput(outputTextureDown); blur.Draw(context); blur.Draw(context); // Upscale from intermediate to output multiScaler.SetInput(outputTextureDown); multiScaler.SetOutput(output); multiScaler.Draw(context); } else { // TODO: Workaround to that the output filled with 1x1 Scaler.SetInput(luminance1x1); Scaler.SetOutput(output); Scaler.Draw(context); } // Calculate average luminance only if needed if (EnableAverageLuminanceReadback) { readback.Draw(); var rawLogValue = readback.Result[0]; AverageLuminance = (float)Math.Pow(2.0, rawLogValue); // In case AvergaeLuminance go crazy because of halp float/infinity precision, some code to save the values here: //if (float.IsInfinity(AverageLuminance)) //{ // using (var stream = new FileStream("luminance_input.dds", FileMode.Create, FileAccess.Write)) // { // input.Save(stream, ImageFileType.Dds); // } // using (var stream = new FileStream("luminance.dds", FileMode.Create, FileAccess.Write)) // { // luminanceMap.Save(stream, ImageFileType.Dds); // } //} } }
protected override void DrawCore(RenderContext 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 farPlane = context.Parameters.Get(CameraKeys.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 < 1f) { autoFocusDistanceCurrent = 1f; } depthAreas = new Vector4(0.5f, autoFocusDistanceCurrent, autoFocusDistanceCurrent, autoFocusDistanceCurrent + farPlane * 0.5f); } coclinearDepthMapEffect.SetInput(0, originalDepthBuffer); coclinearDepthMapEffect.SetOutput(cocLinearDepthTexture); coclinearDepthMapEffect.Parameters.Set(CircleOfConfusionKeys.depthAreas, depthAreas); 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("Center Depth"); depthReadBack.SetInput(depthCenter1x1); depthReadBack.Draw("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. GraphicsDevice.SetBlendState(GraphicsDevice.BlendStates.AlphaBlend); combineLevelsFrontEffect.SetOutput(outputTexture); combineLevelsFrontEffect.Draw(context, "CoCLevelCombineInterpolationFront"); GraphicsDevice.SetBlendState(GraphicsDevice.BlendStates.Default); // Release any reference downscaledSources.Clear(); }
protected override void DrawCore(RenderContext context) { var input = GetSafeInput(0); // TODO: Check that input is power of two // input.Size.Width Texture fromTexture = input; Texture downTexture = null; var nextSize = input.Size; bool isFirstPass = true; while (nextSize.Width > 3 && nextSize.Height > 3) { var previousSize = nextSize; nextSize = nextSize.Down2(); // If the next half size of the texture is not an exact *2, make it 1 pixel larger to avoid loosing pixels min/max. if ((nextSize.Width * 2) < previousSize.Width) { nextSize.Width += 1; } if ((nextSize.Height * 2) < previousSize.Height) { nextSize.Height += 1; } downTexture = NewScopedRenderTarget2D(nextSize.Width, nextSize.Height, PixelFormat.R32G32_Float, 1); effect.Parameters.Set(DepthMinMaxShaderKeys.TextureMap, fromTexture); effect.Parameters.Set(DepthMinMaxShaderKeys.TextureReduction, fromTexture); effect.SetOutput(downTexture); effect.Parameters.Set(IsFirstPassKey, isFirstPass); effect.Draw(context); fromTexture = downTexture; isFirstPass = false; } readback.SetInput(downTexture); readback.Draw(); IsResultAvailable = readback.IsResultAvailable; if (IsResultAvailable) { float min = float.MaxValue; float max = -float.MaxValue; var results = readback.Result; foreach (var result in results) { min = Math.Min(result.X, min); if (result.Y != 1.0f) { max = Math.Max(result.Y, max); } } Result = new Vector2(min, max); } }