OpenGL GLSL - Filtro de detecção de borda de Sobel

9

Com relação a este tópico, implementei com êxito o filtro Sobel Edge Detection no GLSL. Aqui está o código do shader de fragmento do 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);
} 

E aqui está o resultado de um cubo com detecção de borda Sobel:

Cubo com filtro de detecção de borda Sobel

Se você ampliar a imagem, verá que há muito "ruído" produzido por Sobel: existem faixas horizontais cinzentas em toda a cena devido ao gradiente de azul / branco. Além disso, os cones de luz produzem um padrão indesejado no cubo. As bordas pretas à esquerda do cubo também parecem desaparecer devido ao cone de luz na metade esquerda do cubo.

Então, li este artigo que afirmava que a imagem deveria ser em escala de cinza primeiro e usar um filtro de desfoque gaussiano para tornar as bordas mais aparentes. Na parte inferior do artigo, há também o filtro de detecção de borda inteligente que parece produzir melhores resultados.

Agora eu tenho duas perguntas:

  1. As etapas a seguir estão corretas para produzir os melhores resultados possíveis de detecção de borda:

    • Escala de cinza
    • Gaussian Blur
    • Detecção de borda de Sobel / Canny
  2. Se sim, como mesclar a imagem original com a imagem processada? Quero dizer, depois de processar as etapas mencionadas acima, recebo uma imagem totalmente preta com bordas brancas ou vice-versa. Como eu colocaria as bordas na minha imagem / textura original?

Obrigado pela ajuda!

enne87
fonte
Gostaria de saber se você pode obter os resultados desejados utilizando o sinalizador de borda do OpenGL de alguma forma. Parece que apenas as arestas entre os vértices com a bandeira da aresta ativada são desenhadas no modo de linha. Há uma descrição aqui . (Eu não usei-me ou eu ia postar um exemplo.)
user1118321

Respostas:

9
  1. Os melhores resultados dependem fortemente do seu caso de uso. Eles também dependem do efeito que você deseja alcançar. Sobel é apenas um filtro de detecção de borda: as bordas dependerão do sinal de entrada, escolhendo que o sinal de entrada depende de você.

    Aqui você está usando a imagem colorida como entrada, e o filtro detecta corretamente as bordas fracas no gradiente azul, enquanto as bordas do cubo são interrompidas onde a cor está muito próxima da cor de fundo.

    Como presumo que seu programa também seja responsável pelo desenho do cubo, você tem acesso a outras informações que podem alimentar no seu filtro Sobel. Por exemplo, profundidade e normais são bons candidatos para a detecção de bordas. O albedo antes da iluminação poderia ser usado. Teste com entradas diferentes e decida quais usar, dependendo dos resultados obtidos.

  2. Em relação à sua pergunta sobre como combinar as informações da borda, sugiro que você filtre gum pouco antes de usá-las. Em seguida, você pode usá-lo para interpolar entre a cor original e a cor desejada da borda.

    Por exemplo, você pode tentar algo como isto:

    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.);

Termo aditivo

Para usar profundidade ou normais, você precisará salvá-las em uma textura se isso ainda não tiver sido feito. Ao criar o buffer de quadro para o seu passe de renderização normal, você pode anexar várias texturas a ele (consulte glFramebufferTexture2D) e gravar outras informações para eles além da cor da cena.

Se você anexar uma textura de profundidade (com GL_DEPTH_ATTACHMENT), ela será usada automaticamente para profundidade. Se você anexar uma ou várias texturas de cores (com GL_COLOR_ATTACHMENTi), poderá escrevê-las declarando várias saídas para seus shaders de fragmentos (isso costumava ser feito gl_FragData; de qualquer forma, consulte glDrawBuffers).

Para obter mais informações sobre o tópico, procure "Multi Render Target" (MRT).

Julien Guertault
fonte
Boa informação, obrigado Julien. Mais uma pergunta: como eu poderia usar a profundidade ou informações normais no meu shader de fragmento? Ainda sou bastante novo com o GLSL e, portanto, não tenho idéia de como incluir os valores no algoritmo de Sobel.
enne87
2
Ao criar o buffer de estrutura para o seu passe de renderização, você pode anexar mais de uma textura a ele. Se você anexar uma textura de profundidade, ela será usada automaticamente para profundidade. Você anexa outra textura de cor que pode ser gravada separadamente (consulte opengl.org/sdk/docs/man/html/glDrawBuffers.xhtml ), um recurso chamado "Multi Render Target" (MRT).
Julien Guertault
@ enne87: Feliz em ajudar. :)
Julien Guertault
@trichoplax: obrigado pela sugestão; feito.
Julien Guertault