GLSL - однопроходное размытие по Гауссу

18

Возможно ли реализовать фрагментный шейдер, чтобы сделать однопроходное размытие по Гауссу? Я нашел много реализации двухпроходного размытия (гауссово и размытие в рамке):

и так далее.

Я думал о реализации размытия по Гауссу как свертки (на самом деле, это свертка, приведенные выше примеры являются лишь приблизительными):

http://en.wikipedia.org/wiki/Gaussian_blur

Мартин Пилч
источник

Ответы:

33

Да, вы можете реализовать размытие по Гауссу за один проход путем выборки всех n ^ 2 пикселей в ядре (для ширины ядра n). Обычно быстрее выполнить его в строках и столбцах за два прохода, поскольку тогда у вас будет O (n) пикселей для выборки, а не O (n ^ 2). Это не приближение, так как размытие по Гауссу математически отделимо.

Натан Рид
источник
1
Вот хороший, гибкий однопроходный шейдер
Рэй Хула
7

Хитрость для быстрого размытия по Гауссу с помощью GLSL заключается в том, чтобы использовать тот факт, что графический процессор обеспечивает аппаратную линейную интерполяцию. Следовательно, вы можете эффективно сэмплировать четыре 2D-пикселя с помощью одной предварительной выборки или восьми 3D-вокселей. Решив, где взять образец, вы можете взвесить результат. Точная ссылка - «Быстрая фильтрация текстур третьего порядка» Сигга и Хадвигера, которую вы можете найти в Интернете.

Для наглядного объяснения найдите веб-страницу «Эффективное размытие по Гауссу с линейной выборкой». Как уже отмечалось, поскольку размытие по Гауссу отделимо от широких ядер, наиболее эффективно сделать один проход на измерение.

Однако вы также можете использовать этот трюк для аппроксимации гауссиана с плотным ядром за один проход. В приведенном ниже примере я эмулирую трехмерное ядро ​​с верхним срезом = [1 2 1; 2 4 2; 1 2 1]; средний срез = [2 4 2; 4 8 4; 2 4 2]; нижний срез = [1 2 1; 2 4 2; 1 2 1]. Сэмплируя +/- 0,5 вокселей в каждом измерении, вы делаете это всего с 8 выборками текстур, а не 27. Я демонстрирую это в GLSL как файл шейдера MRIcroGL - просто удалите сохраненный ниже скрипт как «a.txt» и поместите его в Папка "Шейдер" MRIcroGL. Когда вы перезапустите программу, вы увидите, что ваше изображение луча размыто. Нажатие на флажок «doBlur» включает и выключает размытие. Использование моего интегрированного графического процессора Intel в моем ноутбуке и "chris_t1" Изображение, которое поставляется с MRIcroGL Я получаю 70 кадров в секунду без размытия (1 выборка текстуры) и 21 кадров в секунду с размытием (8 выборок). Большая часть кода - просто классический лучник, условное выражение doBlur инкапсулирует ваш вопрос.

//-------a.txt файл следует

//pref
doBlur|bool|true
//vert
void main() {
    gl_TexCoord[1] = gl_MultiTexCoord1;
    gl_Position = ftransform();
}
//frag
uniform int loops;
uniform float stepSize, sliceSize, viewWidth, viewHeight;
uniform sampler3D intensityVol;
uniform sampler2D backFace;
uniform vec3 clearColor;
uniform bool doBlur;
void main() {
    // get normalized pixel coordinate in view port (e.g. [0,1]x[0,1])
    vec2 pixelCoord = gl_FragCoord.st;
    pixelCoord.x /= viewWidth;
    pixelCoord.y /= viewHeight; 
    // starting position of the ray is stored in the texture coordinate
    vec3 start = gl_TexCoord[1].xyz;
    vec3 backPosition = texture2D(backFace,pixelCoord).xyz;
    vec3 dir = backPosition - start;
    float len = length(dir);
    dir = normalize(dir);
    vec3 deltaDir = dir * stepSize;
    vec4 colorSample,colAcc = vec4(0.0,0.0,0.0,0.0);
    float lengthAcc = 0.0;
    float opacityCorrection = stepSize/sliceSize;
    //ray dithering http://marcusbannerman.co.uk/index.php/home/42-articles/97-vol-render-optimizations.html
    vec3 samplePos = start.xyz + deltaDir* (fract(sin(gl_FragCoord.x * 12.9898 + gl_FragCoord.y * 78.233) * 43758.5453));
    //offset to eight locations surround target: permute top/bottom, anterior/posterior, left/right
    float dx = 0.5; //distance from target voxel
    vec3 vTAR = vec3( dx, dx, dx)*sliceSize;
    vec3 vTAL = vec3( dx, dx,-dx)*sliceSize;
    vec3 vTPR = vec3( dx,-dx, dx)*sliceSize;
    vec3 vTPL = vec3( dx,-dx,-dx)*sliceSize;
    vec3 vBAR = vec3(-dx, dx, dx)*sliceSize;
    vec3 vBAL = vec3(-dx, dx,-dx)*sliceSize;
    vec3 vBPR = vec3(-dx,-dx, dx)*sliceSize;
    vec3 vBPL = vec3(-dx,-dx,-dx)*sliceSize;
    for(int i = 0; i < loops; i++) {
        if (doBlur) {
            colorSample = texture3D(intensityVol,samplePos+vTAR);
            colorSample += texture3D(intensityVol,samplePos+vTAL);
            colorSample += texture3D(intensityVol,samplePos+vTPR);
            colorSample += texture3D(intensityVol,samplePos+vTPL);
            colorSample += texture3D(intensityVol,samplePos+vBAR);
            colorSample += texture3D(intensityVol,samplePos+vBAL);
            colorSample += texture3D(intensityVol,samplePos+vBPR);
            colorSample += texture3D(intensityVol,samplePos+vBPL);
            colorSample *= 0.125; //average of 8 sample locations
        } else
            colorSample = texture3D(intensityVol,samplePos);
        colorSample.a = 1.0-pow((1.0 - colorSample.a), opacityCorrection);      
        colorSample.rgb *= colorSample.a; 
        //accumulate color
        colAcc = (1.0 - colAcc.a) * colorSample + colAcc;
        samplePos += deltaDir;
        lengthAcc += stepSize;
        // terminate if opacity > 95% or the ray is outside the volume
        if ( lengthAcc >= len || colAcc.a > 0.95 ) break;
    }
    colAcc.rgb = mix(clearColor,colAcc.rgb,colAcc.a);
    gl_FragColor = colAcc;
}
user1677899
источник
2
Эффективное размытие по Гауссу с линейной выборкой Даниэля Ракоса (также обратите внимание на комментарий Кристиана Канна Шульдта Йенсена).
Бенджи XVI