GLSL - desfoque gaussiano de uma passagem

18

É possível implementar o shader de fragmentos para fazer o desfoque gaussiano de uma passagem? Eu encontrei muita implementação do blur de duas passagens (gaussian e box blur):

e assim por diante.

Eu estive pensando em implementar o desfoque gaussiano como convolução (na verdade, é a convolução, os exemplos acima são apenas aproximações):

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

martin pilch
fonte

Respostas:

33

Sim, você pode implementar o desfoque Gaussiano em uma passagem, amostrando todos os n ^ 2 pixels no kernel (para a largura do kernel n). Geralmente é mais rápido executá-lo nas linhas e colunas em duas passagens, desde então você tem O (n) pixels para amostrar em vez de O (n ^ 2). Isso não é uma aproximação, pois o desfoque gaussiano é matematicamente separável.

Nathan Reed
fonte
11
Aqui é um bom, flexível única passagem borrão Shader: shadertoy.com/view/XdfGDH
Ray Hulha
7

O truque para o desfoque gaussiano rápido com o GLSL é aproveitar o fato de que a GPU fornece interpolação linear no hardware. Portanto, você pode testar efetivamente quatro pixels 2D com uma única pré-busca ou oito voxels 3D. Ao decidir onde coletar amostras, você pode ponderar a saída. A referência definitiva é a "Filtragem rápida de textura de terceira ordem" de Sigg e Hadwiger, que você pode encontrar on-line.

Para uma explicação legível, localize a página da web "Desfoque gaussiano eficiente com amostragem linear". Como observado, como o desfoque gaussiano é separável com núcleos largos, é mais eficiente fazer uma passagem por dimensão.

No entanto, você também pode usar esse truque para aproximar um gaussiano com um núcleo apertado em uma única passagem. No exemplo abaixo, eu emulo o kernel 3D com a fatia superior = [1 2 1; 2 4 2; 1 2 1]; fatia do meio = [2 4 2; 4 8 4; 2 4 2]; fatia inferior = [1 2 1; 2 4 2; 1 2 1] Ao amostrar +/- 0,5 voxels em cada dimensão, você faz isso com apenas 8 busca de textura em vez de 27. Estou demonstrando isso no GLSL como um arquivo de sombreador MRIcroGL - basta salvar o script abaixo como "a.txt" e colocá-lo em Pasta "Shader" do MRIcroGL. Ao reiniciar o programa, você verá a sua imagem com raios fundidos embaçada. Clicar na caixa de seleção "doBlur" ativa e desativa o desfoque. Usando minha GPU Intel integrada no meu laptop e o "chris_t1" Na imagem que acompanha o MRIcroGL, obtenho 70fps sem desfocagem (1 busca de textura) e 21fps com desfocagem (8 buscas). A maior parte do código é apenas um lançador de raios clássico, o condicional "doBlur" encapsula sua pergunta.

O arquivo //-------a.txt segue

//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
fonte
2
Desfoque gaussiano eficiente com amostragem linear de Daniel Rákos (observe também o comentário de Christian Cann Schuldt Jensen).
Benji XVI