Даниэль Ракос представил высокооптимизированный шейдерный подход для выполнения размытия по Гауссу с девятью ударами .Его процесс использует базовую интерполяцию, обеспечиваемую аппаратной фильтрацией текстур, для выполнения фильтра из девяти попаданий, используя только пять операций чтения текстур за проход.Это также разделено на отдельные горизонтальные и вертикальные проходы для дальнейшего уменьшения необходимого количества чтений текстур.
Я свернул реализацию этого, настроенную для OpenGL ES и графических процессоров iOS, в мою среду обработки изображений (в классе GPUImageFastBlurFilter).В моих тестах он мог выполнить один проход размытия кадра 640x480 за 2,0 мс на iPhone 4, что довольно быстро.
Я использовал следующий вершинный шейдер:
attribute vec4 position;
attribute vec2 inputTextureCoordinate;
uniform mediump float texelWidthOffset;
uniform mediump float texelHeightOffset;
varying mediump vec2 centerTextureCoordinate;
varying mediump vec2 oneStepLeftTextureCoordinate;
varying mediump vec2 twoStepsLeftTextureCoordinate;
varying mediump vec2 oneStepRightTextureCoordinate;
varying mediump vec2 twoStepsRightTextureCoordinate;
void main()
{
gl_Position = position;
vec2 firstOffset = vec2(1.3846153846 * texelWidthOffset, 1.3846153846 * texelHeightOffset);
vec2 secondOffset = vec2(3.2307692308 * texelWidthOffset, 3.2307692308 * texelHeightOffset);
centerTextureCoordinate = inputTextureCoordinate;
oneStepLeftTextureCoordinate = inputTextureCoordinate - firstOffset;
twoStepsLeftTextureCoordinate = inputTextureCoordinate - secondOffset;
oneStepRightTextureCoordinate = inputTextureCoordinate + firstOffset;
twoStepsRightTextureCoordinate = inputTextureCoordinate + secondOffset;
}
и следующий фрагментный шейдер:
precision highp float;
uniform sampler2D inputImageTexture;
varying mediump vec2 centerTextureCoordinate;
varying mediump vec2 oneStepLeftTextureCoordinate;
varying mediump vec2 twoStepsLeftTextureCoordinate;
varying mediump vec2 oneStepRightTextureCoordinate;
varying mediump vec2 twoStepsRightTextureCoordinate;
// const float weight[3] = float[]( 0.2270270270, 0.3162162162, 0.0702702703 );
void main()
{
lowp vec3 fragmentColor = texture2D(inputImageTexture, centerTextureCoordinate).rgb * 0.2270270270;
fragmentColor += texture2D(inputImageTexture, oneStepLeftTextureCoordinate).rgb * 0.3162162162;
fragmentColor += texture2D(inputImageTexture, oneStepRightTextureCoordinate).rgb * 0.3162162162;
fragmentColor += texture2D(inputImageTexture, twoStepsLeftTextureCoordinate).rgb * 0.0702702703;
fragmentColor += texture2D(inputImageTexture, twoStepsRightTextureCoordinate).rgb * 0.0702702703;
gl_FragColor = vec4(fragmentColor, 1.0);
}
, чтобы выполнить это.Два прохода могут быть достигнуты путем отправки значения 0 для texelWidthOffset
(для вертикального прохода), а затем подачи этого результата в цикл, где вы задаете значение 0 для texelHeightOffset
(для горизонтального прохода).
У меня также есть несколько более продвинутых примеров извилин в вышеупомянутой интегрированной структуре, включая определение края Собеля.