Содержание блога
В предыдущей инструкции мы рисовали с помощью glBegin()/glEnd(). Этот способ устарел. Переходим на новый уровень. Здесь будем рисовать с помощью функции glDrawElements, а формировать объекты с помощью массивов вершин и индексов. Цвет будем задавать с помощью массива цветов. Это более удобный способ. Он позволяет легко накладывать текстуры, указав текстурные координаты (об этом в следующих инструкция)
Рисуем треугольник
- Создаём проект c диалоговым окном (имя проекта "Triangle") по инструкции: Первое оконное приложение на Qt
- Создаём площадку для рисования: Создание проекта с площадкой для рисования
- Нарисуем треугольник на бумаге, или например в Paint'е, и расставим номера вершин с обходом против часовой стрелки:

- Создаём заголовочный файл "Triangle.h". Для этого кликаем правой кнопкой мыши по имени проекта и выбираем: "Add New..." -> в левой колонке выбираем "C++" -> в средней колонке выбираем "C++ Header File" -> нажимаем кнопку "Choose..." -> вводим имя файла: Triangle -> нажимаем "Next" -> "Finish"
- Объявляем три массива: массив вершин, массив индексов и массив цветов. Заполняем в соответствии с рисунком выше. Скопируйте код:
Triangle.h
Кликните здесь для просмотра всего текста
| C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| #ifndef TRIANGLE_H
#define TRIANGLE_H
#include <vector>
class Triangle
{
public:
Triangle()
{
init();
}
void init()
{
// 0
vertices.push_back( 0 ); // X
vertices.push_back( 1 ); // Y
vertices.push_back( 0 ); // Z
// 1
vertices.push_back( -1 ); // X
vertices.push_back( -1 ); // Y
vertices.push_back( 0 ); // Z
// 2
vertices.push_back( 1 ); // X
vertices.push_back( -1 ); // Y
vertices.push_back( 0 ); // Z
indices.push_back( 0 );
indices.push_back( 1 );
indices.push_back( 2 );
for ( unsigned int i = 0; i < 3; ++i ) {
colors.push_back( 0 ); // R
colors.push_back( 1 ); // G
colors.push_back( 0 ); // B
}
}
std::vector<int> vertices;
std::vector<unsigned int> indices;
std::vector<float> colors;
};
#endif // TRIANGLE_H |
|
- В файле "Scene.h" подключаем файл "Triangle.h". Объявляем функцию drawTriangle(). Создаём объект triangle класса Triangle. Скопируйте код:
Scene.h
| C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| #ifndef SCENE_H
#define SCENE_H
#include <QGLWidget>
#include "Triangle.h"
class Scene : public QGLWidget
{
public:
Scene( QWidget *parent = 0 );
private:
void initializeGL();
void paintGL();
void resizeGL( int w, int h );
void drawTriangle();
private:
Triangle triangle;
};
#endif // SCENE_H |
|
- Копируем содержимое файла "Scene.cpp". Читайте комментарии:
Scene.cpp
Кликните здесь для просмотра всего текста
| C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
| #include "Scene.h"
Scene::Scene( QWidget *parent ) :
QGLWidget( parent )
{
}
void Scene::initializeGL()
{
// Цвет для очистки буфера изображения - будет просто фон окна
glClearColor( 0.0f, 0.0f, 1.0f, 1.0f );
// Устанавливает режим проверки глубины пикселей
glEnable( GL_DEPTH_TEST );
// Отключает режим сглаживания цветов
glShadeModel( GL_FLAT );
// Устанавливаем режим, когда строятся только внешние поверхности
glEnable( GL_CULL_FACE );
// Активизация массива вершин
glEnableClientState( GL_VERTEX_ARRAY );
// Активизация массива цветов вершин
glEnableClientState( GL_COLOR_ARRAY );
}
void Scene::paintGL()
{
// Окно виджета очищается текущим цветом очистки
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
drawTriangle();
}
void Scene::resizeGL( int w, int h )
{
// Set Viewport to window dimensions
glViewport( 0, 0, w, h );
// Reset coordinate system
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
// Establish clipping volume (left, right, bottom, top, near, far)
glOrtho( -1, 1, -1, 1, 1, -1);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
}
void Scene::drawTriangle()
{
// Указываем, откуда нужно извлечь данные о массиве вершин
glVertexPointer( 3, GL_INT, 0, triangle.vertices.data() );
// Указываем, откуда нужно извлечь данные о массиве цветов вершин
glColorPointer( 3, GL_FLOAT, 0, triangle.colors.data() );
// Используя массивы вершин и индексов, строим поверхности
glDrawElements( GL_TRIANGLES, triangle.indices.size(), GL_UNSIGNED_INT, triangle.indices.data() );
} |
|
- Запускаем приложение. Мы видим треугольник
Рисуем Квадрат
- Мы можем создать новый проект с именем Square по примеру проекта "Рисуем треугольник". Либо мы можем добавить новый класс "Square" в предыдущий проект (в Scene нужно будет добавить функцию drawSquare). Либо мы можем создать копию папки нашего предыдущего проекта и переименовать её в Square (соответственно, все слова "triangle" нужно заменить на "square"). Я выбрал последний вариант
Примечание. Треугольник в OpengGL - это минимальная единица для рисования любой 2D и 3D фигуры (или поверхности). Этот способ рисования позволяет легко накладывать текстуры, задав массив текстурных координат, а об этом в следующих инструкциях
- Рисуем, например в Paint'е, квадрат, который состоит из двух треугольников. Нумеруем вершины, соблюдая обход против часовой стрелки:

- Создаём класс Square. В соответствии с рисунком выше заполняем массивы вершин, индексов и цветов:
Square.h
Кликните здесь для просмотра всего текста
| C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
| #ifndef SQUARE_H
#define SQUARE_H
#include <vector>
class Square
{
public:
Square()
{
init();
}
void init()
{
// Первый треугольник
// 0
vertices.push_back( -1 ); // X
vertices.push_back( -1 ); // Y
vertices.push_back( 0 ); // Z
// 1
vertices.push_back( 1 ); // X
vertices.push_back( -1 ); // Y
vertices.push_back( 0 ); // Z
// 2
vertices.push_back( -1 ); // X
vertices.push_back( 1 ); // Y
vertices.push_back( 0 ); // Z
// Второй треугольник
// 3
vertices.push_back( -1 ); // X
vertices.push_back( 1 ); // Y
vertices.push_back( 0 ); // Z
// 4
vertices.push_back( 1 ); // X
vertices.push_back( -1 ); // Y
vertices.push_back( 0 ); // Z
// 5
vertices.push_back( 1 ); // X
vertices.push_back( 1 ); // Y
vertices.push_back( 0 ); // Z
for ( unsigned int i = 0; i < 6; ++i ) {
indices.push_back( i );
}
for ( unsigned int i = 0; i < 6; ++i ) {
colors.push_back( 0 ); // R
colors.push_back( 1 ); // G
colors.push_back( 0 ); // B
}
}
std::vector<int> vertices;
std::vector<unsigned int> indices;
std::vector<float> colors;
};
#endif // SQUARE_H |
|
В классе Scene меняем в тексте программы "triangle" на "square" и запускаем приложение:
Scene.h
Кликните здесь для просмотра всего текста
| C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| #ifndef SCENE_H
#define SCENE_H
#include <QGLWidget>
#include "Square.h"
class Scene : public QGLWidget
{
public:
Scene( QWidget *parent = 0 );
private:
void initializeGL();
void paintGL();
void resizeGL( int w, int h );
void drawSquare();
private:
Square square;
};
#endif // SCENE_H |
|
Scene.cpp
Кликните здесь для просмотра всего текста
| C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
| #include "Scene.h"
Scene::Scene( QWidget *parent ) :
QGLWidget( parent )
{
}
void Scene::initializeGL()
{
// Цвет для очистки буфера изображения - будет просто фон окна
glClearColor( 0.0f, 0.0f, 1.0f, 1.0f );
// Устанавливает режим проверки глубины пикселей
glEnable( GL_DEPTH_TEST );
// Отключает режим сглаживания цветов
glShadeModel( GL_FLAT );
// Устанавливаем режим, когда строятся только внешние поверхности
glEnable( GL_CULL_FACE );
// Активизация массива вершин
glEnableClientState( GL_VERTEX_ARRAY );
// Активизация массива цветов вершин
glEnableClientState( GL_COLOR_ARRAY );
}
void Scene::paintGL()
{
// Окно виджета очищается текущим цветом очистки
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
drawSquare();
}
void Scene::resizeGL( int w, int h )
{
// Set Viewport to window dimensions
glViewport( 0, 0, w, h );
// Reset coordinate system
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
// Establish clipping volume (left, right, bottom, top, near, far)
glOrtho( -2, 2, -2, 2, 1, -1);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
}
void Scene::drawSquare()
{
// Указываем, откуда нужно извлечь данные о массиве вершин
glVertexPointer( 3, GL_INT, 0, square.vertices.data() );
// Указываем, откуда нужно извлечь данные о массиве цветов вершин
glColorPointer( 3, GL_FLOAT, 0, square.colors.data() );
// Используя массивы вершин и индексов, строим поверхности
glDrawElements( GL_TRIANGLES, square.indices.size(), GL_UNSIGNED_INT, square.indices.data() );
} |
|
- Если мы хотим увидеть каркас, то есть треугольники без закрашивания, то нужно в initializeGL() добавить эту строку:
| C++ | 1
| glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); |
|
Змейка
Переделал змейку из предыдущей инструкции. Изменения коснулись класса Painter и немного класса Scene. Просто заменил способ рисования glBegin()/glEnd() на glDrawElements В комментариях в коде - то что было:
Painter.h
Кликните здесь для просмотра всего текста
| C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| #ifndef PAINTER_H
#define PAINTER_H
#include <vector>
class Painter
{
public:
Painter();
void bar( int x1, int y1, int x2, int y2 );
void circle( int x, int y, int radius );
private:
void setBarVertices( int x1, int y1, int x2, int y2 );
void setBarIndices();
void setBarColor( float r, float g, float b );
std::vector<int> m_barVertices;
std::vector<unsigned int> m_barIndices;
std::vector<float> m_barColors;
void setCircleVertices( int x, int y, int radius );
void setCircleIndices();
void setCircleColor( float r, float g, float b );
std::vector<int> m_circleVertices;
std::vector<unsigned int> m_circleIndices;
std::vector<float> m_circleColors;
};
#endif // PAINTER_H |
|
Painter.cpp
Кликните здесь для просмотра всего текста
| C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
| #include "Painter.h"
#include <GL/gl.h>
Painter::Painter()
{
m_barVertices.resize( 6 * 3 );
m_barIndices.resize( 6 );
m_barColors.resize( 6 * 3 );
m_circleVertices.resize( 6 * 3 );
m_circleIndices.resize( 6 );
m_circleColors.resize( 6 * 3 );
}
void Painter::bar( int x1, int y1, int x2, int y2 )
{
setBarVertices( x1, y1, x2, y2 );
setBarIndices();
setBarColor( 0, 1, 0 );
// Указываем, откуда нужно извлечь данные о массиве вершин
glVertexPointer( 3, GL_INT, 0, m_barVertices.data() );
// Указываем, откуда нужно извлечь данные о массиве цветов вершин
glColorPointer( 3, GL_FLOAT, 0, m_barColors.data() );
// Используя массивы вершин и индексов, строим поверхности
glDrawElements( GL_TRIANGLES, m_barIndices.size(), GL_UNSIGNED_INT, m_barIndices.data() );
// glBegin( GL_QUADS );
// {
// glVertex2f( x1, y1 );
// glVertex2f( x2, y1 );
// glVertex2f( x2, y2 );
// glVertex2f( x1, y2 );
// }
// glEnd();
}
void Painter::circle( int x, int y, int radius )
{
setCircleVertices( x, y, radius );
setCircleIndices();
setCircleColor( 1, 0, 0 );
// Указываем, откуда нужно извлечь данные о массиве вершин
glVertexPointer( 3, GL_INT, 0, m_circleVertices.data() );
// Указываем, откуда нужно извлечь данные о массиве цветов вершин
glColorPointer( 3, GL_FLOAT, 0, m_circleColors.data() );
// Используя массивы вершин и индексов, строим поверхности
glDrawElements( GL_TRIANGLES, m_circleIndices.size(), GL_UNSIGNED_INT, m_circleIndices.data() );
// glColor3f( 1, 0, 0 );
// glBegin( GL_POLYGON );
// {
// glVertex2f( x + radius, y );
// glVertex2f( x, y + radius );
// glVertex2f( x - radius, y );
// glVertex2f( x, y - radius );
// }
// glEnd();
}
void Painter::setBarVertices( int x1, int y1, int x2, int y2 )
{
// Первый треугольник
// 0
m_barVertices[0] = x1;
m_barVertices[1] = y2;
m_barVertices[2] = 0;
// 1
m_barVertices[3] = x2;
m_barVertices[4] = y2;
m_barVertices[5] = 0;
// 2
m_barVertices[6] = x1;
m_barVertices[7] = y1;
m_barVertices[8] = 0;
// Второй треугольник
// 3
m_barVertices[9] = x1;
m_barVertices[10] = y1;
m_barVertices[11] = 0;
// 4
m_barVertices[12] = x2;
m_barVertices[13] = y2;
m_barVertices[14] = 0;
// 5
m_barVertices[15] = x2;
m_barVertices[16] = y1;
m_barVertices[17] = 0;
}
void Painter::setBarIndices()
{
for ( unsigned int i = 0; i < 6; ++i ) {
m_barIndices[i] = i;
}
}
void Painter::setBarColor( float r, float g, float b )
{
m_barColors[0] = r;
m_barColors[1] = g;
m_barColors[2] = b;
m_barColors[3] = r;
m_barColors[4] = g;
m_barColors[5] = b;
m_barColors[6] = r;
m_barColors[7] = g;
m_barColors[8] = b;
m_barColors[9] = r;
m_barColors[10] = g;
m_barColors[11] = b;
m_barColors[12] = r;
m_barColors[13] = g;
m_barColors[14] = b;
m_barColors[15] = r;
m_barColors[16] = g;
m_barColors[17] = b;
}
void Painter::setCircleVertices( int x, int y, int radius )
{
// 0
m_circleVertices[0] = x + radius;
m_circleVertices[1] = y;
m_circleVertices[2] = 0;
// 1
m_circleVertices[3] = x - radius;
m_circleVertices[4] = y;
m_circleVertices[5] = 0;
// 2
m_circleVertices[6] = x;
m_circleVertices[7] = y + radius;
m_circleVertices[8] = 0;
// 3
m_circleVertices[9] = x + radius;
m_circleVertices[10] = y;
m_circleVertices[11] = 0;
// 4
m_circleVertices[12] = x;
m_circleVertices[13] = y - radius;
m_circleVertices[14] = 0;
// 5
m_circleVertices[15] = x - radius;
m_circleVertices[16] = y;
m_circleVertices[17] = 0;
}
void Painter::setCircleIndices()
{
for ( unsigned int i = 0; i < m_circleIndices.size(); ++i ) {
m_circleIndices[i] = i;
}
}
void Painter::setCircleColor( float r, float g, float b )
{
m_circleColors[0] = r;
m_circleColors[1] = g;
m_circleColors[2] = b;
m_circleColors[3] = r;
m_circleColors[4] = g;
m_circleColors[5] = b;
m_circleColors[6] = r;
m_circleColors[7] = g;
m_circleColors[8] = b;
m_circleColors[9] = r;
m_circleColors[10] = g;
m_circleColors[11] = b;
m_circleColors[12] = r;
m_circleColors[13] = g;
m_circleColors[14] = b;
m_circleColors[15] = r;
m_circleColors[16] = g;
m_circleColors[17] = b;
} |
|
Исполняемый файл для Win7: https://yadi.sk/d/0MZdHSrIcnHxA
Исходники на Qt C++: https://github.com/8Observer8/... awElements
Если мы добавим в Scene::initializeGL() строку
| C++ | 1
| glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); |
|
то увидим каркас:

Продолжение: 4. Простой загрузчик wavefront (.obj) объектов из Blender на C++
|