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

  • 10.3. Анимация преобразований

  • Задание: Добавить в приложение анимацию изме- нения угла поворота куба. Добавить в приложение анимацию изменения цвета куба.Подсказка

  • 11. Отладка графического приложения

  • Список литературы

  • Рябинин Константин Валентинович ВЫЧИСЛИТЕЛЬНАЯ ГЕОМЕТРИЯ И АЛГОРИТМЫ КОМПЬЮТЕРНОЙ ГРАФИКИ РАБОТА С 3D-ГРАФИКОЙ СРЕДСТВАМИ OPENGL

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


    Скачать 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
    страница8 из 8
    1   2   3   4   5   6   7   8
    10.2. Визуализация трёхмерного объекта
    Одним из самых тривиальных трёхмерных объектов является куб, поэтому его и предлагается избрать для тестирования. Для про- стоты можно взять куб
    [
    1, 1] × [1, 1] × [1, 1]
    . Каждую сторону предлагается раскрасить в свой цвет, чтобы они хорошо отличались друг от друга. Геометрически куб содержит
    8
    вершин, однако в этом случае из-за интерполяции цвета у него не будет выраженных углов
    (цвет разных граней будет смешиваться в углах). Поэтому придёт- ся продублировать часть вершин, исключив тем самым смешивание.
    Каждая сторона будет представлена
    4
    вершинами, составляющими
    2
    треугольника, итого
    24
    вершины и
    12
    треугольников.
    Индексация вершин представлена на рис. 20. Эта индексация удовлетворяет описанному ранее условию, что у полигонов, повёр- нутых внешней стороной к наблюдателю, обход вершин осуществ- ляется против часовой стрелки.
    Рис. 20. Индексация вершин куба
    88

    Формат атрибутов вершин предлагается такой:
    (x, y, z, r, g, b)
    Соответствующие вершинный и индексный массивы представлены в листинге 9.
    Листинг 9. Массивы вершин и индексов куба
    1
    const GLfloat vertices[] =
    2
    {
    3

    1.0,

    1.0, 1.0, 1.0, 0.0, 0.0,
    4 1.0,

    1.0, 1.0, 1.0, 0.0, 0.0,
    5 1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
    6

    1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
    7 8
    1.0,

    1.0, 1.0, 1.0, 1.0, 0.0,
    9 1.0,

    1.0,

    1.0, 1.0, 1.0, 0.0,
    10 1.0, 1.0,

    1.0, 1.0, 1.0, 0.0,
    11 1.0, 1.0, 1.0, 1.0, 1.0, 0.0,
    12 13 1.0, 1.0, 1.0, 1.0, 0.0, 1.0,
    14 1.0, 1.0,

    1.0, 1.0, 0.0, 1.0,
    15

    1.0, 1.0,

    1.0, 1.0, 0.0, 1.0,
    16

    1.0, 1.0, 1.0, 1.0, 0.0, 1.0,
    17 18

    1.0, 1.0, 1.0, 0.0, 1.0, 1.0,
    19

    1.0, 1.0,

    1.0, 0.0, 1.0, 1.0,
    20

    1.0,

    1.0,

    1.0, 0.0, 1.0, 1.0,
    21

    1.0,

    1.0, 1.0, 0.0, 1.0, 1.0,
    22 23

    1.0,

    1.0, 1.0, 0.0, 1.0, 0.0,
    24

    1.0,

    1.0,

    1.0, 0.0, 1.0, 0.0,
    25 1.0,

    1.0,

    1.0, 0.0, 1.0, 0.0,
    26 1.0,

    1.0, 1.0, 0.0, 1.0, 0.0,
    27 28

    1.0,

    1.0,

    1.0, 0.0, 0.0, 1.0,
    29

    1.0, 1.0,

    1.0, 0.0, 0.0, 1.0,
    30 1.0, 1.0,

    1.0, 0.0, 0.0, 1.0,
    31 1.0,

    1.0,

    1.0, 0.0, 0.0, 1.0 89

    32
    }
    ;
    33 34
    const GLuint indices[] =
    35
    {
    36 0, 1, 2, 2, 3, 0,
    37 4, 5, 6, 6, 7, 4,
    38 8, 9, 10, 10, 11, 8,
    39 12, 13, 14, 14, 15, 12,
    40 16, 17, 18, 18, 19, 16,
    41 20, 21, 22, 22, 23, 20 42
    }
    ;
    В данном случае уже не обойтись без преобразований вер- шин: во-первых, созданная модель занимает собой весь куб NDC,
    во-вторых, для реалистичного отображения трёхмерной сцены нужна перспективная проекция, в-третьих, для более наглядного отображения куба ему нужно задать некоторый угол поворота, иначе он спроецируется в квадрат.
    В связи с этим необходимо изменить вершинный шейдер, до- бавив в него юниформ-переменную, хранящую матрицу преобразо- вания, и осуществив само преобразование. Код шейдеров с учётом указанных изменений представлен в листинге 10.
    Листинг 10. Код шейдеров для вывода куба
    1
    const GLchar vsh[] =
    2
    ”#version 330
    \
    n”
    3
    ””
    4
    ”layout(location = 0) in vec3 a position;”
    5
    ”layout(location = 1) in vec3 a color;”
    6
    ””
    7
    ”uniform mat4 u pm;”
    8
    ””
    9
    ”out vec3 v color;”
    10
    ””
    11
    ”void main()”
    12

    {

    90

    13

    v color = a color;”
    14

    gl Position = u pm

    vec4(a position, 1.0);”
    15

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

    {

    26

    o color = vec4(v color, 1.0);”
    27

    }
    ”;
    Вершинный шейдер ожидает получить из основной про- граммы матрицу
    P M
    , то есть произведение матрицы проекции и матрицы модели (результат перемножения всех матриц размеще- ния). Умножение этих матриц часто осуществляется в основной программе, а не в шейдере, поскольку в основной программе оно произойдёт однократно для всего массива вершин, тогда как в шейдере производилось бы заново для каждой обрабатываемой вершины.
    Поскольку в нашем случае используется GLSL версии 330, нет возможности явным образом задать дескриптор юниформа и придёт- ся запрашивать его программно. Для этого необходимо создать гло- бальную переменную типа
    GLint и присвоить ей результат функ- ции glGetUniformLocation(g shaderProgram, ”u pm”)
    , где g shaderProgram
    – дескриптор шейдерной программы.
    Для включения теста глубины в функцию init
    (отвечающую за инициализацию основных структур данных сцены и началь- ную настройку состояния конвейера) необходимо добавить вызов glEnable(GL DEPTH TEST)
    . Для заполнения буфера глубины начальными значениями необходимо изменить вызов очистки на glClear(GL COLOR BUFFER BIT | GL DEPTH BUFFER BIT);
    91

    Функция glClear принимает в качестве аргумента битовую маску буферов, которые следует очистить, что позволяет минимизи- ровать число вызовов.
    Затем необходимо подготовить матрицу преобразования. В
    данном случае она может храниться глобально, но часто её окон- чательный вид определяется непосредственно перед отрисовкой каждого нового кадра (в особенности, если на сцене имеет место анимация трансформаций).
    Конкретный код формирования матрицы зависит от использу- емой матричной библиотеки. В качестве матрицы проекции предла- гается использовать перспективное преобразование с углом обзора
    π/4
    , ближней плоскостью отсечения на расстоянии
    0.1
    и дальней –
    на расстоянии
    10
    . В качестве матрицы модели предлагается исполь- зовать произведение матрицы переноса на
    5
    вдоль оси
    OZ
    , пово- рота на
    −π/4
    вокруг оси
    OX
    и поворота на
    π/4
    вокруг оси
    OY
    Цель этих преобразований – «отодвинуть» куб от наблюдателя и обеспечить ракурс, в котором было бы видно, что это объёмный, а не плоский объект.
    Подготовленная матрица должна быть отправлена в шейдер.
    Для этого может быть использован вызов вида glUniformMatrix4fv(g uMP, 1, GL FALSE, g mp.data());
    Он должен быть помещён непосредственно перед вызовом отрисовки. Здесь g uMP
    – дескриптор соответствующего юниформа,
    g mp.data()
    – метод, возвращающий линейный массив данных матрицы итогового преобразования.
    Следует отметить, что для означивания юниформов в OpenGL
    существуют функции «на все случаи жизни»: принимающие целые числа, числа с плавающей точкой, векторы и матрицы разной раз- мерности. С этими функциями читателю предлагается ознакомиться самостоятельно в документации OpenGL [11].
    Задание:
    Изменить тестовое приложение так, что- бы на экран был выведен куб в перспективной про- екции. Изменить проекцию на параллельную. Срав- нить результаты.
    92

    10.3. Анимация преобразований
    Для того чтобы корректно организовать анимационное изме- нение каких-либо свойств объекта, как отмечалось ранее, необходи- мо знать время, затрачиваемое на визуализацию одного кадра. Са- мый простой способ сделать это – воспользоваться системной функ- цией запроса текущего времени и каждый кадр производить измере- ние. Для обеспечения независимости от платформы удобно исполь- зовать абстракцию, предоставляемую std::chrono
    . Пример муль- типлатформенной реализации такого подхода представлен в листин- ге 11.
    Листинг 11. Измерение времени для организации анимации
    1
    g callTime = chrono::system clock::now();
    2 3
    while (glfwGetKey(g window, GLFW KEY ESCAPE) !=
    4
    GLFW PRESS &&
    5
    glfwWindowShouldClose(g window) == 0)
    6
    {
    7
    auto callTime = chrono::system clock::now();
    8
    chrono::duration
    <
    double
    >
    elapsed = callTime

    9
    g callTime;
    10
    g callTime = callTime;
    11
    draw(elapsed.count());
    12 13
    glfwSwapBuffers(g window);
    14
    glfwPollEvents();
    15
    }
    Глобальная переменная, хранящая время предыдущего вызо- ва, описана как chrono::time point g callTime;
    Для использования типов и функций, связанных с изменением времени, необходимо подключить заголовочный файл chrono
    Функция draw
    , которая раннее не имела параметров, теперь принимает параметр double delta
    – время,
    затраченное на визу-
    93

    ализацию предыдущего кадра
    . Ограничением такого подхода явля- ется неверное определение времени самого первого кадра (в данном случае delta в первом кадре будет близка к нулю, тогда как в после- дующих кадрах, при условии включенной вертикальной синхрониза- ции, она, скорее всего, будет около
    1/60
    ). Следовательно анимация,
    как и графический конвейер, тоже нуждается в «разогреве».
    Задание:
    Добавить в приложение анимацию изме- нения угла поворота куба. Добавить в приложение анимацию изменения цвета куба.
    Подсказка:
    Для изменения цвета необходимо вне- сти в один из шейдеров некоторую юниформ- переменную, являющуюся параметром для вычисления цвета, и из- менять значение этой переменной в основной программе в соответ- ствии с параметром delta
    94

    11. Отладка графического
    приложения
    Пожалуй, единственным действительно слабым местом
    OpenGL являются возможности, предоставляемые для отладки.
    Основной механизм здесь состоит в запросе специального поля ошибки машины состояний, который осуществляется функцией glGetError
    . Эта функция возвращает целочисленный код воз- никшей ошибки либо
    0
    , если ошибок не было, и обнуляет поле ошибки.
    Различные API-функции OpenGL могут записывать в поле ошибки значения, в той или иной степени характеризующие при- роду ошибочной ситуации. В документации каждой API-функции расписан набор возможных кодов ошибок, связанных с ней, и
    примерно
    объяснены условия возникновения соответствующих проблем. Однако число самих кодов весьма ограничено, они ак- тивно переиспользуются, из-за чего пропадает однозначность раскодирования. Сказать наверняка, к какой функции относится то или иное значение поля ошибки, можно лишь в том случае, если запрос кода происходит непосредственно после каждого вызова.
    Более того, даже зная, какая функция записала данный код, далеко не всегда можно понять, что же именно привело к этой ошибке и как её исправить.
    При этом достаточно часто на практике возникают ситуации,
    когда «формально» ошибок нет, однако результат визуализации некорректен. Как правило, это связано с неверным размещением объектов, неверными вершинными и индексными массивами, ошиб- ками в алгоритмах шейдеров и т. д. Самым неприятным всегда бывает увидеть пустой экран вместо желаемой сцены, поскольку сходу обычно совершенно не понятно, какое звено в цепочке пре- образований данных на графическом конвейере сработало неверно.
    Существуют профилировщики графического конвейера OpenGL,
    однако почти полностью отсутствуют какие-либо инструменты
    95
    отладки, которые бы, например, позволяли осуществлять удобную трассировку конвейера.
    Отладка кода шейдеров в OpenGL стандартными средствами невозможна: нельзя указывать точки останова, осуществлять трас- сировку или хотя бы выводить куда-либо значения переменных (нет аналогов std::cout
    ). Единственной возможностью хоть как-то уви- деть значение переменной является конвертация его в цвет обраба- тываемой поверхности (нормировка и назначение его в качестве од- ного из компонентов цвета главной цели рендеринга).
    При работе с программируемым конвейером следует помнить об очень широком спектре существующих графических процессо- ров. Различные специальные функции могут быть доступны на од- них процессорах, но недоступны на других, поэтому при реализа- ции сложных алгоритмов следует при помощи соответствующих ме- тодов проверять, поддерживаются ли запрашиваемые механизмы на текущей платформе (в особенности это касается т.н. расширений –
    функций, не входящих в описанное спецификацией ядро графиче- ского API).
    Более того, из-за аппаратных особенностей на различном обо- рудовании некоторые функции могут иногда работать по-разному.
    Источник ошибок такого рода очень трудно обнаружить, поэтому рекомендуется внимательно читать документацию на используемые функции, даже если принцип их работы кажется на первый взгляд очевидным. Так, например, по спецификации GLSL результат функции pow(x,y)
    , вычисляющей
    f (x, y) = x
    y
    , не определён,
    если
    x < 0
    . При этом на большинстве графических процессоров выражение вида a = pow(-2.0, 2.0)
    будет вычислено правильно,
    но есть оборудование, на котором результатом станет
    NaN
    Точки останова, как правило, доступны в основном приложе- нии, однако тоже далеко не всегда помогают в отладке: например, в контексте поиска ошибок в анимации, когда происходит визуализа- ция длинной последовательности кадров, и критичным является ре- альный масштаб времени. В этом случае останов программы резко увеличивает время, затрачиваемое на кадр, и тем самым сбивает ход анимации. Кроме того, многократные остановы внутри цикла визуа-
    96
    лизации, как правило, неинформативны. В таких ситуациях адекват- ным решением является вывод значений интересующих переменных на консоль (или в файл) и последующий анализ изменения этих зна- чений с течением времени.
    Наиболее общей рекомендацией при отладке графическо- го приложения является метод «разделяй и властвуй». В случае обнаружения ошибки неизвестной природы следует постепенно упрощать сцену вплоть до нахождения действий, непосредственно приводящих к ошибке. После каждого, пусть даже незначительного изменения работающего кода, следует производить тестирование корректности визуализации.
    Однако есть надежда, что удобные средства для отладки преобразований на конвейере OpenGL всё-таки появятся в бли- жайшем будущем, поскольку, например, для Direct3D они уже существуют [12].
    97

    Заключение
    Достаточно подробное введение в компьютерную графику можно считать законченным. Следует, однако, ещё раз подчеркнуть,
    что это было именно введение, а разобранное по ходу повествования приложение – своего рода «Hello World».
    У современных низкоуровневых графических API достаточно высокий «порог вхождения» для новичков. Посудите сами: для то- го чтобы вывести один цветной треугольник на экран, пришлось как минимум написать три разных программы (основную, вершенный и фрагментный шейдеры) для двух разных процессоров (CPU и GPU),
    а как максимум – ещё и разобраться в тонкостях устройства графи- ческого конвейера и математического аппарата вычислительной гео- метрии. Однако этот порог можно считать пройденным.
    В качестве дальнейших шагов в изучении компьютерной гра- фики предлагается ознакомится, например, с такими компетентными источниками, как «OpenGL Суперкнига» [1] и «Курс уроков Neon-
    Helium» [2].
    98

    Список литературы
    1. Sellers G., Right Jr. R. S., Haemel N. OpenGL Superbible. Seventh
    Edition. Pearson Education, Inc. 2015.
    2. Neon-Helium [Электронный ресурс].
    URL: http://nehe.gamedev.net/ (дата обращения: 12.04.2017).
    3. Библиотека Eigen [Электронный ресурс].
    URL: http://eigen.tuxfamily.org/ (дата обращения: 12.04.2017).
    4. Библиотека OpenSceneGraph [Электронный ресурс].
    URL: http://www.openscenegraph.org/
    (дата обращения: 12.04.2017).
    5. Библиотека GLM [Электронный ресурс].
    URL: http://glm.g-truc.net/ (дата обращения: 12.04.2017).
    6. Keval H., Sasse M. A. To catch a thief – you need at least 8 frames per second: the impact of frame rates on user performance in a
    CCTV detection task // Proceedings of the 16th ACM international conference on Multimedia. ACM, 2008. P. 941–944.
    7. Библиотека GLEW [Электронный ресурс].
    URL: http://glew.sourceforge.net/ (дата обращения: 12.04.2017).
    8. Библиотека GLFW [Электронный ресурс].
    URL: http://www.glfw.org/ (дата обращения: 12.04.2017).
    9. The United States Patent and Trademark Office. Retina
    [Электронный ресурс]. 2011.
    URL: http://tsdr.uspto.gov/#caseNumber=85056807&caseType=
    SERIAL_NO&searchType=statusSearch
    (дата обращения: 12.04.2017).
    10. Документация языка GLSL [Электронный ресурс].
    URL: https://khronos.org/registry/OpenGL/index_gl.php
    (дата обращения: 12.04.2017).
    11. Документация стандарта OpenGL [Электронный ресурс].
    URL: https://www.opengl.org/sdk/docs/man/
    (дата обращения: 12.04.2017).
    12. Средства отладки Direct3D [Электронный ресурс].
    URL: https://msdn.microsoft.com/en-us/library/hh873193.aspx
    (дата обращения: 12.04.2017).
    99

    Учебное издание
    Рябинин Константин Валентинович
    ВЫЧИСЛИТЕЛЬНАЯ ГЕОМЕТРИЯ
    И АЛГОРИТМЫ КОМПЬЮТЕРНОЙ ГРАФИКИ
    РАБОТА С 3D-ГРАФИКОЙ СРЕДСТВАМИ OPENGL
    Учебное пособие
    Редактор
    М. А. Шемякина
    Корректор
    Н. А. Антонова
    Компьютерная вёрстка:
    К. В. Рябинин
    Подписано в печать 12.04.2017. Формат 60х84/16.
    Усл. печ. л. 5,81. Тираж 100 экз. Заказ
    Издательский центр
    Пермского государственного национального исследовательского университета.
    614990, г. Пермь, ул. Букирева, 15
    Типография ПГНИУ.
    614990, г. Пермь, ул. Букирева, 15 58
    1   2   3   4   5   6   7   8


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