GLSL - blur gaussian sekali jalan

18

Dimungkinkan untuk mengimplementasikan fragmen shader untuk melakukan one-pass gaussian blur? Saya telah menemukan banyak implementasi blur dua jalur (gaussian dan box blur):

dan seterusnya.

Saya telah berpikir untuk mengimplementasikan gaussian blur sebagai konvolusi (pada kenyataannya, ini adalah konvolusi, contoh-contoh di atas hanyalah perkiraan):

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

martin pilch
sumber

Jawaban:

33

Ya, Anda dapat mengimplementasikan Gaussian blur dalam satu pass, dengan mengambil sampel semua n ^ 2 piksel dalam kernel (untuk lebar kernel n). Biasanya lebih cepat untuk menjalankannya pada baris dan kolom dalam dua lintasan, karena Anda memiliki O (n) piksel untuk sampel daripada O (n ^ 2). Ini bukan perkiraan, karena Gaussian blur secara matematis dapat dipisahkan.

Nathan Reed
sumber
1
Ini adalah pass tunggal yang bagus dan fleksibel Blur Shader: shadertoy.com/view/XdfGDH
Ray Hulha
7

Trik untuk kabur Gaussian cepat dengan GLSL adalah untuk mengambil keuntungan dari kenyataan bahwa GPU menyediakan interpolasi linier dalam perangkat keras. Oleh karena itu, Anda dapat secara efektif mencicipi empat piksel 2D dengan satu prefetch atau delapan voxels 3D. Dengan memutuskan tempat sampel Anda dapat menimbang hasilnya. Referensi definitif adalah Sigg dan Hadwiger's "Fast-Order Texture Filtering" yang dapat Anda temukan online.

Untuk penjelasan yang mudah dibaca, temukan halaman web "Efficient Gaussian blur with linear sampling". Sebagaimana dicatat, karena blur Gaussian dapat dipisahkan dengan kernel yang luas, maka paling efisien untuk melakukan satu lintasan per dimensi.

Namun, Anda juga dapat menggunakan trik ini untuk memperkirakan Gaussian dengan kernel yang ketat dalam sekali operan. Pada contoh di bawah ini saya mengemulasi kernel 3D dengan slice atas = [1 2 1; 2 4 2; 1 2 1]; slice tengah = [2 4 2; 4 8 4; 2 4 2]; irisan bawah = [1 2 1; 2 4 2; 1 2 1]. Dengan mengambil sampel +/- 0,5 voxels di setiap dimensi Anda melakukan ini hanya dengan 8 tekstur mengambil daripada 27. Saya mendemonstrasikan ini dalam GLSL sebagai file shader MRIcroGL - cukup simpan save script di bawah ini sebagai "a.txt" dan letakkan di dalam Folder "Shader" MRIcroGL. Ketika Anda meluncurkan kembali program ini, Anda akan melihat gambar ray cast Anda kabur. Mengklik kotak centang "doBlur" mengaktifkan dan menonaktifkan pengaburan. Menggunakan Intel GPU terintegrasi saya di laptop saya dan "chris_t1" gambar yang datang dengan MRIcroGL saya mendapatkan 70fps tanpa kabur (1 fetch tekstur) dan 21fps dengan blurring (8 fetches). Sebagian besar kode hanyalah ray caster klasik, "doBlur" bersyarat merangkum pertanyaan Anda.

//-------a.txt mengikuti

//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;
}
pengguna1677899
sumber
2
Blur Gaussian yang efisien dengan pengambilan sampel linier oleh Daniel Rákos (juga perhatikan komentar oleh Christian Cann Schuldt Jensen).
Benji XVI