/// <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; } }