Фрагментный шейдер OpenGL ES 2.0 для размытия работает медленно и имеет низкое качество

Я пытаюсь написать шейдер размытия для iPad. У меня это работает, но я не очень доволен результатами. Я получаю очень изменчивую частоту кадров, и размытие выглядит как дерьмо, когда степень размытия высока.

Есть идеи, как улучшить ситуацию?

Некоторый пример вывода:

альтернативный текст

uniform sampler2D texture;
varying mediump vec2 fragTexCoord;
varying mediump vec3 eyespaceNormal;

varying highp float blurAmount;

void main(void)
{
    highp vec2 gaussFilter[7];
    gaussFilter[0] = vec2(-3.0, 0.015625);
    gaussFilter[1] = vec2(-2.0, 0.09375);
    gaussFilter[2] = vec2(-1.0, 0.234375);
    gaussFilter[3] = vec2(0.0, 0.3125);
    gaussFilter[4] = vec2(1.0, 0.234375);
    gaussFilter[5] = vec2(2.0, 0.09375);
    gaussFilter[6] = vec2(3.0, 0.015625);

    highp float blurSize = blurAmount * 1.0;

    /////////////////////////////////////////////////
    // 7x1 gaussian blur fragment shader
    /////////////////////////////////////////////////

    highp vec4 color = vec4(0,0,0,1);

    for( int i = 0; i < 7; i++ )
    {
        color += texture2D( texture, vec2( fragTexCoord.x+gaussFilter[i].x*blurSize, fragTexCoord.y+gaussFilter[i].x*blurSize ) )*gaussFilter[i].y;
    }

    gl_FragColor = color;
}

Редактировать: Размытие коробки может быть подходящим способом. Вот версия шейдера с размытием рамки:

highp vec4 color = vec4(0,0,0,1);

color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y - 4.0*blurAmount)) * 0.05;
color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y - 3.0*blurAmount)) * 0.09;
color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y - 2.0*blurAmount)) * 0.12;
color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y - blurAmount)) * 0.15;
color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y)) * 0.16;
color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y + blurAmount)) * 0.15;
color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y + 2.0*blurAmount)) * 0.12;
color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y + 3.0*blurAmount)) * 0.09;
color += texture2D(texture, vec2(fragTexCoord.x, fragTexCoord.y + 4.0*blurAmount)) * 0.05;

gl_FragColor = color;

Вот вывод размытия окна (обратите внимание, что это только горизонтальное размытие, но этого может быть достаточно для того, что я хочу): alt  текст


person Brian    schedule 04.12.2010    source источник


Ответы (3)


Этот шейдер должен запускаться дважды, чтобы он работал, то, что вы называете blurSize, должно быть vec2, а значение этого должно быть vec2(0, 1.0/height) для вертикального размытия и vec2(1.0/width, 0) для горизонтального размытия.

См. http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=240334

Идея двухпроходного размытия заключается в том, что оно значительно сократит количество обращений к текстуре и, надеюсь, увеличит скорость. Двухпроходное размытие с размером ядра 7x7 потребует 14 поисковых запросов текстуры, но если это выполняется во вложенном цикле, вам потребуется 49 поисковых запросов текстуры.

person Daniel    schedule 04.12.2010
comment
Да, я думаю, что это была моя ошибка в адаптации кода. Спасибо. Я думаю, что мне действительно не нужен полный гауссов для моих целей, поэтому простое размытие прямоугольника может работать достаточно хорошо и быть быстрее. - person Brian; 05.12.2010

Выполнение двух или более проходов размытия по рамке улучшает качество почти до размытия по Гауссу, сохраняя при этом производительность на относительно высоком уровне. Размытие рамки также можно относительно легко ускорить. Взгляните на http://web.archive.org/web/20060718054020/http://www.acm.uiuc.edu/siggraph/workshops/wjarosz_convolution_2001.pdf

В любом случае, в большинстве движущихся сцен вам может сойти с рук рендеринг с более низкой точностью, чем это может показаться очевидным на скриншотах.

person fzwo    schedule 10.12.2010

Непонятно, что именно делает ваш код. Вы используете texture2D, который предлагает 2D-фильтр. Тем не менее, ваша матрица свертки имеет одно измерение, и вы зацикливаетесь только один раз. Я могу ошибаться, но кажется, что вы применяете размытие по диагонали. Если он предназначен для 2D-фильтра, вам понадобятся два (вложенных) цикла для x и y соответственно, чтобы покрыть 2D-область.

А насчет переменной blurSize - ее имя немного вводит в заблуждение. Размер размытия зависит от вашей матрицы свертки. У вас 7 пикселей в ширину. Это определяет размер. Переменная больше похожа на «силу» размытия, которая может только ослабить эффект матрицы свертки. Если задать слишком высокое значение, возникнут артефакты.

Я не эксперт и не писал никаких пиксельных шейдеров, кроме мандельбротовского "hello world". Если я не ошибаюсь, то шейдер размытия один из худших для ускорения. Большинство размытий в реальном времени, которые я видел, были box-blurs. Попробуйте перенести какой-нибудь код отсюда: поток разработчиков.

person Rekin    schedule 04.12.2010
comment
Я решил попробовать размытие коробки. Кажется, работает довольно хорошо, но я еще не пробовал его на своем iPad, чтобы убедиться, что он достаточно быстр, но выглядит довольно хорошо. Я опубликую код в своем ответе выше. Спасибо - person Brian; 05.12.2010