W odniesieniu do tego tematu z powodzeniem zaimplementowałem filtr Sobel Edge Detection w GLSL. Oto kod modułu cieniującego fragmentu filtra:
#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);
}
A oto wynik sześcianu z wykrywaniem krawędzi Sobela:
Jeśli powiększysz obraz, zobaczysz, że jest dużo „szumu” wytwarzanego przez Sobela: Szare poziome paski na całej scenie z powodu niebiesko-białego gradientu. Ponadto lekkie stożki wytwarzają niechciany wzór na kostce. Czarne krawędzie po lewej stronie sześcianu również wydają się blaknąć z powodu jasnego stożka po lewej stronie sześcianu.
Przeczytałem więc ten artykuł, w którym stwierdzono, że obraz należy najpierw skalować w skali szarości i użyć filtru rozmycia Gaussa, aby krawędzie były bardziej widoczne. Na dole tego artykułu znajduje się również filtr wykrywania krawędzi kanistych, który wydaje się dawać lepsze wyniki.
Teraz mam dwa pytania:
Czy poniższe kroki są prawidłowe, aby uzyskać najlepsze możliwe wyniki wykrywania krawędzi:
- Skala szarości
- Rozmycie Gaussa
- Wykrywanie krawędzi Sobel / Canny
Jeśli tak, to jak połączyć oryginalny obraz z przetworzonym obrazem? Po przetworzeniu powyższych kroków otrzymuję obraz całkowicie czarny z białymi krawędziami lub odwrotnie. Jak powinienem umieścić krawędzie na moim oryginalnym obrazie / teksturze?
Dzięki za pomoc!
źródło
Odpowiedzi:
Najlepsze wyniki silnie zależą od przypadku użycia. Zależą również od tego, jaki efekt chcesz osiągnąć. Sobel to tylko filtr wykrywający zbocze: zbocza będą zależeć od sygnału wejściowego, wybór tego sygnału zależy od Ciebie.
Tutaj używasz kolorowego obrazu jako danych wejściowych, a filtr słusznie wykrywa słabe krawędzie w niebieskim gradiencie, podczas gdy krawędzie sześcianu zostają przerwane, gdy jego kolor jest zbyt blisko od koloru tła.
Ponieważ zakładam, że twój program jest również odpowiedzialny za rysowanie kostki, masz dostęp do innych informacji, które możesz przekazać do filtra Sobel. Na przykład głębokość i normalne są dobrymi kandydatami do wykrywania krawędzi. Można do tego wykorzystać albedo przed oświetleniem. Przetestuj przy użyciu różnych danych wejściowych i zdecyduj, których użyć w zależności od uzyskanych wyników.
Jeśli chodzi o pytanie dotyczące sposobu łączenia informacji o krawędziach, sugeruję
g
trochę przefiltrować przed użyciem. Następnie można go użyć do interpolacji między kolorem oryginalnym a pożądanym kolorem krawędzi.Na przykład możesz spróbować czegoś takiego:
Uzupełnienie
Aby użyć głębokości lub normalnych, musisz zapisać je w teksturze, jeśli nie zostało to jeszcze zrobione. Podczas tworzenia bufora klatek dla zwykłego przejścia renderowania można do niego dołączać różne tekstury (patrz
glFramebufferTexture2D
) i zapisywać do nich inne informacje niż tylko kolor sceny.Jeśli dołączysz teksturę głębokości (za pomocą
GL_DEPTH_ATTACHMENT
), zostanie ona użyta automatycznie do głębokości. Jeśli dołączysz jedną lub kilka tekstur kolorów (za pomocąGL_COLOR_ATTACHMENTi
), możesz do nich pisać, deklarując kilka wyjść do shaderów fragmentów (kiedyś tak byłogl_FragData
; w obu przypadkach, patrzglDrawBuffers
).Aby uzyskać więcej informacji na ten temat, wyszukaj temat „Multi Render Target” (MRT).
źródło