GLSL - sfocatura gaussiana a un passaggio


18

È possibile implementare lo shader di frammenti per eseguire la sfocatura gaussiana a un passaggio? Ho trovato molta implementazione della sfocatura a due passaggi (gaussiana e box sfocatura):

e così via.

Ho pensato di implementare la sfocatura gaussiana come convoluzione (in effetti è la convoluzione, gli esempi sopra sono solo approssimazioni):

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

Risposte:


33

Sì, puoi implementare la sfocatura gaussiana in un passaggio, campionando tutti i n ^ 2 pixel nel kernel (per la larghezza del kernel n). Di solito è più veloce eseguirlo su righe e colonne in due passaggi, poiché da allora hai O (n) pixel da campionare anziché O (n ^ 2). Questa non è un'approssimazione, poiché la sfocatura gaussiana è matematicamente separabile.


1
Ecco un Blur Shader a passaggio singolo piacevole e flessibile: shadertoy.com/view/XdfGDH
Ray Hulha,

7

Il trucco per una rapida sfocatura gaussiana con GLSL è trarre vantaggio dal fatto che la GPU fornisce interpolazione lineare nell'hardware. Pertanto, puoi effettivamente campionare quattro pixel 2D con un singolo prefetch o otto voxel 3D. Decidendo dove campionare è possibile ponderare l'output. Il riferimento definitivo è il "Filtro rapido per texture di terzo ordine" di Sigg e Hadwiger che puoi trovare online.

Per una spiegazione leggibile, consultare la pagina Web "Sfocatura gaussiana efficiente con campionamento lineare". Come notato, poiché la sfocatura gaussiana è separabile con kernel ampi, è più efficiente eseguire un passaggio per dimensione.

Tuttavia, puoi anche usare questo trucco per approssimare un gaussiano con un kernel stretto in un unico passaggio. Nell'esempio seguente emulo il kernel 3D con la porzione superiore = [1 2 1; 2 4 2; 1 2 1]; fetta centrale = [2 4 2; 4 8 4; 2 4 2]; fetta inferiore = [1 2 1; 2 4 2; 1 2 1]. Campionando +/- 0,5 voxel in ogni dimensione, lo fai con solo 8 recuperi di trama anziché 27. Lo sto dimostrando in GLSL come file shader MRIcroGL: basta rilasciare lo script di seguito come "a.txt" e inserirlo in Cartella "Shader" di MRIcroGL. Quando riavvii il programma, vedrai l'immagine del cast cast sfocata. Fare clic sulla casella di controllo "doBlur" per attivare e disattivare la sfocatura. Utilizzo della GPU Intel integrata nel mio laptop e "chris_t1" immagine fornita con MRIcroGL Ottengo 70 fps senza sfocatura (1 recupero trama) e 21 fps con sfocatura (8 recuperi). La maggior parte del codice è solo un classico lanciatore di raggi, il condizionale "doBlur" incapsula la tua domanda.

Segue il file //-------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;
}

2
Sfocatura gaussiana efficiente con campionamento lineare di Daniel Rákos (nota anche il commento di Christian Cann Schuldt Jensen).
Benji XVI,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.