OpenGL - обнаружение краев

12

Я хотел бы загружать произвольные сетки и рисовать толстые черные линии по краям, чтобы они выглядели как тени. Мне удалось нарисовать черный силуэт вокруг объектов с помощью буфера трафарета. Вы можете увидеть результат здесь:

введите описание изображения здесь

Но чего не хватает, так это черных линий в самом объекте. Я думал о проверке нормальных разрывов: Проверка, имеет ли соседний пиксель вектор нормали, отличный от текущего. Если да, то грань найдена. К сожалению, я понятия не имею, как я мог бы реализовать этот подход, ни в OpenGL, ни в вершинном / фрагментном шейдере GLSL.

Я был бы очень рад за некоторую помощь относительно этого подхода или любого другого относительно обнаружения края.

Изменить: я не использую никаких текстур для моей сетки.

Точнее, я хотел бы создать решение CAD / CAM, которое будет выглядеть как можно больше (взято из Top Solid https://www.youtube.com/watch?v=-qTJZtYUDB4 ):

введите описание изображения здесь

enne87
источник
Я считаю, что вам нужно определить «край» более подробно. Чем он отличается от простого каркаса? В ответе cifz есть хорошее описание пост-обработки пространства экрана, но по вашему вопросу сложно определить, применимо ли это.
Андреас
Ну, под краем я имею в виду «складки» и «гребни», которые образуют твердые тела. Каркас будет отображать все грани треугольника, а это не то, что я хочу.
enne87
Хорошо, именно то, что я просил :-) Я бы сгенерировал каркас из этих складок и гребней. Сложная часть - определить, что такое складка / гребень. У вас есть идеи, как это сделать?
Андреас
1
Программы cad в основном не делают этого с шейдером. Вместо этого они знают твердые края модели и рисуют линию поверх сетки, образуя эту информацию.
Джуджаа
joojaa у вас есть больше информации по этой технике? Что делается в случае двойных изогнутых поверхностей свободной формы? Я также не уверен, что происходит, когда у вас есть конус или цилиндр, который режется / обрезается чем-то свободным. stackoverflow.com/questions/43795262/…
Душан Бошняк 'ведущий'

Ответы:

18

Обычно обнаружение краев сводится к обнаружению областей изображения с высоким значением градиента.

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

Как вы нашли этот градиент действительно то, что вы спрашиваете, и я еще не ответил :)

Много способов! Здесь пара :)

Встроенные шейдерные функции

И hlsl, и glsl предлагают производные функции. В GLSL у вас есть dFdx и dFdy, которые дают вам соответственно информацию о градиенте в направлении x и y. Обычно эти функции оцениваются в блоке из фрагментов 2х2.
Если вас не интересует одно направление, хороший способ получить компактный результат, который показывает, насколько сильный градиент в области, - это ширина, которая не дает вам ничего, кроме суммы абсолютных значений dFdy и dFdy.
Скорее всего, вам будет интересен край изображения в целом, а не конкретного канала, поэтому вы можете захотеть преобразовать функцию изображения в яркость. Имея это в виду, когда дело доходит до обнаружения краев, ваш шейдер может включать что-то вроде:

  float luminance = dot(yourFinalColour,vec3(0.2126, 0.7152, 0.0722));
  float gradient = fwidth(luminance );
  float isEdge = gradient > threshold;

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

Стоит упомянуть причину, по которой эти функции работают, но сейчас у меня нет на это времени, я, скорее всего, обновлю этот ответ позже :)

Постобработка пространства экрана

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

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

                                   введите описание изображения здесь

Что соответственно дает вам градиенты в направлениях х и у:

                                  Из старого PDF я написал очень давно.

GradientMagnitude=(Gradientx)2+(Gradienty)2

Тогда вы можете порог, как я упоминал выше.

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

Вышеописанное работает довольно хорошо, но если вам не нравится сглаживание, вы можете использовать ядра Prewitt:

                                                   введите описание изображения здесь

(Обратите внимание, я спешу, скоро напишу правильный форматированный текст вместо изображений!)

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

cifz
источник
Очень хорошее объяснение, cifz, но что, если в определенной ситуации градиент не виден? Например, в задней части куба нет источника света и, следовательно, градиент не виден. Тогда описанный вами процесс обнаружения краев на основе изображений не сработает, не так ли?
enne87
Если вы используете отложенный рендер, вы можете сделать то же самое, но в обычном буфере или снова, если у вас есть предварительный проход, вы можете применить алгоритм к глубине. Тем не менее, вы правы, подход к пространству экрана может быть не идеальным во всех случаях :) То, какое решение жизнеспособно, во многом зависит от того, каков ваш бюджет для этого эффекта, насколько сложна ваша сцена.
CIFZ
Потенциально, если это действительно главное для вашей игры, очень простым, но потенциально сложным, будет вычисление градиента соседних нормалей для каждой вершины (в автономном режиме во время загрузки) и передача этого фактора в качестве дополнительного атрибута вершины.
CIFZ
Thangs снова CIFZ за вашу помощь. Что я хочу достичь, так это разработать решение CAD / CAM, которое выглядит примерно так: youtube.com/watch?v=-qTJZtYUDB4 И я действительно хотел бы знать, как им удалось отрисовать все черные края, складки и хребты. CIFZ, вы думаете, как специалист по графическому программированию, что они достигли этого вида, используя один из ваших подходов к экранному пространству? Или, возможно, вычисляя градиент на соседних нормалях? Я действительно хочу знать, как это можно сделать. Еще раз спасибо!
enne87
1
Не нужно никому платить, просто начните читать несколько уроков онлайн, купите несколько книг и усердно учитесь :) Это будет очень полезно в конце!
CIFZ
3

На всякий случай, если кому-либо еще также необходимо определить края: вот хорошая статья о том, как отображать каркас, и в этой статье объясняется, как отображать только края.

enne87
источник