Урок 2 - Простые примитивы

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

После строки

FillChar (pfd, SizeOf (pfd), 0);
Допишем:
pfd.dwFlags  := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER;
Мы сообщили системе, что будет использоваться двойная буферизация. Это необходимо для того чтоб изображение не мерцало.

В процедуре создания Формы напишите:
glEnable(GL_DEPTH_TEST); // включаем проверку разрешения фигур (впереди стоящая закрывает фигуру за ней)  
glDepthFunc(GL_LEQUAL); //тип проверки
Далее в обработчике события Form1 OnResize создадим процедуру перерисовки нашего окна.
glViewport(0, 0, ClientWidth, ClientHeight); //выделяем область куда будет выводиться наш буфер
glMatrixMode ( GL_PROJECTION ); //переходим в матрицу проекции
glLoadIdentity; //Сбрасываем текущую матрицу
glFrustum ( -1 , 1 , -1 , 1 , 1.25 , 100.0 ); //Область видимости
glMatrixMode ( GL_MODELVIEW ); // переходим в модельную матрицу
glLoadIdentity; //Сбрасываем текущую матрицу
gluLookAt(5,5,5,0,0,0,0,0,1); //позиция наблюдателя
InvalidateRect ( Handle,nil,False ); //перерисовка формы
Функция glFrustum имеет 6 параметров, первые 4 из них, это границы отсечения: верхняя, правая, нижняя, левая. Последние 2 параметра – это передняя и задняя граница отсечения.

Функция gluLookAt имеет 9 параметров, первые 3 из них - позиция наблюдателя (x, y, z), следующие 3 параметра – точка наблюдения (x, y, z) и последние 3 параметра – вектор верха.

InvalidateRect ( Handle,nil,False ); перерисовывает форму гораздо быстрее, чем Form1.Repaint.

Для того, чтобы наше окно обновлялось надо вызвать процедуру FormResize до отрисовки примитивов.
FormResize(Sender); //процедура обновления
  Всё, с приготовлением мы закончили, Теперь давайте приступим к рисованию примитивов и начнём мы с точки. Все фигуры рисуются в логических скобках glBegin(param); и glEnd; где param – тип рисуемого примитива.

glBegin() может иметь один из следующих параметров:
  • GL_PONTS - точка
  • GL_LINES - линия
  • GL_TRIANGLES - треугольник
  • GL_QUADS - квадрат
  • GL_POLYGON – многоугольник
Каждая вершина задаётся функцией glVertex3f(x, y ,z) и может иметь вид: glVertex3fv(@массив) , glVertex3i(x, y ,z) с параметрами типа integer.

  Теперь нарисуем точку, в процедуре FormPaint после очистки буфера цвета запишем следующий код:
glPointSize(20); // размер точки

glBegin(GL_POINTS);
glVertex3f(-2,5,1);
glEnd;
Точка

  Вот мы и получили очень большую точку в 20 пикселов :). Теперь давайте изменим цвет точки функцией glColor3f(R,G,B);
Эту функцию следует вызывать перед заданием позиции каждой вершины.

Раскрасим в красный цвет:
glPointSize(20); // размер точки

glBegin(GL_POINTS);
glColor3f(1,0,0);  glVertex3f(-2,5,1);
glEnd;
Красная точка

  Точку мы нарисовали. Теперь нарисуем линию. Делается это точно так же как и точку. Т.к. линия состоит из 2 вершин то функцию glVertex3f(x, y ,z) будем вызывать 2 раза:
glBegin(GL_LINES); //рисуем линию
glColor3f(1,0,0); {раскрасим первую вершину}  glVertex3f(-1,0,1); //позиция первой вершины
glColor3f(0,1,0); {раскрасим вторую вершину}   glVertex3f(-4,5,1); //позиция второй вершины
glEnd;
Допишем этот код и получим:

Линия

OpenGL сам делает плавное перетекание цвета между вершинами. Поучается довольно красиво.

Не хватает квадрата и треугольника. Давайте нарисуем!
glBegin(GL_TRIANGLES); //рисуем треугольник
glColor3f(1,0,0);  glVertex3f(0,5,1); //первая вершина
glColor3f(0,1,0);  glVertex3f(1,4,1); //вторая вершина
glColor3f(0,1,0);  glVertex3f(-1,4,1); //третья вершина
glEnd;

glBegin(GL_QUADS); //рисуем квадрат
glColor3f(1,0,0);  glVertex3i(-1,1,0); //первая вершина
glColor3f(0,1,0);  glVertex3f(1,1,-0); //вторая вершина
glColor3f(0,1,1);  glVertex3f(1,-1,-0); //третья вершина
glColor3f(0,0,1);  glVertex3f(-1,-1,0); //четвёртая вершина
glEnd;
Фигуры

Вроде бы получилось.

  В примере данного урока я сделал небольшое дополнение: при нажатии стрелки вправо вся система координат поворачивается вправо функцией glRotatef(угол, x, y, z) где x, y, z – координаты оси поворота, а при нажатии на стрелку влево, то вся система координат поворачивается влево.

Можете поэкспериментировать и нарисовать что-нибудь другое.

  В следующем уроке мы будем работать с освещением и нарисуем объёмные геометрические фигуры с падающим на них светом.



Добавил(а): Drimer Дата: 2012-12-05
в примере одно ))) а тут другоє))))



Добавил(а): Doggy Дата: 2013-01-11
не пойму зачем FormResize(Sender). Рисунок с ней рябит при перетаскивании формы, а без нее все норм (рисунок сохраняется). И еще - что это за wglMakeCurrent(0,0); - в первом уроке он есть а во втором он тупо мешает (с этой строкой не работает прога)



Добавил(а): Doggy Дата: 2013-01-11
а, и еще - что такое SwapBuffers(Canvas.Handle); в первом уроке его небыло



Добавил(а): Doggy Дата: 2013-01-12
аааааа!!!!! она вертится !!! круто!!!! я еще добавил воторой угол поворота и завязал его на клавиши вверх/вниз. теперь наиграться не могу



Добавил(а): Klesh Дата: 2013-01-12
wglMakeCurrent(0,0); - удаляет созданный контекст воспроизведения. В первом уроке мы просто инициализировали OpenGL ничего не рисуя, поэтому контекст нам был не нужен. В этом уроке используется двойная буферизация, чтобы не было мерцания, сначала сцена отрисовывается в задний буфер, а затем разом выводится в передний, то есть на форму. Эту процедуру выполняет функция SwapBuffers(Canvas.Handle);



Добавил(а): Doggy Дата: 2013-01-12
ясно. спасибо. я правда пока еще плохо понимаю что такое контекст воспроизведения, но будем постепенно разбираться.



Добавил(а): susczanski Дата: 2013-04-04
і, все-таки, для чого FormResize? Чому не можна той код прописувати відразу в FormPaint?



Добавил(а): susczanski Дата: 2013-04-05
Переведу, наверно так понятнее. В Ваших в уроках все основные действия (оброботка клавиатуры, мыши, прорисовка и т.д.) выполняются в процедуре formpaint. Мне же в проэкте надо создать цыкл, в конце которого должна выполняться прорисовка 3-д. Пробовал вызывать с цыкла formpaint, что-то не так. Пробовал всю прорисовку всунуть всам ц жикл но тогда глючит formresize. Подскажите, пожалуйста, что делат?



Добавил(а): Klesh Дата: 2013-04-07
Конечно можно и засунуть код FormResize в FormPaint, это я так сделал чтоб было удобнее и за одно обрабатывалось сжатие/растяжение формы, оно и так обработается но это будет не синхронно . Здесь замкнутый цыкл сначала при создании формы она перерисовывается, выполняется FromPaint, следовательно выполнится FormResize, а там есть функция InvalidateRect ( Handle,nil,False ); которая опять все сотрёт с формы и её придется снова перерисовывается (FormPaint) и так по кругу. Вам надо стереть InvalidateRect и вставить в конец вашего цикла тогда форма перересуется 1 раз.



Добавил(а): Klesh Дата: 2013-04-07
и еще цыкл который вы хотите сделать не должен быть в FormPaint или FornResize, например в обработчике нажатия на кнопку.



Добавил(а): susczanski Дата: 2013-04-08
Супер, спасибо Но возник новый вопрос: в NeHe прочитал, что для координатов полигонов можно использовать нецелые числа, а компилятор говорит ноборот, только целые. В то же время для объемных фигур можно и числа с плавающей точкой. Правда ли это, и почему так?



Добавил(а): susczanski Дата: 2013-04-08
А как сделать так, чтоб вместо формы графика была на Image? Просто заменить Handle на Image1.Canvas.Handle не получаеться...



Добавил(а): Klesh Дата: 2013-04-10
Да можно передавать вещественные числа, у вас скорее всего ошибка в типе переменной которую вы передаёте или или используете функцию которая принимает только целый тип(glVertex3i(x,y,z: integer)). Последняя буква в названии функции говорит о типе передаваемых данных: i - integer, f - Single и все вещественные типы (Double тоже), fv - ссылка(Pointer). Попробуйте glVertex3f(x,y,z: Single).



Добавил(а): Klesh Дата: 2013-04-10
На Image выводить не получится, не знаю почему вывести можно например на Panel или на кнопку. Событие OnPaint есть только у формы, а значит на другие компоненты выводить привычный способом не получится. Нужно создать таймер с интервалом 1 мс и туда прописать процедуру рисования, в нашем случае FormPaint(Sender); и параметр функции получения дескриптора - DC:= GetDC(Handle); заменить на Panel1.Handle, тоже самое сделать и при выводе буферов SwapBuffers(), но параметром будет уже DC, в FormResize закоментировать строчку InvalidateRect ( Handle,nil,False ); и изменить параметры функций: glViewport, gluPerspective на аналогичные Panel. Например glViewport(0, 0, Form1.ClientWidth, Form1.ClientHeight); заменить на glViewport(0, 0, Panel.Width, Panel.Height);. производительность падает в десятки раз, но это незаметно



Добавил(а): почти человек Дата: 2014-04-14
не знаю почему, но у меня выводится только пустая форма, хотя выполнял всё по инструкции.



Добавил(а): kuza2011zaika Дата: 2015-04-19
У меня форма вся пустая. Почему так?



Добавил(а): R1D16ST29 Дата: 2015-08-06
glutSolidCube(1); почему то не знает как оператор, а другие (от opengl) знает



Добавил(а): Nesf Дата: 2017-03-14
Если у вас выводится пустая форма, то у вас включился режим двойной буферизации, поэтому в конце обработчика события OnPaint следует написать команду SwapBuffers;



Добавил(а): dimonsky Дата: 2017-07-03
GetDC(handle) вместо canvas.handle, и убрать вызов FormResize. Тогда получается конфетка.


  1. Евгений
    Евгений a year ago
    У меня постоянно выходит экран формы с её цветом, и ничего не могу сделать, файл с примером не скачивается походу его нет на сервере, так же я не знаю куда мне написать "FormResize(Sender); //процедура обновления", сможете ответить на мои вопросы?
Отменить.