Example #1
0
        // Naive approach: 4 passes. (Reference version)
        private void DrawCoreNaive(RenderContext 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.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(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(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(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");
        }
Example #2
0
        protected override void DrawCore(RenderContext 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.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);
        }
Example #3
0
        // Optimized approach: 2 passes.
        private void DrawCoreOptimized(RenderContext 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.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(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.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(context, "McIntoshBokehPass2_BlurABCombine_tap{0}_radius{1}", tapNumber, (int)Radius);
        }
Example #4
0
        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();
        }
Example #5
0
        // Naive approach: 6 passes
        protected void DrawCoreNaive(RenderContext 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.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");
        }