OpenGL GLSL - фильтр обнаружения краев Собеля

9

Что касается этой темы, я успешно внедрил фильтр Sobel Edge Detection в GLSL. Вот фрагмент кода шейдера фильтра:

#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);
} 

И вот результат куба с обнаружением края Собеля:

Куб с фильтром обнаружения краев Собеля

Если вы увеличите изображение, вы увидите, что Sobel производит много «шума»: по всей сцене присутствуют серые горизонтальные полосы из-за сине-белого градиента. Кроме того, световые конусы создают нежелательный узор на кубе. Черные края слева от куба также кажутся блеклыми из-за светового конуса на левой половине куба.

Поэтому я прочитал эту статью, в которой говорилось, что сначала нужно откорректировать изображение в оттенках серого и использовать фильтр размытия по Гауссу, чтобы сделать края более заметными. В нижней части статьи также есть фильтр обнаружения краев, который, кажется, дает лучшие результаты.

Теперь у меня есть два вопроса:

  1. Правильны ли следующие шаги для получения наилучших результатов обнаружения края:

    • Оттенки серого
    • Gaussian Blur
    • Sobel / Canny edge обнаружения
  2. Если да, как мне объединить исходное изображение с обработанным изображением? Я имею в виду, что после обработки описанных выше шагов я получаю изображение, которое либо полностью черное с белыми краями, либо наоборот. Как бы я поместил края на исходное изображение / текстуру?

Спасибо за вашу помощь!

enne87
источник
Интересно, можете ли вы получить желаемые результаты, используя флаг края OpenGL каким-либо образом. Похоже, только ребра между вершинами, на которых установлен флаг ребра, отображаются в режиме линии. Здесь есть описание . (Я не использовал это сам, или я
выложу

Ответы:

9
  1. Лучшие результаты сильно зависят от вашего варианта использования. Они также зависят от того, какого эффекта вы хотите достичь. Sobel - это просто фильтр обнаружения краев: края будут зависеть от входного сигнала, при этом выбор входного сигнала зависит от вас.

    Здесь вы используете цветное изображение в качестве входных данных, и фильтр справедливо обнаруживает слабые края в синем градиенте, в то время как края куба прерываются там, где его цвет слишком близок к цвету фона.

    Поскольку я предполагаю, что ваша программа также отвечает за рисование куба, у вас есть доступ к другой информации, которую вы можете передать в свой фильтр Собеля. Например, глубина и нормали являются хорошими кандидатами для обнаружения края. К альбедо до освещения можно было привыкнуть. Протестируйте с разными входами и решите, какие из них использовать, в зависимости от полученных результатов.

  2. Что касается вашего вопроса о том, как объединить информацию о краях, я предлагаю вам gнемного отфильтровать перед ее использованием. Затем вы можете использовать его для интерполяции между исходным цветом и желаемым цветом края.

    Например, вы можете попробовать что-то вроде этого:

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

добавление

Чтобы использовать глубину или нормали, вам нужно сохранить их в текстуре, если это еще не сделано. Когда вы создаете буфер кадров для обычного прохода рендеринга, вы можете прикрепить к нему различные текстуры (см. glFramebufferTexture2D) И записать в них другую информацию, кроме цвета сцены.

Если вы прикрепите текстуру глубины (с помощью GL_DEPTH_ATTACHMENT), она будет автоматически использоваться для глубины. Если вы прикрепляете одну или несколько цветных текстур (с помощью GL_COLOR_ATTACHMENTi), вы можете писать в них, объявляя несколько выходных данных для ваших фрагментных шейдеров (раньше это делали gl_FragData; в любом случае, смотрите glDrawBuffers).

Для получения дополнительной информации по теме, посмотрите «Multi Render Target» (MRT).

Жюльен Геро
источник
Хорошая информация, спасибо Жюльен. Еще один вопрос: как я могу использовать глубину или нормальную информацию в моем фрагментном шейдере? Я все еще новичок в GLSL и поэтому не знаю, как включить значения в алгоритм Собела.
enne87
2
Когда вы создаете кадровый буфер для прохода рендеринга, вы можете прикрепить к нему более одной текстуры. Если вы прикрепите текстуру глубины, она будет автоматически использоваться для глубины. Если вы прикрепите другую цветную текстуру, которую вы можете написать в нее отдельно (см. Opengl.org/sdk/docs/man/html/glDrawBuffers.xhtml ), функцию под названием «Multi Render Target» (MRT).
Жюльен Герто
@ enne87: рады помочь. :)
Жюльен Геро
@trichoplax: спасибо за предложение; сделанный.
Жюльен Герто