Opengl рисуем простые объекты


Содержание

Нарисовать линии произвольной длины в OpenGl

Никак не могу решить простую проблему с OpenGl. Только начинаю изучать OpenGl. Нигде не могу найти как нормально нарисовать линии на экране. Получается рисовать линии «единичной длины» на весь экран, а нужно чтобы линия с длиной 1 была гораздо меньше экрана, например 1/20 часть. Писать значения длины как меньшие единицы (0,75 и т. д.) не подходит, нужно целыми. Все брал из примеров в интернете.

Инициализация OpenGL и библиотек:

Рисование линий произвольной длины:

27.08.2020, 17:11

OpenGL, шаг 0, линии
Хочу нарисовать куб из линий, а-ля графика 80х. Пока додумался только до способа рисования через.

OpenGL C++: чтобы свет падал на сферу, а не на линии
Как сделать чтобы свет падал к примеру на сферу, а на линии не падал. Ибо линии меняют цвет. Может.

C# + OpenGL. Рисование карандашом. Как ускорить прорисовку линии
Ну и дела. Я всё-таки разобрался с рисованием карандашом на C# с помощью OpenGL, НО, похоже, что на.

Нарисовать тень к линии
В универе только начали изучать С++. На паре нарисовали линию. Самим нужно нарисовать к ней тень.

Нарисовать призму на OpenGL
Задание: нарисовать 11-гранную призму. Путаюсь в координатах, не пойму как её нарисовать((.

27.08.2020, 17:59 2

glPushMatrix()
glPopMatrix()
зачем это делать с единичной матрицей?

gluPerspective(45, Экран.Width / Экран.Width, 10, 200)
пробовали изменять угол обзора вместо 45?

27.08.2020, 18:06 [ТС] 3

>зачем это делать с единичной матрицей?

Я плохо в этом разбираюсь.

При изменении угла вроде ничего не меняется

Opengl рисуем простые объекты

Точки, линии, треугольники, четырехугольники, многоугольники — простые объекты, из которых состоят любые сложные фигуры. В предыдущей главе мы рисовали сферу, конус и тор. OpenGL непосредственно не поддерживает функций для создания таких сложных объектов, т.е. таких функций нет в opengl32.dll. Эти функции есть в библиотеки утилит glu32.dll, и устроены они следующим образом. Для того чтобы нарисовать сферу функция auxSol >Сначала вы говорите, что будете рисовать — glBegin с соответствующим параметром. Возможные значения mode перечислены ниже в таблице. Далее вы указываете вершины, определяющие объекты указанного типа. Обычно вы будете задавать вершину одним из четырех способов.

И наконец, вы вызываете glEnd, чтобы указать, что вы закончили рисовать объекты типа, указанного в glBegin. Далее мы подробно разберем создание всех примитивов.

Значение mode Описание
GL_POINTS Каждый вызов glVertex задает отдельную точку.
GL_LINES Каждая пара вершин задает отрезок.
GL_LINE_STRIP Рисуется ломанная.
GL_LINE_LOOP Рисуется ломанная, причем ее последняя точка соединяется с первой.
GL_TRIANGLES Каждые три вызова glVertex задают треугольник.
GL_TRIANGLE_STRIP Рисуются треугольники с общей стороной.
GL_TRIANGLE_FAN Тоже самое, но по другому правилу соединяются вершины, вряд ли понадобится.
GL_QUADS Каждые четыре вызова glVertex задают четырехугольник.
GL_QUAD_STRIP Четырехугольники с общей стороной.
GL_POLYGON Многоугольник.

3.2 Точки

Вы можете нарисовать столько точек, сколько вам нужно. Вызывая glVertex3d, вы устанавливает новую точку. При создании точек вы можете изменять следующие параметры. Вы можете вызывать glColor3d внутри glBegin/glEnd. Размер точки можно устанавливать с помощью функции:

Режим сглаживания можно устанавливать вызовом функции

Отключается соответственно вызовом glDisable() c этим параметром. Последние функции — glPointSize и glEnable/glDisable надо вызывать вне glBegin/glEnd, иначе они будут проигнорированы. Функции glEnable/glDisable включают/выключают множество опций, но вы должны учитывать, что некоторые опции влекут за собой большие вычисления и, следовательно, изрядно затормаживают ваше приложение, поэтому без надобности не стоит их включать. Очевидно, что совершенно не к чем включать освещение, наложение текстуру и сглаживания цветов при рисовании точек. Пока вы с этими возможностями OpenGL не познакомились, поэтому запомните это на будущее.

3.3 Линии

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

Второй параметр задает саму маску. Например, если его значение равно 255(0x00FF), то чтобы вычислить задаваемую маску воспользуемся калькулятором. В двоичном виде это число выглядит так: 0000000011111111, т.е. всего 16 бит. Старшие восемь установлены в ноль, значит тут линии не будет. Младшие установлены в единицу, тут будет рисоваться линия. Первый параметр определяет, сколько раз повторяется каждый бит. Скажем, если его установить равным 2, то накладываемая маска будет выглядеть так:

Далее приведен исходный текст с комментариями для наглядной демонстрации что к чему.

3.4 Треугольники

Для треугольника можно задавать те же параметры, что и для линии плюс есть еще одна функция glPolygonMode. Она устанавливает опции для отрисовки многоугольника. Первый параметр может принимать значения — GL_FRONT, GL_BACK и GL_FRONT_AND_BACK. Второй параметр указывает, как будет рисоваться многоугольник. Он принимает значения — GL_POINT(рисуются только точки), GL_LINE(рисуем линии) и GL_FILL(рисуем заполненный многоугольник). Первый параметр указывает: к лицевой, тыльной или же к обеим сторонам применяется опция, заданная вторым параметром. Треугольники можно рисовать, передав GL_TRIANGLE_STRIP или GL_TRIANGLE_FAN в glBegin. В первом случае первая, вторая и третья вершины задают первый треугольник. Вторая, третья и четвертая вершина — второй треугольник. Третья, четвертая и пятая вершина — третий треугольник и т.д. Вершины n, n+1 и n+2 определят n-ый треугольник. Во втором случае первая, вторая и третья вершина задают первый треугольник. Первая, третья и четвертая вершины задают второй треугольник и т.д. Вершины 1, n+1, n+2 определяют n-ый треугольник. Далее следует пример с комментариями.

3.5 Четырехугольники и многоугольники

Четырехугольники рисуются вызовом функции glBegin с параметром GL_QUADS или GL_QUAD_STRIP. Для первого случая каждые четыре вершины определяют свой четырехугольник. Во втором случае рисуются связанные четырехугольники. Первая, вторая, третья и четвертая вершина определяют первый четырехугольник. Третья, четвертая, пятая и шестая вершина — второй четырехугольник и т.д. (2n-1), 2n, (2n+1) и (2n+2) вершины задают n-ый четырехугольник. Многоугольники задаются вызовом glBegin с параметром GL_POLYGON. Все вершины определяют один многоугольник. Для многоугольников можно задавать стили при помощи выше описанной функции glPolygonMode, толщину линии, толщину точек и цвет.

3.6 Упражнение: «Примитивы»

Изобразите точки, линии, треугольники, многоугольники в одном окне, как показано ниже.

Исходный файл смотрите здесь. Исполняемый файл здесь.

3.7 Упражнение: «Многогранники»

Реализуйте проволочные многогранники с помощью проволочных треугольников, многоугольников и линий.

3.8 Резюме

Ну вот, вы еще на один шаг продвинулись в изучение библиотеки OpenGL. Теперь вы имеете представление о том, как рисовать элементарные фигуры. Из примитивов вы можете составить фигуры любой сложности.

Геометрические примитивы OpenGL

Вершины надо задавать обязательно применительно к какому-нибудь примитиву. Рисование примитива начинается с вызова функции glBegin(), которой в качестве параметра передается константа, обозначающая тип примитива. Завершается примитив функцией glEnd(). Прототипы этих функций:

void glBegin( Glenum mode );

void glEnd( void );

Между вызовами glBegin() и glEnd() делаются вызовы glVertex*(). Например, для описания многоугольника, показанного на рис. 3.5 слева, надо вызвать следующие функции:

glVertex2d( 0.0, 0.0 );

glVertex2d( 0.0, 3.0 );

glVertex2d( 3.0, 3.0 );

glVertex2d( 4.0, 1.5 );

glVertex2d( 3.0, 0.0 );

Рис. 3.5.Примитивы двух типов: многоугольник и множество точек.

Если в качестве типа примитива вместо GL_POLYGON указать GL_POINTS, то будет нарисовано множество из 5-ти точек (рис. 3.5, справа). В табл. 3.1 перечислены все допустимые типы примитивов, которые можно указывать при вызове glBegin().

Таблица 3.1.Имена и назначение геометрических примитивов.

Имя константы Назначение
GL_POINTS Отдельные точки
GL_LINES Пары вершин, являющиеся концами отрезков
GL_POLYGON Граница простого выпуклого многоугольника
GL_TRIANGLES Тройки вершин, которые интерпретируются как вершины треугольников
GL_QUADS Четверки вершин, которые интерпретируются как вершины четырехугольников
GL_LINE_STRIP Вершины ломаной линии
GL_LINE_LOOP Вершины замкнутой ломаной линии (то же, что и предыдущий тип, но последняя и первая вершина соединяются автоматически)
GL_TRIANGLE_STRIP Связная полоса из треугольников (триангулированная полоса)
GL_TRIANGLE_FAN Веер из треугольников
GL_QUAD_STRIP Связная полоса из четырехугольников (квадрированная полоса)

На рис. 3.6 показаны примеры примитивов, перечисленных в табл. 3.1. Предполагается, что между glBegin() и glEnd() перечислено n вершин (v, v1, v2, . vn-1). Как следует из рис. 3.6, кроме точек, отрезков и многоугольников, в OpenGL есть еще несколько специальных типов примитивов.

Рис. 3.6.Типы геометрических примитивов.

Свойства точек, отрезков и многоугольников

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

Точки

Экранный размер точки задается с помощью функции:

void glPointSize( float size );

где size – диаметр точки в пикселах (должен быть больше 0.0, по умолчанию 1.0).

Количество пикселей, которые будут закрашены на экране при отображении данной точки, зависит от того, включено сглаживание или нет. Если сглаживание выключено, то size округляется до целого числа и на экране текущим цветом закрашивается квадрат со стороной size пикселей.


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

void CALLBACK display();

auxInitDisplayMode( AUX_SINGLE | AUX_RGBA );

auxInitPosition( 0, 0, 200, 200 );

auxInitWindow( «Лекция 3, Программа 3.1» );

glClearColor( 0.0, 0.0, 0.0, 0.0 );

void CALLBACK display()

// В 1-й строке три точки диаметром 2 пиксела, без сглаживания

glColor3d( 1, 0, 0 );

glVertex2d( 50, 180 );

glColor3d( 0, 1, 0 );

glVertex2d( 100, 180 );

glColor3d( 0, 0, 1 );

glVertex2d( 150, 180 );

// Во 2-й строке три точки диаметром 5 пикселов, без сглаживания

glColor3d( 1, 0, 0 );

glVertex2d( 50, 100 );

glColor3d( 0, 1, 0 );

glVertex2d( 100, 100 );

glColor3d( 0, 0, 1 );

glVertex2d( 150, 100 );

// В 3-й строке три точки диаметром 10 пикселов, со сглаживанием

glColor3d( 1, 0, 0 );

glVertex2d( 50, 20 );

glColor3d( 0, 1, 0 );

glVertex2d( 100, 20 );

glColor3d( 0, 0, 1 );

glVertex2d( 150, 20 );

// Принудительное завершение всех операций рисования

Программа 3.1. Рисование точек разного размера при

выключенном и включенном сглаживании.

Отрезки

У отрезков в OpenGL можно задавать толщину и стиль (точечный пунктир, штриховой пунктир, штрих-точка и т.п.). Толщина отрезка (в пикселах) по умолчанию равна 1.0, для ее изменения имеется функция:

void glLineWidth( GLfloat width );

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

Стиль отрезка (тип пунктира) устанавливается функцией glLineStipple():

void glLineStipple( GLint factor, GLushort pattern);

После вызова этой функции надо обязательно разрешить использование стиля с помощью glEnable(), например:

glLineStipple( 1, 0x3F07 );

Тип пунктира определяется шаблоном – 16-ти битным числом pattern, в котором биты, равные 0 и 1, обозначают пустые и закрашенные пикселы. Размер шаблона можно увеличить с помощью множителя factor, который задает количество повторений каждого бита шаблона.

В приведенном примере задан шаблон 0x3F07 (в двоичной форме это число 0011111100000111). При рисовании отрезка сначала закрашиваются 3 пиксела, затем 5 пропускаются, затем 6 закрашиваются, 2 пропускаются (шаблон обрабатывается справа налево, от младших битов к старшим). Если задать factor=2, то шаблон будет задавать следующее правило рисования: закрасить 6 пикселов, 10 пропустить, 12 закрасить, 4 пропустить. На рис. 3.7 показаны отрезки различных стилей и соответствующие значения шаблона и множителя.

Рис. 3.7.Пунктирные отрезки.

Рисование отрезков нескольких стилей и различной толщины демонстрируется в программе 3.2. Результат работы этой программы показан на рис. 3.8. Интересно отметить эффект, который достигается программе 3.2 за счет того, что отрезок рисуется не сразу целиком, а из нескольких отдельных сегментов.

Рис. 3.8.Отрезки различных стилей и разной толщины.

void drawOneLine( double x1, double y1, double x2, double y2 );

void CALLBACK display();

auxInitDisplayMode( AUX_SINGLE | AUX_RGBA );

auxInitPosition( 0, 0, 400, 150 );

auxInitWindow( «Лекция 3, Программа 3.2» );

glClearColor( 0.0, 0.0, 0.0, 0.0 );

void drawOneLine( double x1, double y1, double x2, double y2 )

glVertex2d( x1, y1 );

glVertex2d( x2, y2 );

void CALLBACK display()

glColor3d( 1.0, 1.0, 1.0 ); // Все отрезки рисуются белым цветом

// В 1-й строке 3 отрезка разных стилей

glLineStipple( 1, 0x0101 ); // Точечный пунктир

drawOneLine( 50.0, 125.0, 150.0, 125.0 );

glLineStipple( 1, 0x00FF ); // Штриховой пунктир

drawOneLine( 150.0, 125.0, 250.0, 125.0 );

glLineStipple( 1, 0x1C47 ); // Штрих-точка-штрих

drawOneLine( 250.0, 125.0, 350.0, 125.0 );

// Во 2-й строке 3 отрезка толщиной 5 пикселей и разных стилей


glLineStipple( 1, 0x0101 );

drawOneLine( 50.0, 100.0, 150.0, 100.0 );

glLineStipple( 1, 0x00FF );

drawOneLine( 150.0, 100.0, 250.0, 100.0 );

glLineStipple( 1, 0x1C47 );

drawOneLine( 250.0, 100.0, 350.0, 100.0 );

// В 3-й строке 6 отрезков стиля штрих-точка-штрих, которые

// образуют один большой отрезок

glLineStipple( 1, 0x1C47 );

for ( int i = 0; i 0), которая вычисляется по формуле:

где xi и yi есть экранные координаты i-й вершины n-угольника. Операция Å является обычным сложением, за исключением того, что n Å 1 = 1:

При отображении замкнутой поверхности из многоугольников одной ориентации не будет видно ни одной задней стороны – все они загораживаются передними сторонами многоугольников. В данном случае вы можете ускорить отображение поверхности, если запретите OpenGL рисование задних сторон. Для запрещения рисования определенных сторон многоугольников применяется функция:

Илон Маск рекомендует:  Что такое код setpalette

void glCullFace( GLenum mode );

Параметр mode равен GL_FRONT, GL_BACK или GL_FRONT_AND_BACK. Перед вызовом этой функции надо включить режим отсечения сторон многоугольников с помощью вызова glEnable( GL_CULL_FACE ).

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

void CALLBACK resize( int width, int height );

void CALLBACK display();

void CALLBACK addAngleX();

void CALLBACK subAngleX();

void CALLBACK addAngleY();

void CALLBACK subAngleY();

// Расположение пирамиды относительно осей X и Y

int angle_x = 0, angle_y = 0;

auxInitDisplayMode( AUX_RGBA | AUX_DEPTH | AUX_DOUBLE );

auxInitPosition( 50, 10, 400, 400);

auxInitWindow( «Лекция 3, Программа 3.3» );

// Включение ряда параметров OpenGL

// Задание положения и направления нулевого источника света

glLightfv( GL_LIGHT0, GL_POSITION, pos );

glLightfv( GL_LIGHT0, GL_SPOT_DIRECTION, dir );

// Регистрация обработчиков событий

auxKeyFunc( AUX_UP, subAngleX );

auxKeyFunc( AUX_DOWN, addAngleX );

auxKeyFunc( AUX_RIGHT, addAngleY );

auxKeyFunc( AUX_LEFT, subAngleY );

void CALLBACK resize( int width, int height )

glViewport( 0, 0, width, height );

gluPerspective( 60.0, (double)width/(double)height, 1.0, 20.0 );

gluLookAt( 0,0,5, 0,0,0, 0,1,0 );

void CALLBACK display()

const double BASE_R = 1; // Радиус основания

const double PYRAM >

const double PI = 3.14159;

glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

Как правильно рисовать игровую сцену в openGL?

Большинство толковых уроков по openGL сводится к рисованию цветного треугольничка. Максимум что рассказывают — как загрузить модель. Примеры таких вступительных уроков: вот, вот ещё урок на гугл-коде и вот (последний — подробный вводный урок, увы, однако, не заходящий дальше всё того же треугольника).

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

Примечание: Дальше я буду называть объектом «экземпляр» модели. В случаи с играми, объектом будет то, что игрок видит как юнит. Ясно, что в сцене могут быть несколько юнитов одного типа (соответственно, это будет несколько объектов одной модели).

На текущем уровне понимания openGL я вижу следующие способы нарисовать сцену с более чем одним объектом модели:

1. Неэффективный по скорости. Вызывается draw одного и того же VAO с вершинами модели, но с разными матрицами. Вызов draw, как я понимаю, дорогая операция — требует явной синхронизации чего-то там и прочих неприятных вещей. Если для каждого объекта вызывать draw (которых в RTS, например, могут быть тысячи) — работать такой код может намного дольше, чем код, в который мы скопом передаём все вершины сцены.
Эта техника обсуждалась тут, тут и тут. Везде, короче.

2. Неэффективный по памяти. Можно отправить на видеоустройство громадный VAO, хранящий все вершины мира — с дублированием вершин для каждого объекта модели. Таким образом треугольники всех объектов пойдут на отрисовку одновременно и этот процесс будет легче распараллелить. Но таким образом видеопамять будет забивать по самое не хочу бессмысленно дублируемыми данными. Такой метод, думаю, вообще нигде не используется.

3. Индексирование. В openGL есть возможность индексирования вершин. В случаи использования этого метода, вместо атрибутов, привязываемых через VAO, можно отсылать массив индексов вершин, по которым будут формироваться треугольники объектов. В этом случаи дублирование данных всё равно будет — дублирование элементов индексных массивов (см. картинку, индексы в массиве мировых моделей будут от 0 до N, где N — это количество вершин модели). Но места индексы будут занимать меньше места.

Ещё одна проблема — отсылка информации о матрицах преобразования для объектов (на картинке — строчка transforms). Ясно что передавать матрицу с каждой вершиной будет избыточно (для каждой вершины одного объекта модели используется одна и та же матрица преобразования). С другой стороны, если опять-таки сделать что-то вроде индексации. То есть отсылать матрицы в большом UBO и к индексам вершин привязывать индекс матрицы преобразования соответствующей модели (не уверен что знаю как это сделать). К тому же, так как объекты могут создаваться и исчезать достаточно активно в течение сцены, использование UBO может тормозить процесс рендера (насколько я понимаю, UBO грузить на видяху дольше чем VAO).

Сам вопрос. Точнее, вопросы.

1. Действительно ли команда draw настолько дорога? По логике должна быть дорога — ведь если мы рисуем сцену маленькими порциями по несколько сот треугольников вместо отрисовки сразу миллионов треугольников всей сцены — отрисовку сложнее запараллелить.

2. Если ответ на первый вопрос «Да», то есть ли команда рисования в openGL, принимающая на вход массив промежутков индексов (в формате «индекса начала — индекс конец») и вызывающая пайпалайн для этих промежутков так, как glDrawArrays вызывается для одного VAO, заданного подряд идущими, не разнесёнными в памяти вершинами. Блин, звучит, кажется, вообще непонятно. Я имею в виду что-то вроде такого:

// В следующей строчка — код отсылки вершин моделей. Скорее всего, в едином VAO
// или UBO.
. . .

// В следующей строчке кода идёт отправка трансформаций моделей
// worldObjectTransforms (и, кстати, ещё какой-нибудь уникальной для моделей
// сопроводительной инфы) — предположительно через UBO.
. . .

// Тут делается магия. Пайплайн вызывается для всех вершин, находящихся
// в промежутках заданных через worldObjectRanges. При нужно чтобы в вершинный
// шейдер передавался индекс элемента worldObjectRanges, на основе которого шейдер
// был вызван для данной вершины (фактически, это будет индекс объекта); это нужно
// чтобы по индексу из UBO с трансформациями моделей получить трансформацию
// рисуемой в шейдере модели.
glDrawIndexedRanges(worldObjectRanges);

3. Ну, и если это все мои размышления полная фигня — где можно почитать про техники эффективной отрисовки реальных сцен с многими моделями с помощью openGL?

Opengl рисуем простые объекты

В статье мы научимся использовать систему событий SDL2 и рисовать фигуры средствами OpenGL в блоках glBegin/glEnd.

Содержание


CoreProfile и CompatibilityProfile

Начиная с OpenGL 3.0, стандарт OpenGL описывает два способа создания контекста:

  • Compatibility Profile — контекст, совместимый с OpenGL 1.x и 2.x
  • Core Profile — контекст, в котором большинство функций версий 1.x и 2.x не действуют, и доступна лишь современная высокопроизводительная модель рисования

Конечно, Core Profile гораздо больше подходит для современных приложений. Но у него высокий порог вхождения, и поэтому мы сначала освоимся в применении Compatibility Profile, где будем можно смешивать функциональность разных версий OpenGL.

Чтобы использовать Compatibility Profile, следует установить глобальный флаг SDL2 до создания контекста OpenGL:

Трансляция событий ввода SDL2

SDL2 вводит единую абстракцию для разных устройств и операционных систем: Windows, MacOSX, Linux, Android, iOS. Есть существенные отличия в устройствах ввода на этих системах.

Например, на мобильных устройствах нет мыши и нет возможности навести курсор на кнопку, не нажимая её. Вместо событий мыши (англ. mouse events) используются события касания (англ. touch events), которые в целом соответствуют друг другу, но имеют различия. Для простоты мы пока что будем опираться на модель событий ввода настольных компьютеров, имеющих мышь и клавиатуру.

SDL2 передаёт события в виде union, а не структуры, в целях экономии памяти. В этот обобщённый union вложены структуры, предназначенные для различных категорий событий:

Чтобы выполнить диспетчеризацию событий в отдельные ветви выполнения, подойдёт инструкция switch . Чтобы не писать одинаковый и потенциально ошибочный код со switch в разных местах, вынесем диспетчеризацию отдельно от CAbstractWindow .

Листинг DispatchEvent.h

Реализуем транслятор ввода

Перебор возможных типов события может видоизменяться в зависимости от нужд и модели работы приложения. В данный момент мы не будем заниматься трансляцией touch events в mouse events и обеспечим только базовую диспетчеризацию.

Теперь получается, что мы можем обработать событие SDL_QUIT в пределах класса CAbstractWindow и рассылаем события ввода по категориям в дочерних классах, унаследованных от CAbstractEventDispatchWindow .

Изменение размеров окна

Теперь нам пора изменить класс CAbstractWindow , чтобы получить нормальную рамку окна без фиксации размера окна. Прежде всего, при создании окна надо указать новый флаг SDL_WINDOW_RESIZABLE :

Теперь мы должны добавить обработку оконных событий, помеченных категорией SDL_WINDOWEVENT . Данные этой категории событий хранятся в структуре SDL_WindowEvent , у которой есть поле event , описывающее подкатегорию события окна. Для события изменения размера event == SDL_WINDOWEVENT_RESIZED , а поля data1 и data2 хранят новые значения ширины и высоты соответственно.

Чтобы не нагружать обработкой этих событий метод CAbstractWindow::DoGameLoop , сделаем следующее: добавим классу CAbstractWindow::Impl свойство IsTerminated , которое указывает, был основной цикл окна остановлен или нет. Теперь мы можем переписать DoGameLoop :

Метод Impl::ConsumeEvent реализован следующим образом:

Размер окна следует всего лишь запомнить, изменение самого окна операционная система и SDL2 совершат самостоятельно. Далее размер окна будет передан в метод OnDrawWindow(const glm::ivec2 &size) , который вызывается при рисовании очередного кадра — именно так класс CWindow узнает о размерах области рисования.

Порт просмотра, матрица проецирования

OpenGL рассчитан на проецирование трёхмерной сцены на плоскость объектива виртуальной камеры и не имеет специального режима для 2D. Поэтому мы должны настроить симуляцию трёхмерной камеры, чтобы получить ортографическое проецирование плоскости, в которой мы рисуем фигуры, на клиентскую область окна программы.

В OpenGL всё рисование происходит в трёхмерных координатах. Затем, в процессе обработки данных, видеодрайвер преобразует все поверхности и другие объекты виртуального трёхмерного мира в нормализованную систему координат. Существует два основных способа преобразования:

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

Нормализованная система координат устройства – система координат, получаемая после выполнения операции перспективного или ортографического преобразования, в которой левому краю видового порта соответствует координатная плоскость X=-1, правому краю – X=1, нижнему краю – Y=-1, верхнему – Y=1, ближней плоскости отсечения – Z=-1, а дальней – Z=+1. Для установки матрицы ортографического преобразования следует сделать три вещи:

  • переключить режим операций с матрицами с GL_MODELVIEW, включённого по умолчанию, на GL_PROJECTION вызовом функции-команды glMatrixMode(GL_PROJECTION)
  • создать или загрузить готовую матрицу ортографического преобразования
  • переключить режим операций с матрицами обратно на GL_MODELVIEW вызовом функции-команды glMatrixMode(GL_MODELVIEW)

При помощи преобразования в порт просмотра координаты вершин примитивов из нормализованной системы координат устройства преобразовываются в оконные координаты. Порт просмотра (англ. viewport) обычно занимает всю клиентскую область окна, но это не обязательно. Установка размеров видового порта в координатах клиентской области окна производится функцией-командой glViewport(x, y, width, height) .

Для установки видового порта и матрица проецирования введём вспомогательный метод SetupView , принимающий один параметр — размер окна в виде вектора 2-х целых чисел.

1-я реализация SetupView

Здесь мы воспользовались встроенными в OpenGL 1.x функциями для работы с матрицами. Существует целое семейство таких функций:

  • glLoadIdentity() для загрузки единичной матрицы
  • glTranslatef() и glTranslated() для трансформации перемещения
  • glRotatef() и glRotated() для трансформации вращения
  • glScalef() и glScaled() для трансформации масштабирования
  • glOrtho() для матрицы ортографического проецирования
  • glFrustum() для матрицы перспективного проецирования
  • glPushMatrix() для сохранения матрицы на вспомогательный стек
  • glPopMatrix() для восстановления матрицы из вспомогательного стека
  • glLoadMatrixf() и glLoadMatrixd() для явной установки матрицы

Перепишем код на GLM

К сожалению, в будущем мы потеряем возможность прямой работы с матрицами: в Core Profile эта функциональность не работает. Поэтому мы постараемся максимально избегать работы с матрицами средствами OpenGL, и применим только две функции-команды: glMatrixMode и glLoadMatrixf . Всё остальное сделаем с помощью библиотеки GLM.

2-я реализация SetupView

Immediate mode в OpenGL

Основные особенности OpenGL до версии 3.0 — это фиксированный конвейер (англ. fixed graphics pipeline) и безотлагательный режим исполнения (англ. immediate mode).

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

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

Рисование контура звезды

Следующим нашим шагом будет рисование синей пятиконечной звезды. Звезда будет задана в виде замкнутой ломаной линии из пяти вершин:

OpenGL 1.x позволяет рисовать группы различных примитивов путем перечисления вершин примитивов в нужном порядке между вызовами функций glBegin и glEnd. Координаты вершин задаются при помощи семейства функций glVertex.

Вершины нашей пятиконечной звезды располагаются на окружности в точках, соответствующих углам -90°, 54°, 198°, 342° и 486° (126°) – каждая последующая вершина сдвинута на 360 * 2 / 5 = 144° относительно предыдущей. Оформим рисование пятиконечной звезды в виде функции Stroke5PointStar:

Если после вызова этой функции с подходящими аргументами звезда всё ещё не нарисована, следует проверить:

  • нет ли ошибок, возвращённых запросом glGetError
  • правильно ли настроена матрица ортографического проецирования
  • установлен ли цвет рисования, отличный от цвета заливки. Установить цвет рисования внутри glBegin/glEnd можно функцией glColor3f.

Рисование контура эллипса

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

Рисование залитого цветом эллипса

Функция FillEllipse рисования эллипса использует группу примитивов GL_TRIANGLE_FAN (веер из треугольников) для рисования закрашенного эллипса. Способ задания вершин группы примитивов Triangle Fan показан на следующем рисунке:

Кибер-Сайт

Приветствую Вас Гость | RSS
Главная » 2013 » Октябрь » 24 » Рисование примитивов в OpenGL. Урок 4

Рисование примитивов в OpenGL. Урок 4

Сегодня мы поговорим о том, как рисовать примитивы средствами OpenGL.


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

Начало и конец рисования примитивов

Все примитивы в OpenGL задаются с помощью команд, которые расположены между командами glBegin и glEnd – начало и конец рисования примитивов.

Процедура содержит параметр mode, который задает флаг примитива.

Параметр mode может содержать один из следующих флагов

  • GL_POINTS – точки
  • GL_LINES, GL_LINE_LOOP – линии
  • GL_TRIANGLES, GL_LINE_STRIP, GL_TRIANGLE_FAN – треугольники
  • GL_QUADS, GL_QUAD_STRIP – квадраты
  • GL_POLYGON – полигоны

Команда glEnd не содержит параметров.

Команды рисования примитивов

Рисование примитивов в OpenGL осуществляется с помощью команд задания вершин: glVertex*. И команд задания их атрибутов: glColor* — цвета, glNormal* — нормали, glTexCoord* — текстурные координаты. Так как атрибуты glNormal* и glTexCoord* нам пока не нужны, остановимся на командах задания вершин и цветов.

Команды задания вершин

Команда glVertex задает координаты вершин и может принимать следующие виды

Для двухмерной системы координат
procedure glVertex2d (x,y: GLdouble);
procedure glVertex2dv (v: PGLdouble);
procedure glVertex2f (x,y: GLfloat);
procedure glVertex2fv (v: PGLfloat);
procedure glVertex2i (x,y: GLint);
procedure glVertex2iv (v: PGLint);
procedure glVertex2s (x,y: GLshort);
procedure glVertex2sv (v: PGLshort);

Для трехмерной системы координат
procedure glVertex3d (x,y,z: GLdouble);
procedure glVertex3dv (v: PGLdouble);
procedure glVertex3f (x,y,z: GLfloat);
procedure glVertex3fv (v: PGLfloat);
procedure glVertex3i (x,y,z: GLint);
procedure glVertex3iv (v: PGLint);
procedure glVertex3s (x,y,z: GLshort);
procedure glVertex3sv (v: PGLshort);

Для четырехмерной системы координат
procedure glVertex4d (x,y,z,w: GLdouble);
procedure glVertex4dv (v: PGLdouble);
procedure glVertex4f (x,y,z,w: GLfloat);
procedure glVertex4fv (v: PGLfloat);
procedure glVertex4i (x,y,z,w: GLint);
procedure glVertex4iv (v: PGLint);
procedure glVertex4s (x,y,z,w: GLshort);
procedure glVertex4sv (v: PGLshort);

Все эти команды задают координаты вершин в различной вариации, в зависимости от числа после glVertex и типов данных параметров. Если в команде после glVertex стоит число 2 то команда задает координаты X и Y вершины. Если число 3, то X, Y и Z. Если 4, то X, Y, Z, W. Если в конце стоит символ v, то он означает векторную команду. То есть, параметр задается с помощью указателя на 2, 3 или 4 координаты, которые могут храниться, например, в одномерном массиве.

Но, не смотря на разнообразие команд все координаты, в конечном итоге, будут преобразованы OpenGL к четырехмерной системе.

Скажем, если вы задаете вершины с помощью glVertex2*, то все Z координаты будут равны нулю, а все координаты W будут равны единице. То же самое будет с координатой W при использовании команд glVertex3*.

Команды установки цвета вершин

Команда glColor может принимать следующие виды

Режим RGB
procedure glColor3b (red, green, blue: GLbyte);
procedure glColor3bv (v: PGLByte);
procedure glColor3d (red, green, blue: GLdouble);
procedure glColor3dv (v: PGLdouble);
procedure glColor3f (red, green, blue: GLfloat);
procedure glColor3fv (v: PGLfloat);
procedure glColor3i (red, green, blue: GLint);
procedure glColor3iv (v: PGLint);
procedure glColor3s (red, green, blue: GLshort);
procedure glColor3sv (v: PGLshort);
procedure glColor3ub (red, green, blue: GLubyte);
procedure glColor3ubv (v: PGLubyte);
procedure glColor3ui (red, green, blue: GLuint);
procedure glColor3uiv (v: PGLuint);
procedure glColor3us (red, green, blue: GLushort);
procedure glColor3usv (v: PGLushort);

Режим RGBA
procedure glColor4b (red, green, blue, alpha: GLbyte);
procedure glColor4bv (v: PGLbyte);
procedure glColor4d (red, green, blue, alpha: GLdouble);
procedure glColor4dv (v: PGLdouble);
procedure glColor4f (red, green, blue, alpha: GLfloat);
procedure glColor4fv (v: PGLfloat);
procedure glColor4i (red, green, blue, alpha: GLint);
procedure glColor4iv (v: PGLint);
procedure glColor4s (red, green, blue, alpha: GLshort);
procedure glColor4sv (v: PGLshort);
procedure glColor4ub (red, green, blue, alpha: GLubyte);
procedure glColor4ubv (v: PGLubyte);
procedure glColor4ui (red, green, blue, alpha: GLuint);
procedure glColor4uiv (v: PGLuint);
procedure glColor4us (red, green, blue, alpha: GLushort);

Как и в случае с командами вершин все цвета задаются в зависимости от типов параметров glColor*. Тройка после glColor означает, что команда имеет 3 параметра, red, green и blue (красный, зеленый и синий). Четверка – red, green, blue и alpha (красный, зеленый, синий и альфа), соответственно. Символ v в конце означает векторный тип данных (указатель на 3 или 4 параметра). Команда glColor4* имеет смысл только тогда, когда включен режим прозрачности.

Не зависимо от того, как мы задаем цвет все параметры будут преобразованы OpenGL в, красный, зеленый, синий и альфа каналы. При указании цвета с помощью команд glColor3* альфа будет равно единице.

Рассмотрим следующий пример

glColor3ub(255, 0, 0);

glColor3ub($FF, $00, $00);

Все строки задают один и то же красный цвет разными способами.

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

Аналогичным способом можно задать синий и зеленый цвета

glColor3ub(0, 255, 0);

glColor3ub($00, $FF, $00);

glColor3ub(0, 0, 255);

glColor3ub($00, $00, $FF);

Чтобы задать другие цвета следует комбинировать red, green и blue.

А сейчас вернемся к примеру прошлого урока. Цвета вершин там заданы с помощью типизированных констант.

Чтобы задать цвет достаточно вызвать команду glColor3ubv с указателем на нужную константу. Например

Данный способ является достаточно удобным и наглядно демонстрирует установку цвета.

Порядок указания вершин. Направление граней

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

Для указания направления граней существует команда glFrontFace

Где параметр mode может принимать значения: GL_CCW – ориентация против часовой стрелки и GL_CW – ориентация по часовой стрелке.

Если вершины примитивов у вас заданы по часовой стрелке, то следует вызвать команду glFrontFace с параметром GL_CW.

Рисование точек

Рассмотрим процедуру Points

Здесь, между командами glBegin и glEnd вызывается 4 команды glVertex2f, которые и указывают точки. Команда glBegin с параметром GL_POINTS включает рисование точек. Перед указанием каждой вершины (glVertex2f) указаны цвета этих вершин, (команда glColor3ubv). В результате, у нас получилось 4 точки, закрашенные в, красный, зеленый, синий и желтый цвета (см. рисунок).

Рисование линий

Рассмотрим процедуру Lines

Команда glBegin с параметром GL_LINES включает рисование линий. Для рисования линии нужно указать 2 вершины. Но можно указать и большее количество. Тогда OpenGL нарисует n линий подряд. Наша же процедура рисует 4 линии, закрашенные в красный, синий, зеленый и желтый цвета соответственно (см. рисунок).

Рисование треугольников

Рассмотрим процедуру Triangle

Для включения рисования треугольников мы вызываем команду glBegin с параметром GL_TRIANGLES. Рисуем треугольник с помощью трех команд glVertex2f, которые задают его вершины. Если вам нужно нарисовать 2 или более треугольников, то вы можете указать все вершины подряд, как в случае с линиями. Обратите внимание на цвета вершин треугольника. Перед указанием каждой вершины (glVertex2f) мы вызываем glColor3ubv каждый раз с новым цветом. В результате у нас получается треугольник с вершинами, которые закрашены в, красный, зеленый и синий цвета, плавно перетекающие друг в друга (см. рисунок).

Данный способ заливки может применяться ко всем плоским примитивам, а также к линиям.

Рисование квадратов

Рассмотрим процедуру Quad

Чтобы начать рисование квадратов вызываем glBegin с параметром GL_QUADS. Рисование квадратов почти ничем не отличается от рисования треугольников, только вместо трех здесь четыре вершины. И, также, как в случае с треугольником мы можем нарисовать более одного квадрата между glBegin и glEnd.

Рисование полигонов

Рассмотрим процедуру Polygon

Для рисования полигона вызываем команду glBegin с параметром GL_POLYGON. В отличии от всех вышеперечисленных примитивов, здесь между glBegin и glEnd, можно нарисовать только один полигон. Рисовать можно от 3 до n вершин, что дает возможность рисовать более сложные формы. Наш же пример рисует пятиугольник, с вершинами, закрашенными в разные цвета.

Следует отметить, что OpenGL рисует только выпуклые полигоны. Это значит, что отдельные вершины не должны заходить за соседние. Таким образом, попытка нарисовать, например, пятиконечную звезду с помощью полигона не приведет к корректному результату.

Если вам нужно раскрасить один либо несколько примитивов одним цветом, то вы можете вызвать команду glColor, с нужными параметрами, всего один раз и все последующие вершины/примитивы будут раскрашены этим цветом.

Opengl рисуем простые объекты

На прошлом уроке мы настроили OpenGL и теперь что-нибудь нарисуем в нашем окне. Наш код будет размещаться в функции display() между glClear() и glFlush(). Сначала нарисуем треугольник. Зададим цвет с помощью функции glColor3ub(). Эта функция принимает три параметра (красный, зеленый, синий), каждый из которых в диапазоне (0; 255). Например так: glColor3ub( 145, 30, 66). Теперь напишем сам фрагмент кода рисования.


Функции glBegin() и glEnd() отвечают за начало и конец рисования соответственно. Параметр GL_TRIANGLES сообщает, что мы будем рисовать треугольник, три раза вызывая glVertex3f(). Функция glVertex3f() принимает значения x,y,z в формате float. Значения x и y лежат в диапазоне (-1; 1). Мы задали три вершины с координатами: (0; 0.8), (-0.4; 0.4), (0.4; 0.4). Третий параметр отвечает за ось z. Он равен 0, так как у нас 2d фигуры.

Теперь нарисуем прямоугольник. Будем использовать функцию glRectf(). Она принимает координаты левого верхнего угла и правого нижнего. Также зададим другой цвет.

У нас получился домик). Теперь еще добавим основание. Для этого нарисуем отрезок с толщиной, например, 50. Также зададим другой цвет.

Для рисования отрезка достаточно двух точек. В нашем случае (-0.9; 0) и (0.9; 0).

У нас получился симпатичный домик). И теперь полный код программы.

Урок 2 — Рисуем первый треугольник

Рисуем первый треугольник с использованием OpenGL 3.3

Введение

Если вы уже работали с версиями OpenGL младше 3.0, то наверняка вам приходилось использовать конструкции вид glBegin() / glEnd():

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

Расширения

Из первого урока вы узнали как создать контекст с поддержкой OpenGL 3.3, однако одного контекста мало, необходимо еще получить доступ к функционалу OpenGL 3.3 Для этого предусмотрен специальный механизм получения указателей на функции из драйвера видеокарты, который был мельком упомянут в предыдущем уроке, для Windows это функция wglGetProcAddress.

Стоит также оговориться почему нам необходима функция wglGetProcAddress, дело в том что при сборке приложения в Windows нам доступна только библиотека с поддержкой функционала OpenGL 1.1, а все функции из старших версий мы должны загружать уже в процессе работы приложения, динамически. Не во всех ОС это так, например в Linux производители драйверов для видеокарт распространяют библиотеку с поддержкой именно того функционал, который есть в драйвере, его можно подключать уже в момент сборки приложения, статически. Но даже там есть возможность загружать расширения в процессе работы приложения, сделано это для того, чтобы бинарные файлы собранные на одном компьютере могли быть запущены на другом, возможно с совершенно другим драйвером.

Кстати, называть «расширениями» функции находящиеся в ядре OpenGL 3.3 не совсем верно, это уже не расширение функционала, это стандартный набор для версии 3.3, однако по старой традиции обычно все функции которые надо грузить динамически называют «расширениями».

Динамическая загрузка функция достаточно проста, вы уже видели как используется функция wglGetProcAddress в прошлом уроке — надо создать контекст OpenGL нужной версии и можно приступать к загрузке функций. Для динамической загрузки функций нам понадобится файл с прототипами этих функций glext.h. Этот свободно доступен с сайта opengl.org и с выходом новых версий стандарта OpenGL он обновляется. В архиве с исходниками к этому уроку этот файл уже есть.

Используя прототипы мы определим указатели на функции и после создания контекста с поддержкой OpenGL 3.3 загрузим их динамически. Выглядит это так:

В исходниках приложенных к этому уроку процесс получения указателя и проверки на успех немного автоматизирован с использованием макроса OPENGL_GET_PROC:

Также существуют различные готовые библиотеки, которые позволяют еще больше автоматизировать работу с расширениями, например библиотека GLEW. Однако в наших уроках нм не понадобиться большой список расширений, поэтому будем использовать «ручной» способ загрузки, представленный выше.

Шейдеры

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

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

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

Шейдеры для OpenGL пишутся с использованием специального языка GLSL, на который имеется спецификация (доступна с сайта opengl.org). Мы будем использовать GLSL 3.30, эта версия GLSL вышла одновременно с выходом стандарта OpenGL 3.3.

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

Входные атрибуты обозначены как in, а выходные атрибуты как out, юниформы обозначаются как uniform. Выходные атрибуты предназначены для передачи данных из шейдера на следующий этап обработки данных, в нашем случае следующий этап это фрагментный шейдер:

Здесь имеется выходной атрибут color, он определяет какого цвета будет пиксель на экране. Стоит также отметить что фрагментный шейдер получает интерполированные данные из предыдущего вершинного шейдера, каким образом данные будут интерполироваться можно указать отдельно для каждого атрибута, по-умолчанию это обычная линейная интерполяция. Таким образом, в случае с атрибутом fragmentColor, цвет меняется «линейно» от вершины к вершине.

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

Полностью создание шейдерной программы происходит следующим образом:

Для проверки статусов шейдера и шейдерной программы используется небольшая функция-обертка, которая в случае ошибки выводит текст этой ошибки в лог-файл:

Вершинный буфер и его связь с вершинными атрибутами

Для хранения данных о вершинах геометрии в OpenGL существует специальный объект называемый Vertex Buffer Object, коротко VBO. VBO позволяет создать буфер в памяти вдиеокарты и поместить туда необходимые нам данные. Вершинный буфер создается с подсказкой, как часто мы будем менять данные в этом буфере.

Однако создания одного VBO недостаточно, необходимо создать еще один специальный объект Vertex Array Object, коротко VAO. VAO хранит связи между параметрами вершинных атрибутов и источниками данных в VBO. В VAO хранится из какого VBO какие атрибуты берутся, тип и размер этих атрибутов, смещение в буфере VBO до начала данных для этих атрибутов. Есть одно исключение — VBO для хранения индексов, к VAO можно присоединить только один такой VBO, но про индексный буфер будет рассказано в отдельном уроке.

Одно из удобств VAO, когда он будет настроен, не надо подключать различные VBO в которых хранятся атрибуты вершин, можно один раз подключить VAO и можно приступать к выводу геометрии.

В этом уроке мы используем VBO для хранения всего трех вершин для нашего треугольника, менять мы их не собираемся, поэтому данные поместим в VBO всего один раз, при инициализации OpenGL, там же создадим VAO:

Передача атрибутов и юниформов в шейдер

Чтобы передать какие-то данные в шейдерную программу из нашего приложения необходимо использовать атрибуты и юниформы, общий алгоритм передачи данных такой:
# Получить индекс атрибута или юниформа в шейдере
# Передать по этому адресу данные из программы

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

У атрибутов есть одна особенность — мы можем использовать функцию glBindAttribLocation для назначения своих индексов атрибутам прежде чем вызвать функцию glLinkProgram. Можно и не устанавливать свои индексы, шейдер это сделает за нас, именно так и сделано в исходных кодах к этому уроку.

Когда шейдерная программа собрана, индексы интересующих нас атрибутов и юниформов получены и в VBO скопированы данные геометрии — можно настроить параметры вершинных атрибутов:

Матрицы

В данном уроке нам понадобится только одна матрица — перспективная матрица проекции. Она нам необходима, чтобы переводить позиции вершин в однородные координаты (clip space). Раньше установка матрицы проекции делалась достаточно просто, сначала мы говорили, что хотим поменять матрицу проекции командой glMatrixMode(GL_PROJECTION), а потом указывали параметры матрицы проекции, например для создания перспективной матрицы проекции использовалась функция gluPerspective(fov, aspect, znear, zfar).

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

В итоге в массиве M у нас будет готовая перспективная матрица. Именно эту матрицу мы будем передавать в шейдер как юниформ projectionMatrix:

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

Подробнее про матрицы и трансформации будет рассказано в следующем уроке, где мы подробней остановимся на системе преобразования вершинных координат принятой в OpenGL и других GAPI.

Вывод треугольника

Наконец мы подошли к последнему шагу нашего урока — вывод треугольника на экран. Совместив все предыдущие занния мы можем создать шейдер, загрузить его, установить нужные параметры, скопировать данные геометрии в буфер видеокарты, установить матрицу и наконец приступать к рендерингу треугольника:

Использование VAO

Согласно стандарту OpenGL версии 3.3 операции над вершинными атрибутами без активного VAO более не допускаются (секция E стандарта). Указано, что VAO по-умолчанию (индекс 0) считается устаревшим и при использовании функции glVertexAttribPointer без текущего активного VAO будет выдана ошибка INVALID_OPERATION. На момент написания урока в драйверах ATI именно так и сделано, однако nVidia все еще позволяет пользователю не создавать VAO.

Полезные ссылки

Исходный код

Доступ к исходному коду уроку с проектом для MSVC можно получить двумя способами:

  • Используя SVN lesson02
  • Скачав архив lesson02.zip

Графическая библиотека opengl

Лабораторная работа № 2.

OpenGL – Open Graphics Library, открытая графическая библиотека. Термин «открытый» – означает независимый от производителей. Имеется спецификация (стандарт) на OpenGL, где все четко задокументировано и описано. Библиотеку OpenGL может производить кто угодно. Главное, чтобы библиотека удовлетворяла спецификации OpenGL и ряду тестов. Как следствие, в библиотеке нет никаких темных мест, секретов, недокументированных возможностей и т. п, те кто программировал под MS Windows или MS DOS понимают о чем я говорю. Библиотеку выпускают такие корпорации, как Microsoft, Silicon Graphics, а также просто группы программистов. Одним из таких примеров служит реализация Mesa. Эту библиотеку написали целый ряд программистов, главным автором является Brian Paul. Библиотека Mesa распространяется в исходных текстах на языке Си и собирается почти для любой операционной системы. Стандарт OpenGL развивается с 1992 года. Он разрабатывается фирмой Silicon Graphics. С тех пор библиотека завоевала огромную популярность и была интегрирована с множеством языков и систем разработки приложений. Вы можете писать программу с использованием OpenGL на Си, С++, Pascal, Java и многих других языках. Основное предназначение OpenGL программирование трехмерной графики.

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

Самым простым объектом, с помощью которого можно увидеть всю мощь OpenGL, является сфера. Можно попытаться ее изобразить. Для этого надо создать новый проект в VisualC++, выполнив следующие действия:

— запустить Visual C++;

— выполнить команду File | New…;

— в открывшемся диалоговом окне выбрать тип проекта Win32 Application, в поле Project Name указать имя проекта, а в поле Location – папку в которой будет находиться проект;

— нажать кнопку OK. Затем, ничего не изменяя, – кнопку Finish и еще раз OK;

— скопировать в папку проекта два файла Sample. cpp и Sample. h (выдаются преподавателем);


— подключить эти два файла к проекту выполнив команду Project | Add To Project | Files…;

— далее выполнить команду Project->Settings, выбрать закладку Link и добавить в поле Object/library modules следующие lib-файлы: opengl32.lib, glu32.lib и glaux. lib;

— вставьте в функцию Display следующий код:

— создать exe-модуль (F7).

Поясним назначение вставленных двух функций. Функция glColor3d устанавливает текущий цвет, которым будут рисоваться фигуры. Тут нужно пояснить, как устанавливается цвет и общую философию в названии функций OpenGL. Цвет устанавливается четырьмя параметрами: красный, синий, зеленый и прозрачность. Эти параметры варьируются в диапазоне от нуля до единицы. Четвертый параметр нам пока не нужен, поэтому мы вызвали glColor с тремя параметрами. В этом случае, значение четвертого параметра, прозрачности, по умолчанию считается равным единице, т. е. абсолютно непрозрачным, ноль – будет абсолютно прозрачным. Применяется следующий синтаксис вызова функций – FuncionName[n=число параметров][тип параметров].

Доступны следующие типы:

S – GLshort короткое целое;

F – GLfloat дробное;

D – GLdouble дробное с двойной точностью;

Ub – GLubyte беззнаковый байт;

Us – GLushort беззнаковое короткое целое;

Ui – GLuint беззнаковое целое;

V – массив из n параметров указанного типа;

В нашем случае – glColor3d – означает, что в функцию передается три параметра типа GLdouble. Также можно было вызвать glColor3i, т. е. три параметра типа GLint. Если тип параметров короткое целое, целое, байт или длинное, то компонента цвета приводится к диапазону [0,1]. Приведение к этому диапазону осуществляется по следующим правилам. В случае беззнакового типа возможное наибольшее значение приводится к единице, ноль к нулю. В случае знакового максимальное значение приводится к единице или к минус единице, а минус единица будет приведена к единице. На практике обычно пользуются одним из трех случаев, рассмотренных в качестве примера ниже. Например, для беззнакового байта приведение будет осуществлено по следующей формуле: значение_переменной_хранимой_в_байте/255, т. к. 255 максимальное число, хранимое в одном байте. Функция glColor3dv означает, что в качестве параметров передается массив из трех элементов типа GLdouble. Например:

GlColor3ub(200,100,0); // приводится к

// 200/256, 100/256, 0,256

3. Простые объекты. Общие положения

Точки, линии, треугольники, четырехугольники, многоугольники –простые объекты, из которых состоят любые сложные фигуры. OpenGL непосредственно не поддерживает функций для создания таких сложных объектов как сфера, цилиндр тор и др., т. е. таких функций нет в opengl32.dll. Эти функции есть в библиотеки утилит glu32.dll, и устроены они следующим образом. Для того чтобы нарисовать сферу функция auxSolidSphere использует функции из библиотеки glu32.dll, а те в свою очередь, используют базовую библиотеку opengl32.dll и из линий или многоугольников строят сферу. Примитивы создаются следующим образом:

GlBegin(mode); // указываем, что будем рисовать

glVertex[2 3 4][s i f d](…); // первая вершина

… // тут остальные вершины

glVertex[2 3 4][s i f d](…); // последняя

GlEnd(); // закончили рисовать примитив

Сначала необходимо указать начало рисования – glBegin с соответствующим параметром.

Возможные значения mode перечислены ниже в таблице. Далее указываются вершины, определяющие объекты указанного типа. Обычно вершину задают одним из четырех способов.

GlVertex2d(x, y); // две переменных типа double

GlVertex3d(x, y,z); // три переменных типа double

GlVertex2dv(array); // массив из двух переменных типа

GlVertex3d(array); // массив из трех переменных типа

Указывает на конец рисования объектов типа, указанного в glBegin. Далее подробно разберем создание всех примитивов.

Возможные значения mode

Каждый вызов glVertex задает отдельную точку.

Каждая пара вершин задает отрезок.

Рисуется ломанная, причем ее последняя точка соединяется с первой.

Каждые три вызова glVertex задают треугольник.

Рисуются треугольники с общей стороной.

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

Каждые четыре вызова glVertex задают четырехугольник.

Четырехугольники с общей стороной.

Можно нарисовать сколько угодно точек, вызывая glVertex3d, и тем самым, устанавливая новую точку. При создании точек можно изменять следующие параметры. Можно вызывать glColor3d внутри glBegin/glEnd. Размер точки можно устанавливать с помощью функции:

Void glPointSize(GLfloat size);

Режим сглаживания можно устанавливать вызовом функции

Отключается соответственно вызовом glDisable(GL_POINT_SMOOTH). Последние функции – glPointSize и glEnable/glDisable надо вызывать вне glBegin/glEnd, иначе они будут проигнорированы. Функции glEnable/glDisable включают/выключают множество опций, но следует учитывать, что некоторые опции влекут за собой большие вычисления и, следовательно, изрядно затормаживают приложение, поэтому без надобности не стоит их включать.

glVertex3d(-4.5,4,0); // первая точка

glVertex3d(-4,4,0); // вторая точка

glVertex3d(-2,4,0); // первая точка

glVertex3d(-1,4,0); // вторая точка

glVertex3d(2,4,0); // первая точка

glVertex3d(3,4,0); // вторая точка

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

Void glLineStipple(GLint factor, GLushort pattern);

Второй параметр задает саму маску. Например, если его значение равно 255(0x00FF), то чтобы вычислить задаваемую маску воспользуемся калькулятором. В двоичном виде это число выглядит так: 0000000011111111, т. е. всего 16 бит. Старшие восемь установлены в ноль, значит, тут линии не будет. Младшие установлены в единицу, тут будет рисоваться линия. Первый параметр определяет, сколько раз повторяется каждый бит. Скажем, если его установить равным 2, то накладываемая маска будет выглядеть так:

Далее приведен исходный текст с комментариями для наглядной демонстрации.

Невозможно нарисовать несколько объектов в opengl

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

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

Я вижу, что вы пытаетесь использовать VAO, но вы создаете только одно. Также почему вы готовите:

glEnableVertexAttribArray (0); glBindBuffer (GL_ARRAY_BUFFER, VertexBuffer); glVertexAttribPointer (0,3, GL_FLOAT, GL_FALSE, 0, (недействительными *) 0);

glEnableVertexAttribArray (1); glBindBuffer (GL_ARRAY_BUFFER, colourBuffer); glVertexAttribPointer (1,3, GL_FLOAT, GL_FALSE, 0, (недействительными *) 0);

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

Что вы должны сделать, так это: Инициируйте ваши VAO в некотором методе init только один раз. Затем в цикле рендеринга:

Сделайте это для каждой геометрии, которую вы хотите визуализировать.

После более глубокого обзора кода я вижу, что вы это делаете:

Илон Маск рекомендует:  Что такое код ocisavelob
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL