OpenGL GLSL - Filtro de detección de bordes Sobel

9

Con respecto a este tema , he implementado con éxito el filtro de detección de bordes Sobel en GLSL. Aquí está el código del sombreador de fragmentos del filtro:

#version 330 core
in vec2 TexCoords;
out vec4 color;

uniform sampler2D screenTexture;

mat3 sx = mat3( 
    1.0, 2.0, 1.0, 
    0.0, 0.0, 0.0, 
   -1.0, -2.0, -1.0 
);
mat3 sy = mat3( 
    1.0, 0.0, -1.0, 
    2.0, 0.0, -2.0, 
    1.0, 0.0, -1.0 
);

void main()
{
    vec3 diffuse = texture(screenTexture, TexCoords.st).rgb;
    mat3 I;
    for (int i=0; i<3; i++) {
        for (int j=0; j<3; j++) {
            vec3 sample  = texelFetch(screenTexture, ivec2(gl_FragCoord) + ivec2(i-1,j-1), 0 ).rgb;
            I[i][j] = length(sample); 
    }
}

float gx = dot(sx[0], I[0]) + dot(sx[1], I[1]) + dot(sx[2], I[2]); 
float gy = dot(sy[0], I[0]) + dot(sy[1], I[1]) + dot(sy[2], I[2]);

float g = sqrt(pow(gx, 2.0)+pow(gy, 2.0));
color = vec4(diffuse - vec3(g), 1.0);
} 

Y aquí está el resultado de un cubo con detección de bordes Sobel:

Cubo con filtro de detección de bordes Sobel

Si amplía la imagen, verá que Sobel produce mucho "ruido": hay franjas horizontales grises en toda la escena debido al gradiente azul / blanco. Además, los conos de luz producen un patrón no deseado en el cubo. Los bordes negros a la izquierda del cubo también parecen desvanecerse debido al cono de luz en la mitad izquierda del cubo.

Entonces leí este artículo que decía que primero se debería escalar la imagen en escala de grises y usar un filtro de desenfoque gaussiano para hacer que los bordes sean más aparentes. Al final del artículo, también está el filtro de detección de bordes astutos que parece producir mejores resultados.

Ahora tengo dos preguntas:

  1. ¿Son correctos los siguientes pasos para producir los mejores resultados posibles de detección de bordes?

    • Escala de grises
    • Desenfoque gaussiano
    • Detección de bordes Sobel / Canny
  2. En caso afirmativo, ¿cómo fusionaría la imagen original con la imagen procesada? Quiero decir, después de procesar los pasos indicados anteriormente, obtengo una imagen que es completamente negra con bordes blancos o viceversa. ¿Cómo pondría los bordes en mi imagen / textura original?

¡Gracias por tu ayuda!

enne87
fuente
Me pregunto si podría obtener los resultados que desea utilizando la bandera de borde de OpenGL de alguna manera. Parece que solo los bordes entre vértices con la bandera de borde activada se dibujan cuando está en modo de línea. Hay una descripción aquí . (No lo he usado yo mismo o publicaría un ejemplo)
User1118321

Respuestas:

9
  1. Los mejores resultados dependen en gran medida de su caso de uso. También dependen del efecto que quieras lograr. Sobel es solo un filtro de detección de bordes: los bordes dependerán de la señal de entrada, la elección de esa señal de entrada depende de usted.

    Aquí está utilizando la imagen en color como entrada, y el filtro detecta correctamente los bordes débiles en el degradado azul, mientras que los bordes del cubo se interrumpen cuando su color está demasiado cerca del color de fondo.

    Como supongo que su programa también es responsable de dibujar el cubo, tiene acceso a otra información que puede alimentar a su filtro Sobel. Por ejemplo, la profundidad y las normales son buenas candidatas para la detección de bordes. El albedo antes de la iluminación podría estar acostumbrado. Pruebe con diferentes entradas y decida cuáles usar según los resultados que obtenga.

  2. Con respecto a su pregunta sobre cómo combinar la información de borde, le sugiero que filtre gun poco antes de usarla. Luego puede usarlo para interpolar entre el color original y el color del borde deseado.

    Por ejemplo, podrías probar algo como esto:

    float g = sqrt(pow(gx, 2.0)+pow(gy, 2.0));

    // Try different values and see what happens
    g = smoothstep(0.4, 0.6, g);

    vec3 edgeColor = vec3(1., 0., 0.2);
    color = vec4(mix(diffuse, edgeColor, g), 1.);

Apéndice

Para usar profundidad o normales, necesitaría guardarlas en una textura si eso no se ha hecho ya. Cuando crea el búfer de cuadro para su pase de representación regular, puede adjuntarle varias texturas (ver glFramebufferTexture2D) y escribirles otra información que no sea solo el color de la escena.

Si adjunta una textura de profundidad (con GL_DEPTH_ATTACHMENT), se usará automáticamente para la profundidad. Si adjunta una o varias texturas de color (con GL_COLOR_ATTACHMENTi), puede escribirles declarando varias salidas a sus sombreadores de fragmentos (esto solía hacerse con gl_FragData; de cualquier manera, ver glDrawBuffers).

Para obtener más información sobre el tema, busque "Multi Render Target" (MRT).

Julien Guertault
fuente
Buena información, gracias Julien. Una pregunta más: ¿Cómo podría usar la profundidad o la información normal en mi sombreador de fragmentos? Todavía soy bastante nuevo con GLSL y, por lo tanto, no tengo idea de cómo incluir los valores en el algoritmo Sobel.
enne87
2
Cuando crea el framebuffer para su pase de renderizado, puede adjuntarle más de una textura. Si adjunta una textura de profundidad, se usará automáticamente para la profundidad. Si adjunta otra textura de color, puede escribirla por separado (consulte opengl.org/sdk/docs/man/html/glDrawBuffers.xhtml ), una función llamada "Objetivo de renderizado múltiple" (MRT).
Julien Guertault
@ enne87: encantado de ayudar. :)
Julien Guertault
@trichoplax: gracias por la sugerencia; hecho.
Julien Guertault