Example #1
0
        /// <summary>
        /// Computes sample offsets and weights for Gaussian blur filter kernel.
        /// </summary>
        /// <param name="numberOfSamples">
        /// The number of samples. This value must be an odd number (e.g. 3, 5, 7, ...).
        /// </param>
        /// <param name="standardDeviation">The standard deviation.</param>
        /// <param name="useHardwareFiltering">
        /// If set to <see langword="true"/> hardware filtering is used to increase the blur effect;
        /// otherwise, hardware filtering is not used. Use <see langword="false"/> if you are filtering
        /// floating-point textures.
        /// </param>
        /// <exception cref="ArgumentOutOfRangeException">
        /// <paramref name="numberOfSamples"/> is zero or negative.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="numberOfSamples"/> is an even number. A Gaussian blur requires an odd number of
        /// samples.
        /// </exception>
        public void InitializeGaussianBlur(int numberOfSamples, float standardDeviation, bool useHardwareFiltering)
        {
            if (numberOfSamples < 1)
            {
                throw new ArgumentOutOfRangeException("numberOfSamples", "The numberOfSamples must be greater than 0.");
            }
            if (numberOfSamples % 2 == 0)
            {
                throw new ArgumentException("Gaussian blur expects an odd number of samples.");
            }

            // Initialize weights with 0.
            for (int i = 0; i < MaxNumberOfSamples; i++)
            {
                Weights[i] = 0;
            }

            NumberOfSamples = numberOfSamples;
            Scale           = 1;
            IsSeparable     = true;

            // Define the Gaussian function coefficient that we use.
            float coefficient = 1 / (float)Math.Sqrt(ConstantsF.TwoPi) / standardDeviation;

            if (useHardwareFiltering)
            {
                // Sample the center pixel in the middle and then between pixel.
                // We sample 2 pixels per tap, so we can sample twice as wide.
                standardDeviation = standardDeviation * 2;

                /*
                 * // ----- Unordered samples
                 * Offsets[0] = new Vector2(0, 0);
                 * Weights[0] = MathHelper.Gaussian(0, coefficient, 0, standardDeviation);
                 * float weightSum = Weights[0];
                 * for (int i = 1; i < numberOfSamples; i += 2)
                 * {
                 *  // Get an offset between two pixels.
                 *  var offset = new Vector2(1.5f + (i - 1), 0); // = 1.5 + k * 2
                 *  // Get the offsets of the neighboring pixel centers.
                 *  var o0 = offset.X - 0.5f;
                 *  var o1 = offset.X + 0.5f;
                 *  // Compute the weights of the pixel centers.
                 *  var w0 = MathHelper.Gaussian(o0, coefficient, 0, standardDeviation);
                 *  var w1 = MathHelper.Gaussian(o1, coefficient, 0, standardDeviation);
                 *  // Shift the offset to the pixel center that has the higher weight.
                 *  offset.X = (o0 * w0 + o1 * w1) / (w0 + w1);
                 *
                 *  Offsets[i] = offset;
                 *  Offsets[i + 1] = -offset;
                 *  Weights[i] = w0 + w1;
                 *  Weights[i + 1] = Weights[i];
                 *  weightSum += Weights[i] * 2;
                 * }
                 *
                 * // Normalize weights.
                 * for (int i = 0; i < numberOfSamples; i++)
                 *  Weights[i] /= weightSum;
                 */

                // ----- Ordered samples
                int left  = (numberOfSamples - 1) / 2; // Number of samples on the left.
                int right = numberOfSamples / 2;       // Number of samples on the right.
                int count = 0;                         // Number of samples generated.

                Debug.Assert(left + right + 1 == numberOfSamples, "Wrong number of samples?");

                float weight;        // The weight of the current sample.
                float weightSum = 0; // The sum of all weights (for normalization).

                // Samples on the left.
                for (int i = -left; i <= -1; i++)
                {
                    Vector2 offset = new Vector2(2 * i + 0.5f, 0);

                    // Get the offsets and weights of the neighboring pixel centers.
                    var o0 = offset.X - 0.5f;
                    var o1 = offset.X + 0.5f;
                    var w0 = MathHelper.Gaussian(o0, coefficient, 0, standardDeviation);
                    var w1 = MathHelper.Gaussian(o1, coefficient, 0, standardDeviation);

                    // Shift the offset to the pixel center that has the higher weight.
                    offset.X = (o0 * w0 + o1 * w1) / (w0 + w1);

                    Offsets[count] = offset;
                    weight         = w0 + w1;
                    Weights[count] = weight;
                    weightSum     += weight;
                    count++;
                }

                // Center sample.
                Offsets[count] = new Vector2(0, 0);
                weight         = MathHelper.Gaussian(0, coefficient, 0, standardDeviation);
                Weights[count] = weight;
                weightSum     += weight;
                count++;

                // Samples on the right.
                for (int i = 1; i <= right; i++)
                {
                    Vector2 offset = new Vector2(2 * i - 0.5f, 0);

                    // Get the offsets and weights of the neighboring pixel centers.
                    var o0 = offset.X - 0.5f;
                    var o1 = offset.X + 0.5f;
                    var w0 = MathHelper.Gaussian(o0, coefficient, 0, standardDeviation);
                    var w1 = MathHelper.Gaussian(o1, coefficient, 0, standardDeviation);

                    // Shift the offset to the pixel center that has the higher weight.
                    offset.X = (o0 * w0 + o1 * w1) / (w0 + w1);

                    Offsets[count] = offset;
                    weight         = w0 + w1;
                    Weights[count] = weight;
                    weightSum     += weight;
                    count++;
                }

                // Normalize weights.
                for (int i = 0; i < numberOfSamples; i++)
                {
                    Weights[i] /= weightSum;
                }

                Debug.Assert(count == numberOfSamples, "Wrong number of samples generated?");
                Debug.Assert(Numeric.AreEqual(Weights.Take(numberOfSamples).Sum(), 1.0f), "Invalid sample weights.");
            }
            else
            {
                // Sample in the middle of pixels.

                /*
                 * // ----- Unordered samples
                 * Offsets[0] = new Vector2(0, 0);
                 * Weights[0] = MathHelper.Gaussian(0, coefficient, 0, standardDeviation);
                 * float weightSum = Weights[0];
                 * for (int i = 1; i < numberOfSamples; i += 2)
                 * {
                 *  var offset = new Vector2(1 + i / 2, 0);
                 *  Offsets[i] = offset;
                 *  Offsets[i + 1] = -offset;
                 *  Weights[i] = MathHelper.Gaussian(offset.X, coefficient, 0, standardDeviation);
                 *  Weights[i + 1] = Weights[i];
                 *  weightSum += (Weights[i] * 2);
                 * }
                 *
                 * // Normalize weights.
                 * for (int i = 0; i < numberOfSamples; i++)
                 *  Weights[i] /= weightSum;
                 */

                // ----- Ordered samples
                int left  = (numberOfSamples - 1) / 2; // Number of samples on the left.
                int right = numberOfSamples / 2;       // Number of samples on the right.
                int count = 0;                         // Number of samples generated.

                Debug.Assert(left + right + 1 == numberOfSamples, "Wrong number of samples?");

                float weight;        // The weight of the current sample.
                float weightSum = 0; // The sum of all weights (for normalization).

                // Samples on the left.
                for (int i = -left; i <= -1; i++)
                {
                    Offsets[count] = new Vector2(i, 0);
                    weight         = MathHelper.Gaussian(i, coefficient, 0, standardDeviation);
                    Weights[count] = weight;
                    weightSum     += weight;
                    count++;
                }

                // Center sample.
                Offsets[count] = new Vector2(0, 0);
                weight         = MathHelper.Gaussian(0, coefficient, 0, standardDeviation);
                Weights[count] = weight;
                weightSum     += weight;
                count++;

                // Samples on the right.
                for (int i = 1; i <= right; i++)
                {
                    Offsets[count] = new Vector2(i, 0);
                    weight         = MathHelper.Gaussian(i, coefficient, 0, standardDeviation);
                    Weights[count] = weight;
                    weightSum     += weight;
                    count++;
                }

                // Normalize weights.
                for (int i = 0; i < numberOfSamples; i++)
                {
                    Weights[i] /= weightSum;
                }

                Debug.Assert(count == numberOfSamples, "Wrong number of samples generated?");
                Debug.Assert(Numeric.AreEqual(Weights.Take(numberOfSamples).Sum(), 1.0f), "Invalid sample weights.");
            }
        }
        private void InitializeGaussianBlur(Vector2F viewportSize, bool useHardwareFiltering)
        {
            if (_horizontalOffsets != null && _lastViewportSize == viewportSize)
            {
                return;
            }

            _lastViewportSize = viewportSize;

            int   numberOfSamples   = _offsetsParameter.Elements.Count;
            float standardDeviation = numberOfSamples / 3.0f * BlurStrength;

            if (_horizontalOffsets == null)
            {
                _horizontalOffsets = new Vector2[numberOfSamples];
                _verticalOffsets   = new Vector2[numberOfSamples];
                _weights           = new float[numberOfSamples];
            }

            // Define the Gaussian function coefficient that we use.
            float coefficient = 1 / (float)Math.Sqrt(ConstantsF.TwoPi) / standardDeviation;

            float weightSum;

            if (useHardwareFiltering)
            {
                // We sample 2 pixels per tap, so we can sample twice as wide.
                standardDeviation = standardDeviation * 2;

                // Sample the center pixel in the middle and then between pixel.
                _horizontalOffsets[0] = new Vector2(0, 0);
                _verticalOffsets[0]   = new Vector2(0, 0);
                _weights[0]           = (BlurStrength > 0) ? MathHelper.Gaussian(0, coefficient, 0, standardDeviation) : 1;
                weightSum             = _weights[0];
                for (int i = 1; i < numberOfSamples; i += 2)
                {
                    // Get an offset between two pixels.
                    var offset = new Vector2(1.5f + (i - 1), 0); // = 1.5 + k * 2

                    // Get the offsets of the neighboring pixel centers.
                    var o0 = offset.X - 0.5f;
                    var o1 = offset.X + 0.5f;

                    // Compute the weights of the pixel centers.
                    var w0 = (BlurStrength > 0) ? MathHelper.Gaussian(o0, coefficient, 0, standardDeviation) : 0;
                    var w1 = (BlurStrength > 0) ? MathHelper.Gaussian(o1, coefficient, 0, standardDeviation) : 0;
                    _weights[i]     = (w0 + w1);
                    _weights[i + 1] = _weights[i];
                    weightSum      += (_weights[i] * 2);

                    // Shift the offset to the pixel center that has the higher weight.
                    offset.X = (o0 * w0 + o1 * w1) / (w0 + w1);

                    _horizontalOffsets[i]     = offset / viewportSize.X;
                    _horizontalOffsets[i + 1] = -_horizontalOffsets[i];
                    _verticalOffsets[i]       = new Vector2(0, offset.X) / viewportSize.Y;
                    _verticalOffsets[i + 1]   = -_verticalOffsets[i];
                }
            }
            else
            {
                // Same as above but: Sample in the middle of pixels.
                _horizontalOffsets[0] = new Vector2(0, 0);
                _verticalOffsets[0]   = new Vector2(0, 0);
                _weights[0]           = (BlurStrength > 0) ? MathHelper.Gaussian(0, coefficient, 0, standardDeviation) : 1;
                weightSum             = _weights[0];
                for (int i = 1; i < numberOfSamples; i += 2)
                {
                    var offset = new Vector2(1 + i / 2, 0);

                    _weights[i]     = (BlurStrength > 0) ? MathHelper.Gaussian(offset.X, coefficient, 0, standardDeviation) : 0;
                    _weights[i + 1] = _weights[i];
                    weightSum      += (_weights[i] * 2);

                    _horizontalOffsets[i]     = offset / viewportSize.X;
                    _horizontalOffsets[i + 1] = -_horizontalOffsets[i];
                    _verticalOffsets[i]       = new Vector2(0, offset.X) / viewportSize.Y;
                    _verticalOffsets[i + 1]   = -_verticalOffsets[i];
                }
            }

            // Normalize weights.
            for (int i = 0; i < numberOfSamples; i++)
            {
                _weights[i] /= weightSum;
            }
        }