GLSL - flou gaussien en un seul passage

18

Il est possible d'implémenter un fragment shader pour faire un flou gaussien en une passe? J'ai trouvé beaucoup d'implémentation de flou à deux passes (gaussien et flou de boîte):

etc.

J'ai pensé à implémenter le flou gaussien comme convolution (en fait, c'est la convolution, les exemples ci-dessus ne sont que des approximations):

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

martin pilch
la source

Réponses:

33

Oui, vous pouvez implémenter le flou gaussien en une seule passe, en échantillonnant tous les n ^ 2 pixels du noyau (pour la largeur du noyau n). Il est généralement plus rapide de l'exécuter sur les lignes et les colonnes en deux passes, car vous avez alors O (n) pixels à échantillonner plutôt que O (n ^ 2). Ce n'est pas une approximation, car le flou gaussien est mathématiquement séparable.

Nathan Reed
la source
1
Voici un joli Blur Shader en une seule passe: shadertoy.com/view/XdfGDH
Ray Hulha
7

L'astuce pour un flou gaussien rapide avec GLSL est de tirer parti du fait que le GPU fournit une interpolation linéaire dans le matériel. Par conséquent, vous pouvez effectivement échantillonner quatre pixels 2D avec une seule prélecture ou huit voxels 3D. En décidant où échantillonner, vous pouvez pondérer la sortie. La référence définitive est le "Filtrage rapide des textures du troisième ordre" de Sigg et Hadwiger que vous pouvez trouver en ligne.

Pour une explication lisible, consultez la page Web "Flou gaussien efficace avec échantillonnage linéaire". Comme indiqué, comme le flou gaussien est séparable avec des noyaux larges, il est plus efficace de faire un passage par dimension.

Cependant, vous pouvez également utiliser cette astuce pour approximer une gaussienne avec un noyau serré en une seule passe. Dans l'exemple ci-dessous, j'émule le noyau 3D avec la tranche supérieure = [1 2 1; 2 4 2; 1 2 1]; tranche médiane = [2 4 2; 4 8 4; 2 4 2]; tranche inférieure = [1 2 1; 2 4 2; 1 2 1]. En échantillonnant +/- 0,5 voxels dans chaque dimension, vous le faites avec seulement 8 récupérations de texture au lieu de 27. Je le démontre dans GLSL en tant que fichier de shader MRIcroGL - il suffit de déposer le script ci-dessous en tant que "a.txt" et de le placer dans Dossier "Shader" de MRIcroGL. Lorsque vous relancez le programme, vous verrez votre image rayée floue. Cliquer sur la case "doBlur" active et désactive le flou. Utiliser mon processeur graphique Intel intégré dans mon ordinateur portable et le "chris_t1" image fournie avec MRIcroGL J'obtiens 70fps sans flou (1 fetch de texture) et 21fps avec flou (8 fetches). La plupart du code n'est qu'un lanceur de rayons classique, le conditionnel "doBlur" résume votre question.

//-------a.txt suit

//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
la source
2
Flou gaussien efficace avec échantillonnage linéaire par Daniel Rákos (à noter également le commentaire de Christian Cann Schuldt Jensen).
Benji XVI