GLSL - Gaußsche Unschärfe in einem Durchgang

18

Ist es möglich, Fragment-Shader zu implementieren, um eine Gaußsche Unschärfe in einem Durchgang zu erzielen? Ich habe viele Implementierungen von Two-Pass-Blur (Gauß- und Box-Blur) gefunden:

und so weiter.

Ich habe darüber nachgedacht, Gaußsche Unschärfe als Faltung zu implementieren (in der Tat ist es die Faltung, die obigen Beispiele sind nur Annäherungen):

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

Martin Pilch
quelle

Antworten:

33

Ja, Sie können die Gaußsche Unschärfe in einem Durchgang implementieren, indem Sie alle n ^ 2 Pixel im Kernel abtasten (für die Kernelbreite n). Es ist normalerweise schneller , es in zwei Durchläufen in den Zeilen und Spalten auszuführen, da Sie dann O (n) Pixel anstelle von O (n ^ 2) zum Abtasten haben. Dies ist keine Annäherung, da die Gaußsche Unschärfe mathematisch trennbar ist.

Nathan Reed
quelle
1
Hier ist ein schöner, flexibler Single-Pass-Blur-Shader: shadertoy.com/view/XdfGDH
Ray Hulha
7

Der Trick für eine schnelle Gaußsche Unschärfe bei GLSL besteht darin, die Tatsache auszunutzen, dass die GPU eine lineare Interpolation in der Hardware bietet. Daher können Sie effektiv vier 2D-Pixel mit einem einzelnen Prefetch oder acht 3D-Voxeln abtasten. Durch die Entscheidung, wo die Probe entnommen werden soll, können Sie die Ausgabe gewichten. Die endgültige Referenz ist Sigg und Hadwigers "Fast Third-Order Texture Filtering", die Sie online finden können.

Eine lesbare Erklärung finden Sie auf der Webseite "Effiziente Gaußsche Unschärfe mit linearer Abtastung". Wie bereits erwähnt, ist es am effizientesten, einen Durchgang pro Dimension durchzuführen, da die Gaußsche Unschärfe mit breiten Körnern getrennt werden kann.

Sie können diesen Trick jedoch auch verwenden, um einen Gaußschen Kern mit einem engen Kernel in einem einzigen Durchgang anzunähern. Im folgenden Beispiel emuliere ich den 3D-Kernel mit dem oberen Slice = [1 2 1; 2 4 2; 1 2 1]; mittlere Scheibe = [2 4 2; 4 8 4; 2 4 2]; Bodenscheibe = [1 2 1; 2 4 2; 1 2 1]. Indem Sie +/- 0,5 Voxel in jeder Dimension abtasten, tun Sie dies mit nur 8 statt 27 Texturabrufen. Ich demonstriere dies in GLSL als MRIcroGL-Shader-Datei - speichern Sie das Skript einfach als "a.txt" und platzieren Sie es in MRIcroGLs "Shader" -Ordner. Wenn Sie das Programm neu starten, wird Ihr Ray Cast-Bild unscharf. Durch Klicken auf das Kontrollkästchen "doBlur" wird die Unschärfe ein- und ausgeschaltet. Verwendung meiner integrierten Intel GPU in meinem Laptop und dem "chris_t1" Bild, das mit MRIcroGL geliefert wird Ich erhalte 70 fps ohne Unschärfe (1 Texturabruf) und 21 fps mit Unschärfe (8 Abrufe). Der Großteil des Codes ist nur ein klassischer Ray Caster. Die Bedingung "doBlur" kapselt Ihre Frage.

//-------a.txt Datei folgt

//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
quelle
2
Effiziente Gaußsche Unschärfe mit linearer Abtastung von Daniel Rákos (siehe auch Kommentar von Christian Cann Schuldt Jensen).
Benji XVI.,