GLSL - jednoprzebiegowe rozmycie gaussowskie

18

Czy możliwe jest zaimplementowanie modułu cieniującego fragmenty w celu uzyskania rozmycia gaussowskiego? Znalazłem wiele implementacji rozmycia dwuprzebiegowego (rozmycie gaussowskie i rozmycie ramkowe):

i tak dalej.

Myślałem o zastosowaniu rozmycia gaussowskiego jako splotu (w rzeczywistości jest to splot, powyższe przykłady są jedynie przybliżeniami):

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

martin pilch
źródło

Odpowiedzi:

33

Tak, można wdrożyć rozmycie Gaussa w jednym przejściu, przez próbkowanie wszystkich n ^ 2 piksele w jądrze (dla jądra szerokość n). Zwykle szybciej jest uruchamiać go w wierszach i kolumnach w dwóch przejściach, ponieważ od tego czasu masz próbkę O (n) pikseli niż O (n ^ 2). Nie jest to przybliżone, ponieważ rozmycie gaussowskie można matematycznie rozdzielić.

Nathan Reed
źródło
1
Oto ładny, elastyczny, jednoprzebiegowy moduł
Ray Hulha
7

Sztuką szybkiego rozmycia gaussowskiego za pomocą GLSL jest wykorzystanie faktu, że GPU zapewnia interpolację liniową w sprzęcie. Dlatego możesz skutecznie próbkować cztery piksele 2D za pomocą pojedynczego pobrania wstępnego lub ośmiu wokseli 3D. Decydując, gdzie próbkować, możesz zważyć wynik. Ostatecznym odniesieniem jest „Szybkie filtrowanie tekstur trzeciego rzędu” Sigga i Hadwigera, które można znaleźć w Internecie.

Aby uzyskać czytelne wyjaśnienie, należy znaleźć stronę internetową „Efektywne rozmycie gaussowskie z próbkowaniem liniowym”. Jak zauważono, ponieważ rozmycie gaussowskie można rozdzielić szerokimi ziarnami, najskuteczniejsze jest wykonanie jednego przejścia na wymiar.

Możesz jednak użyć tej sztuczki, aby zbliżyć Gaussa z ciasnym jądrem w jednym przejściu. W poniższym przykładzie emuluję jądro 3D z górnym wycięciem = [1 2 1; 2 4 2; 1 2 1]; środkowy plasterek = [2 4 2; 4 8 4; 2 4 2]; dolny plasterek = [1 2 1; 2 4 2; 1 2 1]. Próbkując +/- 0,5 wokseli w każdym wymiarze, robisz to za pomocą 8 pobrań tekstur zamiast 27. Pokazuję to w GLSL jako plik modułu cieniującego MRIcroGL - po prostu upuść, zapisz poniższy skrypt jako „a.txt” i umieść go w Folder „Shader” MRIcroGL. Po ponownym uruchomieniu programu zobaczysz rozmazany obraz. Kliknięcie pola wyboru „doBlur” włącza i wyłącza rozmycie. Używanie zintegrowanego procesora graficznego Intel w laptopie i „chris_t1” obraz, który pochodzi z MRIcroGL Dostaję 70 klatek na sekundę bez rozmycia (1 pobieranie tekstury) i 21 klatek na sekundę z rozmyciem (8 pobrań). Większość kodu to tylko klasyczny rayaster, warunek „doBlur” zawiera w sobie twoje pytanie.

Następuje plik //-------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;
}
użytkownik1677899
źródło
2
Wydajne rozmycie gaussowskie z liniowym próbkowaniem Daniela Rákosa (zauważ także komentarz Christiana Canna Schuldta Jensena).
Benji XVI