GLSL - làm mờ gaussian một lần


18

Có thể thực hiện shader mảnh để làm mờ gaussian một lần? Tôi đã tìm thấy rất nhiều việc thực hiện mờ hai chiều (gaussian và làm mờ hộp):

vân vân

Tôi đã nghĩ đến việc thực hiện mờ gaussian như tích chập (trên thực tế, đó là tích chập, các ví dụ ở trên chỉ là phép ước lượng):

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

Câu trả lời:


33

Có, bạn có thể thực hiện làm mờ Gaussian trong một lần, bằng cách lấy mẫu tất cả n ^ 2 pixel trong kernel (đối với chiều rộng kernel n). Việc chạy nó trên các hàng và cột thường nhanh hơn trong hai lần, kể từ đó bạn có các pixel O (n) để lấy mẫu thay vì O (n ^ 2). Đây không phải là một xấp xỉ, vì độ mờ của Gaussian có thể tách rời về mặt toán học.


1
Dưới đây là một đường chuyền đơn đẹp, linh hoạt Blur Shader: shadertoy.com/view/XdfGDH
Ray Hulha

7

Mẹo để làm mờ Gaussian nhanh chóng với GLSL là tận dụng thực tế là GPU cung cấp phép nội suy tuyến tính trong phần cứng. Do đó, bạn có thể lấy mẫu bốn pixel 2D một cách hiệu quả bằng một lần tải trước hoặc tám voxels 3D. Bằng cách quyết định nơi lấy mẫu, bạn có thể cân trọng lượng đầu ra. Tham chiếu dứt khoát là "Lọc kết cấu thứ tự nhanh thứ ba" của Sigg và Hadwiger mà bạn có thể tìm thấy trực tuyến.

Để giải thích có thể đọc được, hãy tìm trang web "Làm mờ hiệu quả Gaussian với lấy mẫu tuyến tính". Như đã lưu ý, vì độ mờ Gaussian có thể tách rời với các hạt rộng, nên hiệu quả nhất là thực hiện một lần cho mỗi chiều.

Tuy nhiên, bạn cũng có thể sử dụng thủ thuật này để ước tính một Gaussian với một hạt nhân chặt chẽ trong một lần chạy. Trong ví dụ dưới đây, tôi mô phỏng nhân 3D với lát trên cùng = [1 2 1; 2 4 2; 1 2 1]; lát giữa = [2 4 2; 4 8 4; 2 4 2]; lát dưới = [1 2 1; 2 4 2; 1 2 1]. Bằng cách lấy mẫu +/- 0,5 voxels trong mỗi thứ nguyên, bạn thực hiện việc này chỉ với 8 lần tải kết cấu thay vì 27. Tôi đang chứng minh điều này trong GLSL dưới dạng tệp shader MRIcroGL - chỉ cần thả lưu tập lệnh bên dưới dưới dạng "a.txt" và đặt nó vào Thư mục "Shader" của MRIcroGL. Khi bạn khởi chạy lại chương trình, bạn sẽ thấy hình ảnh chiếu tia của bạn bị mờ. Nhấp vào hộp kiểm "doBlur" để bật và tắt. Sử dụng GPU Intel tích hợp của tôi trong máy tính xách tay của tôi và "chris_t1" hình ảnh đi kèm với MRIcroGL Tôi nhận được 70 khung hình / giây mà không bị mờ (1 lần tải kết cấu) và 21 khung hình / giây với độ mờ (8 lần tải). Hầu hết các mã chỉ là một caster ray cổ điển, điều kiện "doBlur" gói gọn câu hỏi của bạn.

//-------a.txt tệp theo sau

//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
Hiệu quả làm mờ Gaussian với lấy mẫu tuyến tính của Daniel Rákos (cũng lưu ý nhận xét của Christian Cann Schuldt Jensen).
Benji XVI
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.