Главная страница
Навигация по странице:

  • 8.3. Разогрев конвейера

  • 9. Процедурная закраска

  • 10. Обработка объёмных структур

  • 10.1. Буфер глубины

  • К. В. Рябинин вычислительная геометрия и алгоритмы компьютерной графики


    Скачать 1.44 Mb.
    НазваниеК. В. Рябинин вычислительная геометрия и алгоритмы компьютерной графики
    АнкорVychislitelnaya_geometriya_i_algoritmy_kompyuternoy_grafiki._Rabota_s_3D-grafikoy_sredstvami_OpenGL
    Дата28.04.2022
    Размер1.44 Mb.
    Формат файлаpdf
    Имя файлаVychislitelnaya_geometriya_i_algoritmy_kompyuternoy_grafiki._Rab.pdf
    ТипДокументы
    #503739
    страница7 из 8
    1   2   3   4   5   6   7   8
    Задание:
    Добавить в тестовое приложение указан- ные в данной главе вызовы функций и добиться отображения закрашенного градиентом треугольни- ка. Временно исключить буфер индексов и переве- сти отрисовку на метод glDrawArrays
    8.3. Разогрев конвейера
    Следует отметить одну неочевидную особенность графическо- го конвейера – необходимость «разогрева».
    Проблема в том, что первый вызов отрисовки с каждым кон- кретным сочетанием шейдерной программы и настроек отображе- ния модели на входы этой программы выполняется дольше, чем по- следующие вызовы. Это связано с валидацией состояния, которую производит графический драйвер. Результат валидации кешируется,
    поэтому повторные вызовы отрисовки не требуют дополнительных проверок и выполняются быстрее.
    В тестовом приложении используются слишком простые шей- деры, поэтому разница между первым и последующими вызовами
    76
    отрисовки совершенно незаметна. Однако в более сложных проек- тах разница в скорости может достигать
    десятков раз
    , в силу чего первый кадр визуализации сцены готовится ощутимо дольше, чем последующие.
    Хуже того, поскольку валидация проводится каждый раз, когда конвейер «сталкивается» с необходимостью выполнить новую шей- дерную программу или принять новую конфигурацию данных (но- вый вид отображения атрибутов вершин модели на входы шейдера),
    увеличение временных затрат на кадр может возникнуть посреди ра- боты графической программы, когда на сцене в соответствии с логи- кой приложения появляется объект с новыми визуальными свойства- ми. Для пользователя такая ситуация выглядит как «внезапный лаг»,
    нарушение плавности движений на сцене и т. д., что сильно снижает эргономику использования приложения.
    Чтобы избежать такой ситуации, перед началом визуализации сцены необходимо «разогреть» конвейер путём «черновой» визу- ализации всех объектов, которые когда-либо будут появляться на сцене в процессе работы графического приложения. В принципе, не требуется визуализировать сами модели объектов: достаточно лишь воссоздать конфигурацию данных каждого объекта (массив вершин при этом можно использовать упрощённый, например, содержащий лишь один примитив).
    Результаты «разогревающей» конвейер визуализации не нуж- но показывать на экране: сразу после всех «черновых» вызовов от- рисовки можно почистить буфер цвета и запускать основной цикл графического приложения.
    Однако перед «черновой» визуализацией на некоторых программно-аппаратных платформах (фактическое поведение за- висит от аппаратуры) требуется отобразить
    минимум два кадра
    с некоторым содержательным изображением, чтобы заполнить стра- ницы видеопамяти двойного буфера (при использовании двойной буферизации). Как правило, в качестве такого «разогревающего»
    изображения используют некоторую
    заставку
    (англ.
    Splash Screen
    ),
    показываемую в момент старта графического приложения.
    77

    9. Процедурная закраска
    Аппарат шейдеров даёт возможность организовывать слож- ные визуальные эффекты при отображении объектов. Типичным примером является
    процедурная закраска
    (
    англ. Procedural
    Shading
    ) – вычисление цвета фрагментов полигонов по некоторому алгоритму, отличному от тривиальной интерполяции. В процедур- ной закраске, как правило, участвуют атрибуты вершин, напрямую не выражающие цвет (как в предыдущем примере), а являющиеся параметрами алгоритма раскрашивания.
    Предлагается доработать тестовое приложение, реализовав в нём процедурную закраску. Сначала предлагается добавить к имею- щемуся в приложении треугольнику ещё один так, чтобы получил- ся прямоугольник (на прямоугольнике будет удобнее проводить экс- перименты с процедурной закраской). При этом предлагается изба- виться от атрибутов, задающих цвет вершин, а вместо них использо- вать один дополнительный атрибут
    t
    [0; 1]
    . Таким образом, каждая вершина будет характеризоваться тройкой чисел
    (x, y, t)
    Вид прямоугольника со значениями атрибутов вершин пред- ставлен на рис. 14.
    Рис. 14. Прямоугольник для тестирования процедурной закраски
    Для примера на рис. 14 указана некоторая внутренняя точка со значением атрибута
    t
    в ней. Этот атрибут будет в данном случае являться параметром процедурной закраски.
    78

    Далее предлагается изменить код шейдеров на приведённый в листинге 8.
    Листинг 8. Код шейдеров процедурной закраски
    1
    const GLchar vsh[] =
    2
    ”#version 330
    \
    n”
    3
    ””
    4
    ”layout(location = 0) in vec2 a position;”
    5
    ”layout(location = 1) in float a t;”
    6
    ””
    7
    ”out float v t;”
    8
    ””
    9
    ”void main()”
    10

    {

    11

    v t = a t;”
    12

    gl Position = vec4(a position, 0.0, 1.0);”
    13

    }
    ”;
    14 15
    const GLchar fsh[] =
    16
    ”#version 330
    \
    n”
    17
    ””
    18
    ”in float v t;”
    19
    ””
    20
    ”layout(location = 0) out vec4 o color;”
    21
    ””
    22
    ”void main()”
    23

    {

    24

    o color = vec4(sin(v t

    62.83),0.0,0.0,1.0);”
    25

    }
    ”;
    79

    Результат работы приложения показан на рис. 15.
    Рис. 15. Результат процедурной закраски по алгоритму из листинга 8
    (цвета изменены на более контрастные в типографских целях)
    Как можно видеть на рис. 15, на экран выводится прямоуголь- ник, закрашенный
    10
    горизонтальными линиями. За их формирова- ние отвечает вектор цвета vec4(sin(v t * 62.83), 0.0, 0.0,
    1.0)
    из листинга 8. Параметр v t изменяется от
    0
    до
    1
    , в результате чего синус проходит примерно
    10
    периодов, попеременно возвращая значения в отрезке
    [
    1; 1]
    . При интерпретации цвета отрицательные значения зануляются, что приводит к появлению чёрного. Положи- тельные же значения приводят к постепенному нарастанию, а затем
    – затуханию интенсивности красного.
    Немного усложнив алгоритм закраски, можно получить изоб- ражения, подобные продемонстрированным на рис. 16.
    Рис. 16. Результат процедурной закраски волнистыми линиями (слева) и концентрическими окружностями (справа)
    80

    Задание:
    Изменить исходный код шейдеров, масси- вы вершин и индексов, настройку отображения ат- рибутов на входы вершинного шейдера и вызов от- рисовки так, чтобы на экран был выведен прямо- угольник, закрашенный линиями, как на рис. 15. Из- менить приложение так, чтобы на экран был выведен прямоуголь- ник, закрашенный волнистыми линиями, как на рис. 16 слева. Из- менить приложение так, чтобы на экран был выведен прямоуголь- ник, закрашенный концентрическими окружностями, как на рис. 16
    справа.
    Подсказка:
    Для достижения закраски волнистыми линиями и окружностями следует добавить ещё один атрибут вершин
    p
    , «на- правив» его изменение вдоль оси
    X
    . Функция изменения яркости будет иметь вид
    f = f (t, p)
    . Яркость в данном случае аналогична высоте поверхности
    y = f (x, y)
    . В принципе, в конкретно данном случае в качестве параметров для вычисления яркости можно использовать координаты вершин, полностью избавившись от дополнительных атрибутов. Однако этого делать не рекомендуется,
    поскольку в реальных ситуациях, при закраске более сложных моделей, завязка на координаты может нарушить единообразие обработки всех полигонов объекта сцены.
    81

    10. Обработка объёмных структур
    В предыдущих главах все примеры визуализации ограничива- лись двумерными объектами, координаты вершин которых дополня- лись значениями
    z = 0
    ,
    w = 1
    лишь в вершинном шейдере. Бо- лее того, все координаты в основной программе задавались в NDC и не использовались никакие преобразования размещения и проекции.
    Таким образом, работа с трёхмерной графикой велась в неявном ви- де: с точки зрения пользователя сцена была плоской. Однако OpenGL
    (и подобные ему стандарты) в первую очередь решает задачи обра- ботки объёмных геометрических структур.
    Переход в трёхмерное пространство, однако, влечёт за собой,
    помимо дополнительных степеней свободы размещения объектов,
    также и дополнительные алгоритмические сложности.
    10.1. Буфер глубины
    Важным вопросом при визуализации объектов сцены являет- ся определение и корректная обработка перекрытий в случае, если проекции объектов имеют пересечение на плоскости экрана.
    В двумерной графике (когда каждый объект характеризуется лишь двумя координатами
    x
    и
    y
    ), как правило, перекрытия полно- стью зависят от порядка поступления данных на конвейер (по су- ти, используется дисциплина FIFO): объект, который был отправлен на конвейер раньше, оказывается перекрыт объектом, отправленным позже. Такой подход достаточно удобен, так как позволяет програм- мисту разграничивать «передний» и «задний» планы путём сорти- ровки списка объектов. Порядок вывода в контексте двумерной гра- фики часто называют
    z
    -порядком
    (англ.
    Z-Order
    ), обозначая тем са- мым, что сцена имеет «псевдоглубину».
    В трёхмерной графике (в случае когда объекты характеризу- ются тремя координатами
    x
    ,
    y
    и
    z
    ) такой подход не применим, так как
    z
    -порядок здесь явным образом определяется самими объектами и для корректной визуализации сцены не должен зависеть от очерёд-
    82
    ности их обработки. Например, если более близкий к наблюдателю объект был выведен раньше, он не должен быть перекрыт более даль- ним, который был отправлен на конвейер позднее.
    При этом сортировка объектов и даже отдельных граней объектов по удалённости в общем случае не обеспечит корректного отображения, поскольку объекты могут быть невыпуклыми и мо- гут пересекаться в пространстве. На рис. 17 продемонстрирована ситуация, когда любой порядок вывода привёл бы к неверному ре- зультату. Кроме того, ввиду потенциально изменяющегося ракурса и взаимного расположения объектов, сортировку необходимо было бы осуществлять после каждого изменения, что в общем случае неэффективно (в особенности, если речь идёт о гранях объектов,
    поскольку изменение порядка их следования означает как минимум изменение массива индексов).
    Рис. 17. Примеры объектов, корректное перекрытие проекций которых не обеспечить порядком отрисовки
    Для обеспечения корректных перекрытий, не зависящих от порядка вывода объектов и определяющихся их реальным вза- имным расположением в трёхмерном пространстве, используют специальный механизм, называемый
    тестом глубины
    (англ.
    Depth
    Test
    ). Этот механизм базируется на структуре данных, называе- мой
    буфером глубины
    (англ.
    Depth Buffer
    ), или
    z-буфером
    (англ.
    Z-Buffer
    ).
    Буфер глубины является частью буфера кадра и представляет собой двумерную матрицу, по размерности совпадающую с буфером цвета. Как правило, память под него выделяют вместе с буфером цве- та при создании буфера кадра в графическом контексте. Ячейки бу-
    83
    фера цвета могут иметь разную разрядность, но предназначены для хранения единственного числового значения (а не вектора, как в слу- чае буфера цвета). На сегодняшний день разрядность буфера глуби- ны составляет, как правило,
    16
    или
    24
    бита.
    Концептуально алгоритм теста глубины состоит в следующем
    (предположим, что ось глубины направлена на наблюдателя):
    1. Перед началом визуализации кадра буфер глубины заполняет- ся некоторыми начальными значениями, чаще всего – нулями.
    2. На этапе растеризации для каждого фрагмента, соответствую- щего обрабатываемому примитиву, методом билинейной ин- терполяции (который был рассмотрен ранее в контексте ин- терполяции атрибутов вершин) определяется значение глуби- ны (координата
    z
    ).
    3. Значение глубины
    z
    сравнивается с содержимым
    z
    b
    соответ- ствующей ячейки буфера глубины.
    3.1. Если
    z
    ≥ z
    b
    , данные фрагмента записываются в ячейки соответствующих ему целей рендеринга (например, цвет в ячейку буфера цвета), а
    z
    записывается в ячейку буфера глубины.
    3.2. Если
    z < z
    b
    , фрагмент отбрасывается (относительно него более не происходит никаких действий, а графический процессор переходит к обработке следующего).
    Таким образом, более близкие к наблюдателю фрагменты мас- кируют более дальние, не давая им выводиться. В свою очередь, бо- лее дальние фрагменты перезаписываются более близкими. За счёт этого обеспечиваются правильные перекрытия.
    Следует, однако, обратить внимание, что разрядность буфера глубины невелика (она продиктована аппаратными ограничениями видеокарт). Из-за этого при сохранении значений в его ячейки про- исходит потеря точности, которая в последствии может привести к неверным результатам теста глубины, в особенности по отношению к копланарным полигонам.
    Ошибки теста глубины называют
    борьбой глубин
    (англ.
    Z-Fighting
    ). Пример такой ситуации показан на рис. 18.
    84

    Рис. 18. Корректное отображение полигонов (слева) и ситуация борьбы глубин (справа)
    Дефекты, возникающие из-за борьбы глубин, резко снижают качество изображения, а на динамически меняющихся сценах созда- ют «мелькание», поскольку тест глубины оказывается неустойчив и при небольших изменениях ракурса даёт резкое изменение результа- та визуализации.
    Для избежания борьбы глубин категорически не рекомендует- ся создавать модели с копланарными пересекающимися полигонами.
    Как правило, графические API дают возможность настраивать функции сравнения и записи, отмеченные в шаге (3.1) алгоритма те- ста глубины. То есть настройками состояния конвейера можно, на- пример, инвертировать работу буфера глубины для решения каких- либо специфических задач, а также задать функцию модификации значения
    z
    перед записью в его ячейку.
    Так, например, фрагментный шейдер может вычислять, по су- ти, произвольное значение глубины фрагмента, которое затем будет использовано для теста глубины, и при прохождении теста записано в буфер. Такая ситуация, однако, в ряде случаев может снизить про- изводительность работы: многие графические процессоры в целях оптимизации обработки сцены производят т. н.
    ранний тест глуби-
    ны
    (англ.
    Early Depth Test
    ). Он производится
    до
    запуска фрагмент- ного шейдера, если этот шейдер не изменяет значение глубины, что позволяет сэкономить время вычисления цвета фрагмента, который не вносит вклад в итоговое изображение (если в буфере цвета на этом месте уже есть «более близкий к наблюдателю» фрагмент).
    85

    Определение возможности раннего теста глубины осуществ- ляется перед началом прогона данных по конвейеру путём статиче- ского анализа кода фрагментного шейдера.
    Для частичного решения проблемы потери точности в буфере глубины значения в нём хранятся не в прямом, а в преобразованном виде, причём преобразование нелинейно. Чаще всего используется следующая формула:
    z
    b
    =
    [
    2
    n
    z
    f ar
    − z
    near
    (
    z
    f ar
    +
    z
    f ar
    z
    near
    z
    )]
    ,
    где
    z
    b
    – целочисленное значение, сохраняемое в буфер глубины,
    n
    – разрядность буфера глубины,
    z
    f ar
    – расстояние от наблюдателя до дальней плоскости отсечения,
    z
    near
    – расстояние от наблюдателя до ближней плоскости отсечения,
    z
    – значение глубины фрагмента.
    Такой способ хранения обеспечивает достаточно высокую точ- ность представления глубин около ближней плоскости отсечения и уменьшение точности по мере удаления. Это оправданно, поскольку ближние к наблюдателю объекты занимают на экране больше места,
    а потому обладают большей детализацией и требуют более точной обработки (в частности, в отношении мелких деталей, расположен- ных по оси глубины близко друг к другу и рискующих вступить в
    «борьбу»).
    Отсюда можно сделать два важных вывода:
    1. Ближняя плоскость отсечения должна быть как можно дальше от наблюдателя (как можно ближе к объектам сцены, насколько это позволяет логика работы графического приложения), что- бы область глубин высокой точности не пустовала, а объекты первого плана не попадали в зону пониженной точности.
    2. Протяжённость сцены, т. е.
    z
    f ar
    −z
    near
    должна быть как можно меньше (опять же, насколько это позволяет логика работы).
    Об этом всегда нужно помнить, проектируя трёхмерные сце- ны, чтобы минимизировать вероятность возникновения ситуации борьбы глубин. Для объектов, удалённых от наблюдателя не дальше,
    чем на
    z
    , минимальное «безопасное» значение расстояние

    , на
    86
    котором они должны находится друг от друга, чтобы не вступить в борьбу, можно грубо оценить по формуле
    ∆ =
    z
    2 2
    n
    z
    near
    − z
    .
    Здесь предполагается, что

    ≪ z
    и
    z
    near
    ≪ z
    f ar
    Буфер глубины может быть получен из видеопамяти в основ- ное приложение для осуществления каких-либо операций, требую- щих знания фактических значений глубины сцены в каждой точке изображения. Например, на основе буфера глубины может быть ор- ганизован эффект переменной резкости, когда определённая точка на сцене полагается точкой фокусировки вирутальной камеры, и части изображения, глубина которых отличается от глубины точки фокуса,
    размываются пропорционально модулю разности этих глубин.
    Содержимое буфера глубины может быть визуализировано в виде изображения в градациях серого (значения интерпретируется как яркость). Результат такой визуализации показан на рис. 19.
    Рис. 19. Трёхмерная сцена (слева) и соответствующее ей содержание буфера глубины (справа)
    В OpenGL тест глубины может быть в любой момент вклю- чен командой glEnable(GL DEPTH TEST)
    и отключен командой glDisable(GL DEPTH TEST)
    . Для работы теста глубины, однако,
    необходимо, чтобы заранее был создан буфер глубины и связан с бу- фером кадра.
    Отсутствие теста глубины при визуализации трёхмерной сцены приводит к достаточно «сюрреалистичным» результатам:
    87
    поскольку перекрытия в этом случае зависят исключительно от по- рядка вывода полигонов, некоторые более близкие к наблюдателю объекты оказываются перекрыты более дальними. Кроме того, часть объектов может казаться «вывернутыми наизнанку», поскольку их
    «задние» грани вывелись позже «передних».
    1   2   3   4   5   6   7   8


    написать администратору сайта