GLSL - desenfoque gaussiano de una pasada

18

¿Es posible implementar fragment shader para hacer un desenfoque gaussiano de una pasada? He encontrado mucha implementación de desenfoque de dos pasos (desenfoque gaussiano y de cuadro):

y así.

He estado pensando en implementar el desenfoque gaussiano como convolución (de hecho, es la convolución, los ejemplos anteriores son solo aproximaciones):

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

Martin Pilch
fuente

Respuestas:

33

Sí, puede implementar el desenfoque gaussiano en una pasada, muestreando todos los n ^ 2 píxeles en el núcleo (para el ancho del núcleo n). Por lo general, es más rápido ejecutarlo en las filas y columnas en dos pasadas, ya que entonces tiene O (n) píxeles para muestrear en lugar de O (n ^ 2). Esto no es una aproximación, ya que el desenfoque gaussiano es matemáticamente separable.

Nathan Reed
fuente
1
Aquí hay un bonito, flexible pase único Blur Shader: shadertoy.com/view/XdfGDH
Ray Hulha
7

El truco para el desenfoque gaussiano rápido con GLSL es aprovechar el hecho de que la GPU proporciona interpolación lineal en hardware. Por lo tanto, puede muestrear eficazmente cuatro píxeles 2D con una sola captación previa u ocho voxels 3D. Al decidir dónde tomar muestras, puede ponderar la salida. La referencia definitiva es el "Filtrado rápido de texturas de tercer orden" de Sigg y Hadwiger que puede encontrar en línea.

Para obtener una explicación legible, busque la página web "Desenfoque gaussiano eficiente con muestreo lineal". Como se señaló, dado que el desenfoque gaussiano es separable con núcleos anchos, es más eficiente hacer una pasada por dimensión.

Sin embargo, también puede usar este truco para aproximar un gaussiano con un núcleo ajustado en una sola pasada. En el siguiente ejemplo, emulo el núcleo 3D con el segmento superior = [1 2 1; 2 4 2; 1 2 1]; rebanada del medio = [2 4 2; 4 8 4; 2 4 2]; rebanada inferior = [1 2 1; 2 4 2; 1 2 1]. Al muestrear +/- 0.5 vóxeles en cada dimensión, hace esto con solo 8 captaciones de textura en lugar de 27. Estoy demostrando esto en GLSL como un archivo de sombreador MRIcroGL: simplemente suelte el guión a continuación como "a.txt" y colóquelo en Carpeta "Shader" de MRIcroGL. Cuando reinicie el programa, verá su imagen de emisión de rayos borrosa. Al hacer clic en la casilla de verificación "doBlur" se activa y desactiva el desenfoque. Uso de mi GPU Intel integrada en mi computadora portátil y el "chris_t1" imagen que viene con MRIcroGL Obtengo 70 fps sin desenfoque (1 búsqueda de textura) y 21 fps con desenfoque (8 descargas). La mayor parte del código es solo un clásico lanzador de rayos, el condicional "doBlur" encapsula su pregunta.

//-------a.txt sigue el archivo

//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;
}
usuario1677899
fuente
2
Desenfoque gaussiano eficiente con muestreo lineal de Daniel Rákos (también tenga en cuenta el comentario de Christian Cann Schuldt Jensen).
Benji XVI