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


12

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

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

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

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

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

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

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


Я считаю, что вам нужно определить «край» более подробно. Чем он отличается от простого каркаса? В ответе 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, но что, если в определенной ситуации градиент не виден? Например, в задней части куба нет источника света и, следовательно, градиент не виден. Тогда описанный вами процесс обнаружения краев на основе изображений не сработает, не так ли?
— enne87

Если вы используете отложенный рендер, вы можете сделать то же самое, но в обычном буфере или снова, если у вас есть предварительный проход, вы можете применить алгоритм к глубине. Тем не менее, вы правы, подход к пространству экрана может быть не идеальным во всех случаях :) То, какое решение жизнеспособно, во многом зависит от того, каков ваш бюджет для этого эффекта, насколько сложна ваша сцена.
— CIFZ

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

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

1
Не нужно никому платить, просто начните читать несколько уроков онлайн, купите несколько книг и усердно учитесь :) Это будет очень полезно в конце!
— CIFZ

3

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.