Рисование границы 2-го многоугольника с помощью фрагментарного шейдера

У меня есть несколько простых многоугольников (менее 20 вершин), которые отображаются на простой плоскости ху, используя GL_TRIANGLES и плоский цвет, 2d-симуляцию.

Я хотел бы добавить границу переменных толщин и другой цвет для этих полигонов. У меня есть что-то реализованое с использованием тех же самых вершин и glLineWidth/GL_LINE_LOOP, которое работает, но это еще один проход рендеринга и повторяет все преобразования вершин.

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

Я представляю себе что-то вроде следующего.

uniform vec2 scale;  // viewport h/w
uniform float line_width;
uniform vec4 fill_color;
uniform vec4 border_color;

varying vec4 vertex; // in world coords

void main()
{
    if (distance_from_edge(gl_FragCoord, vertex, scale) < line_width)
    {
        // we're close to the edge the polygon, so we're the border.
        gl_FragColor = border_color;
    }
    else
    {
        gl_FragColor = fill_color;
    }
}

Часть, которую я пытаюсь выяснить, это функция distance_from_edge - как ее можно вычислить? Является ли использование gl_FragCoord неправильным подходом - следует ли использовать какое-то отображение текстур?

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

Любые идеи?

EDIT: на основе ответа Nicol мой вопрос будет:

Предполагая, что у меня есть треугольник с тремя угловыми вершинами, отмеченными как вершины ребра, а одна вершина в середине отмечена как не ребро (так отображается с тремя треугольниками в целом), то как мне интерполировать в шейдере фрагмента, чтобы нарисовать границу данной толщины? Я предполагаю, что передаю флаг грани в шейдер фрагмента, а также желаемую толщину линии, и он делает некоторый интерполяционный расчет, чтобы выяснить расстояние между ребрами, а не вершинами ребер, и пороговое значение цвета как границы/заливки по мере необходимости

Ответ 1

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

Вы можете думать о барицентрических координатах для каждой вершины как расстоянии от противоположного края. На приведенной ниже диаграмме вершина P0 противоположного края равна e1, а ее расстояние представлено h1; его барицентрическая координата <0.0, h1, 0.0>. Графические процессоры могут использовать это пространство координат внутри, чтобы интерполировать атрибуты вершин для треугольников, когда фрагменты генерируются во время растеризации, он быстро выполняет взвешивание свойств вершин на основе местоположения в треугольнике.

Diagram illustrating the calculation of distance from each edge

Ниже приведены два учебника, в которых объясняется, как это сделать, как правило, это используется для рендеринга наложения каркасного каркаса, поэтому вам может быть лучше повезло в этом. Для ваших целей, поскольку это эффективно специализируется на рендеринге каркаса (с добавлением вы хотите выкинуть линии, которые не относятся к краям внешнего многоугольника), вам нужно будет идентифицировать вершины ребер и выполнить дополнительную обработку.

Например, если вершина не является частью внешнего края, тогда вы захотите присвоить ей барицентрическую координату, например, < 1,100,0 > и подключенную вершину & ​​lt; 0,100,1 > и внутреннюю кромку будет игнорироваться (если предположить, что это край, противоположный вершине, обозначенной < 0,1,0 > , как показано на диаграмме ниже). Идея состоит в том, что вы никогда не хотите, чтобы точка вдоль этого края интерполировалась где угодно около 0.0 (или независимо от того, какой порог вы используете для затенения фрагмента как части границы), что делает его чрезвычайно далеким от центра треугольника в направлении противоположная вершина разрешит это.

Diagram showing how to exclude interior edges

Без шейдеров геометрии (OpenGL ES дружественный):

Здесь ссылка, объясняющая, как это сделать, если вы можете изменить данные вершин, чтобы удерживать барицентрические координаты. Он имеет более высокие требования к хранению и предварительной обработке (в частности, совместное использование вершин между смежными ребрами уже невозможно, поскольку каждый треугольник должен состоять из трех вершин, каждая из которых имеет различную входную барицентрическую координату, и именно поэтому геометрические шейдеры являются желательное решение). Однако он будет работать на гораздо более аппаратном уровне OpenGL ES, чем более общие решения, требующие геометрических шейдеров.

http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/

С геометрическими шейдерами ( Not OpenGL ES дружелюбный):

В качестве альтернативы вы можете использовать геометрический шейдер для вычисления барицентрических координат для каждого треугольника во время рендеринга, как показано в этом уроке. Скорее всего, в OpenGL ES у вас не будет доступа к геометрическим шейдерам, поэтому это, вероятно, можно игнорировать.

http://strattonbrazil.blogspot.com/2011/09/single-pass-wireframe-rendering_10.html http://strattonbrazil.blogspot.com/2011/09/single-pass-wireframe-rendering_11.html

Теоретические основы для этого решения можно найти здесь (любезно предоставлено Internet Way Way Machine):

http://web.archive.org/web/ */http://cgg-journal.com/2008-2/06/index.html