КОМПЬЮТЕРНАЯ ГРАФИКА

  • doc
  • 06.05.2020
Публикация на сайте для учителей

Публикация педагогических разработок

Бесплатное участие. Свидетельство автора сразу.
Мгновенные 10 документов в портфолио.

Иконка файла материала 3. КОМПЬЮТЕРНАЯ ГРАФИКА.doc

 

 

 

 

 

 

 

 

 

 

 

КОМПЬЮТЕРНАЯ ГРАФИКА

лабораторный практикум

 

 

 

 

 

 

 

 

ЛАБОРАТОРНАЯ РАБОТА №1

OpenGL: инициализация, построение двумерных примитивов в отдельном окне

 

    Цель работы

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

Теоретическая информация

 

Использование библиотеки OpenGL требует навыков программирования на C++, а также знания основ событийного программирования на уровне операционной системы Windows. Однако, авторы не ставят своей целью ограничивать выбор программных и аппаратных средств для выполнения работ по компьютерной графике, поэтому в приложении приведены дополнительно варианты инициализации OpenGL для различных ситуаций: с использованием библиотеки glut, языков С# и Delphi, для ОС Linux.

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

Основные понятия, используемые в данной лабораторной работе: контекст устройства, контекст воспроизведения, формат пиксела, синтаксис команд OpenGL, примитивы OpenGL.

Контекст графического устройства (Device Context) указывает плоскость отображения, на которую осуществляется графический вывод: окно программы на экране дисплея, страница принтера или другое место, куда может быть направлен графический вывод. Если программа вызывает различные графические функции, такие как рисование точек, линий, фигур и др., необходимо указывать идентификатор контекста (hdchandle of device context) и координаты. Смысл использования контекста устройства заключается в том, что вывод на различные устройства осуществляется одними и теми же функциями, изменяется лишь значение hDC. «Контекст устройства является структурой, которая определяет комплект графических объектов и связанных с ними атрибутов и графические режимы, влияющие на вывод».  При составлении программы необходимо получить это числовое значение перед рисованием, а после рисования – освободить контекст.

В OpenGL существует понятие контекст воспроизведения (контекст рендеринга), аналогичное понятию контекст устройства. Графическая система OpenGL также нуждается в ссылке на устройство, на которое будет осуществляться вывод. Это специальная ссылка на контекст воспроизведения – величина типа HGLRC (handle openGL rendering context, ссылка на контекст воспроизведения OpenGL) - hRC.

Алгоритм инициализации OpenGL

Основная задача инициализации – синхронизировать 2 контекста: hRC (контекст OpenGL) и hDC (контекст окна), чтобы всё, что будет нарисовано OpenGL, отображалось в окне.

Графически алгоритм инициализации OpenGL можно представить следующим образом (рис. 1).

Рассмотрим его подробнее.

1.             Подключение заголовочных файлов.

В зависимости от системы программирования набор подключаемых библиотек может быть различным.

Если вы используете Delphi, то всё необходимое для работы с OpenGL находится в модуле OpenGL.dcu.

 

Рисунок 1. Обобщенный алгоритм инициализации OpenGL

 

Код на языке Delphi

uses OpenGL;

 

Если вы используете С++ Builder, то подключать придётся несколько файлов:

·            gl.h и glu.h содержат прототипы основных функций OpenGL определённых в opengl32.dll и glu32.dll;

·            glaux.h содержит вспомогательные (auxiliary) функции (glaux.dll).

Код на языке C++

#include <GL/gl.h>;
#include <GL/glu.h>;
#include <GL/glaux.h>;

 

Для инициализации в Microsoft Visual Studio необходимо подключить следующие библиотеки:

 
#include <windows.h>  // Заголовочный файл для Windows
#include <gl\gl.h>    //Заголовочный файл для библиотеки OpenGL32 
#include <gl\glu.h>   // Заголовочный файл для библиотеки GLu32 
#include <gl\glaux.h>// Заголовочный файл для библиотеки GLaux 

 

2.                     Объявить глобальные переменные для контекста устройства и контекста воспроизведения

Код на Delphi

var
  
  hRC : HGLRC;
  hDC :  HDC;

 

Код на языке C++

static HGLRC hRC;           // Постоянный контекст рендеринга
static HDC hDC;       // Приватный контекст устройства GDI

 

3.         Создание контекста устройства и установка подходящего формата пикселей.

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

Ø    подбирается один из доступных в системе форматов пикселей, наиболее подходящий нашим нуждам,

Ø    затем этот формат и назначается устройству воспроизведения.

Задание всех необходимых требований выполняется в структуре PixelFormatDescriptor, которая содержит более 30 полей. Все мы их рассматривать не будем, остановимся на самых важных:

·                     nSize

Как и многие структуры в Windows это поле должно содержать размер самой структуры, проще всего его инициализировать оператором sizeof().

·                     nVersion

Для текущей реализации должно быть установлено в 1.

·                     dwFlags

Битовые флаги, определяющие устройство и интерфейс, с которым совместим буфер пикселей данного формата. Рассмотрим основные из них: 

Ø    PFD_DRAW_TO_WINDOW – разрешено рисование в окне или поверхности устройства. 

Ø    PFD_DRAW_TO_BITMAP – р \азрешено рисование в битовый образ в памяти.

Ø PFD_SUPPORT_GDI – буфер поддерживает рисование с использованием GDI. Этот флаг нельзя использовать с флагом PFD_DOUBLEBUFFER.

Ø PFD_SUPPORT_OPENGL – буфер поддерживает OpenGL рисование.

Ø PFD_DOUBLEBUFFER – поддерживается режим двойной буферизации. Нельзя использовать совместно с флагом PFD_SUPPORT_GDI. 

Ø PFD_SWAP_COPY – поддерживается ли копирование из внеэкранного буфера на первичную (видимую поверхность). Обмен не влияет на внеэкранную поверхность, а копирует изображение из нее на первичную.

В стандартном запросе на формат рисования в dwFlags устанавливаются следующие флаги: для вывода графических объектов в окно необходимо выставить флаг PFD_DRAW_TO_WINDOW; буфер кадра поддерживает вывод через OpenGL - PFD_SUPPORT_OPENGL;  и, так как мы используем два буфера (Back и Front), то устанавливаем флаг PFD_DOUBLEBUFFER. Рассмотрим двойную буферизацию подробнее.  Обычно в трёхмерных сценах каждый видимый кадр строится заново. То есть, в каждом кадре полностью перерисовывается вся видимая графика. Построение кадра может происходить следующим образом. Сначала весь кадр очищается, а потом последовательно выводятся трёхмерные графические объекты. Такое действие происходит несколько раз в секунду. Количество этих перерисовок в секунду называют FPS (frames per second). Если мы будем стирать и рисовать в видимой части экрана, то мы будем видеть мигание и мерцание наших рисующихся и стирающихся объектов.

Для исключения этого неприятного эффекта, пользуются следующим способом. Весь кадр полностью строят в невидимой части видеопамяти адаптера. Эту часть называют Back Buffer. После того, как все графические объекты выведены, вся эта невидимая часть копируется в видимую область видеопамяти, называемую Front Buffer. Т.е. происходит так называемая смена буферов, что и составляет эффект двойной буферизации и позволяет выполнять анимацию изображений без ненужных эффектов.

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

·      iPixelType

Определяет режим, используемый для отображения цветов: значение PFD_TYPE_RGBA - цвет каждого пикселя определяется 4 значениями – красным, зеленым, синим и альфа; PFD_TYPE_COLORINDEX - цвет задается индексом в таблице цветов (палитре).

·      cColorBits

Количество бит (битовых плоскостей) цвета. Фактически глубина цвета. Здесь указывается желаемая глубина цвета (bpp – bits per pixel, 8, 15,  16, 24, 32; список поддерживаемых значений зависит от драйверов и варьируется в зависимости от видеоплаты).

·      cRedBits cRedShift cGreenBits cGreenShift cBlueBits cBlueShift cAlphaBits cAlphaShift

Биты R,G,B,A составляющих и их смещения от начала битовых плоскостей, т.е. порядок следования цветовых компонент: RGBA или ABGR.  Например, если глубина цвета равна 32,  cRedBits = 8 и cRedShift = 16, то это означает, что под красную составляющую будет выделен один байт (8 бит) и в двойном слове глубины (32 бита) она будет занимать биты с 16 по 23 (отсчет бит идет с нуля).

·      cDepthBits

Размер (глубина) буфера глубины (Z-буфер). Обычные значения 16, 24, 32 бит. Z-буфер используется в трехмерной графике, и его разрядность влияет на точность вычислений операций с глубиной сцены. Недостаточная глубина Z-буфера будет приводить к ошибкам визуализации сцены, например, более дальний объект будет вылезать сквозь ближний. Глубина сцены задается как отношение задней плоскости отсечения к передней плоскости. Подробнее о механизмах построения трехмерных сцен, а так же алгоритмах работы с Z-буфером мы поговорим позднее.

·      cStencilBits

Размер (глубина) буфера трафарета. Буфер трафарета используется во многих продвинутых техниках рисования, например, для расчета стенсильных теней и пр. Однако на современных видеоплатах его разрядность, как правило, ограничена 8 битами. К тому же в большинстве случаев аппаратно акселерируется буфер трафарета только в режиме 32bpp, поэтому, например, в режиме 16bpp любое обращение к буферу трафарета будет сильно тормозить рендеринг всей сцены.

Шаблон инициализации полей объекта типа структура PixelFormatDescriptor приведен ниже:

{
RECT     Screen;      // используется позднее для размеров окна
   GLuint PixelFormat;
   static PIXELFORMATDESCRIPTOR pfd=
   {
   sizeof(PIXELFORMATDESCRIPTOR),  // Размер этой структуры
   1,                       // Номер версии 
   PFD_DRAW_TO_WINDOW |           // Формат для Окна
   PFD_SUPPORT_OPENGL |           // Формат для OpenGL
   PFD_DOUBLEBUFFER,        // Формат для двойного буфера
   PFD_TYPE_RGBA,                 // Требуется RGBA формат
   16,                      // Выбор 16 бит глубины цвета
   0, 0, 0, 0, 0, 0,        // Игнорирование цветовых битов 
   0,                       // нет буфера прозрачности
   0,                       // Сдвиговый бит игнорируется 
   0,                       // Нет буфера аккумуляции
   0, 0, 0, 0,        // Биты аккумуляции игнорируются 
   16,               // 16 битный Z-буфер (буфер глубины)  
   0,                       // Нет буфера трафарета
   0,                       // Нет вспомогательных буферов 
   PFD_MAIN_PLANE,          // Главный слой рисования
   0,                       // Резерв 
   0, 0, 0                  // Маски слоя игнорируются 
   };

 

Код на языке Delphi

function SetDCPixelFormat (DC: HDC): integer;
var
  pfnum: integer;
  pfd: PIXELFORMATDESCRIPTOR;
begin
  fillchar(pfd, sizeof(pfd), 0);
    pfd.nVersion := 1;
  pfd.nSize := sizeof(pfd);
  pfd.dwFlags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER;
  pfd.iPixelType := PFD_TYPE_RGBA
  {выбираем формат пикселей}
  pfnum := ChoosePixelFormat(DC, @pfd);
  {если вернули 0, значит ошибка}
  if pfnum = 0 then exit;
  {устанавливаем формат пикселей}
  SetPixelFormat(DC, pfnum, @pfd);
  Result := pfnum;
End;
 

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

switch (message)      // Тип сообщения
{ case WM_CREATE:
hDC = GetDC(hWnd);    // Получить контекст устройства для окна
PixelFormat = ChoosePixelFormat(hDC, &pfd);
// Найти ближайшее совпадение для формата пикселей
 

 

WM_CREATE указывает программе, что сообщение должно быть создано. Сначала следует запросить DC (контекст устройства) для окна –  без него рисование в окне невозможно. Затем  запрашивается формат пикселя.

Функция ChoosePixelFormat (<контекст устройства>, <указатель на структуру >) возвращает номер одного из доступных в системе форматов пикселей. Компьютер будет выбирать формат, который полностью совпадает или наиболее близок к запрашиваемому формату[1].

Если подходящий формат пикселя не найден, будет выведено сообщение об ошибке.

    if (!PixelFormat)
   {
   MessageBox(0,"Не найден подходящий формат пиксела.",
                     "Ошибка",MB_OK|MB_ICONERROR);[2]
   PostQuitMessage(0);
// Это сообщение говорит, что программа должна завершится
   break;      // Предотвращение повтора кода
 
    }

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

    if(!SetPixelFormat(hDC,PixelFormat,&pfd))
         {
         MessageBox(0,"Формат пиксела не установлен.",
                            "Ошибка",MB_OK|MB_ICONERROR);
         PostQuitMessage(0);
         break;
   }

Если код записан, как показано выше, будет создан контекст устройства (hDC), и установлен подходящий формат пикселя. Рекомендуется все эти действия выполнять в отдельной функции, дав ей значимое имя, например, SetWindowPixelFormat или SetDCPixelFormat.

4.             Создание контекста воспроизведения

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

wglCreateContext(<контекст воспроизведения>),

которая определяет контекст воспроизведения OpenGL, подходящий для рисования на устройстве, определённом дескриптором hRC. При успешном завершении функция возвращает дескриптор созданного контекста воспроизведения OpenGL, и NULL – в случае неудачи.

hRC = wglCreateContext(hDC);
   if(!hRC)
   {
   MessageBox(0,"Контекст воспроизведения не создан.",
                     "Ошибка",MB_OK|MB_ICONERROR);
   PostQuitMessage(0);
   break;
   }

 

Текущий контекст воспроизведения потока должен быть единственным. Следующая функция позволяет определить контекст воспроизведения для контекста устройства.

wglMakeCurrent(hDC, hRC)

В результате у нас получилась функция создания GL контекста рисования:

Код на языке Delphi

function CreateContext(DC: HDC): boolean;
var
  hrc: HGLRC;
begin 
  Result := false;
  //Проверим входные параметры
  if DC = 0 then exit;
  //создаем GL   if SetWindowPixelFormat(hDC, 32, 0, 0) = 0 then exit;
  {теперь можно создать контекст GL}
  rc := wglCreateContext(hDC);
   if rc = 0 then exit;
  {назначаем GL контекст текущим}
  wglMakeCurrent(hDC, hrc);
 OneTimeSceneInit;
 Result := true;
end;
 
Код на языке C++
 
void __fastcall TForm1::FormCreate(TObject *Sender)
{
   hDC = GetDC(Handle);
   if (!SetupPixelFormat(hDC)) Close();
   hRC = wglCreateContext(hDC);
   wglMakeCurrent(hDC, hRC);
}
 

5.             Отрисовка.

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

Область вывода в OpenGL задается командой

             glViewPort (0, 0, ClientWidth, ClientHeight),

где первые два параметра задают положение левого верхнего угла окна вывода, параметры ClientWidth, ClientHeight определяют размер окна в экранных координатах. Центр полученной области вывода имеет координаты (0, 0). Координаты изменяются в диапазоне [-1; 1] по каждой оси.

По умолчанию центр системы координат находится в центре окна вывода, а максимальное значение координат по осям определено в диапазоне [-1; 1].

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

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

Если при установке формата пикселя был установлен режим двойной буферизации (флаг PFD_DOUBLEBUFFER), то изображение готовится в фоновом буфере, и  необходимо обеспечить перезапись содержимого фонового буфера в рабочий. Для этого можно использовать функцию

             BOOL glSwapBuffers (HDC hdc)

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

VOID glFlush (void)

 Функция glFlush, определенная в библиотеке  gl.h, инициирует выполнение всех ожидающих команд OpenGL, которые обычно выстраиваются в очередь и выполняются пакетами с целью оптимизации производительности. Этот принцип может варьироваться для различного аппаратного обеспечения, драйверов и реализаций OpenGL, однако все действия должны завершаться “в конечное время”, что и позволяет реализовать команда glFlush .

В общем виде эту секцию кода можно записать так.

GLvoid DrawGLScene(GLvoid)
{
   glViewPort (0, 0, ClientWidth, ClientHeight),
   glClear(GL_COLOR_BUFFER_BIT);   // очистка экрана 
   glSwapBuffers (hDC);
}

Позднее мы добавим сюда функции OpenGL для отрисовки примитивов.

 

6.             Освобождение контекстов.

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

 wglMakeCurrent(0,0); //освободить контекст

Завершая работу с OpenGL необходимо удалить контекст воспроизведения. Для этой цели используется функция:

wglDeleteContext(hRC);

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

Код на Delphi:

procedure TForm1.FormDestroy(Sender: TObject);

begin

  wglMakeCurrent(0,0); //освободить контекст

wglDeleteContext(hrc)//удалить контекст воспроизведения;

ReleaseDC(Handle,dc);// удалить контекст устройства

end;

Код на С++

void __fastcall TForm1::FormDestroy(TObject *Sender)

{

        wglMakeCurrent(0,0);

        wglDeleteContext(hGLRC);

        ReleaseDC(Handle, hDC);

        Form1->Close();       

}

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

Синтаксис команд OpenGL

Однако, если мы остановимся на этом, то результат программы нас не удовлетворит. Потому как мы пишем программу для отрисовки графики! А где графика в этом шаблоне?

Поэтому мы добавим немножко информации уже непосредственно про OpenGL, чтобы ваша первая программа могла что-то нарисовать. А вы могли это увидеть, обрадоваться (все не зря!), и вдохновиться на новые свершения.

Итак, приступим.

В OpenGL полное имя команды имеет вид:

type glName[1 2 3 4][b s i f d ub us ui][v] (type1 arg1,…,typeN argN)

где:

gl

это имя библиотеки, в которой описана эта функция: для базовых функций OpenGL, функций из библиотек GLU, GLUT, GLAUX это gl, glu, glut, glaux соответственно

Name

имя команды

[1 2 3 4]

число аргументов команды

[b s i f d ub us ui]

тип аргумента (см. таблица 1)

[v]

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

Символы в квадратных скобках в некоторых названиях не используются. Например, команда glVertex2i() описана как базовая в библиотеке OpenGL и использует в качестве параметров два целых числа, а команда glColor3fv() использует в качестве параметра указатель на массив из трех вещественных чисел.

Если имя команды заканчивается на v (векторная форма), то аргументом её служит указатель на массив значений. Например: Если последние три символа в имени команды 3fv, то её аргумент – адрес массива трёх вещественных чисел.

Использования нескольких вариантов каждой команды можно частично избежать, применяя перегрузку функций языка C++. Но интерфейс OpenGL не рассчитан на конкретный язык программирования, и, следовательно, должен быть максимально универсален.

 

Таблица 1 Возможные типы аргументов

Символ

Обозначение типа в OpenGL

Расшифровка

b

GLbyte

Байтовый

s

GLshort

Короткий целый

i

GLint

Целый

d

GLdouble

Вещественный двойной точности

f

GLfloat

вещественный

ub

GLubyte

Байтовый,
беззнаковый

us

GLushort

Короткий целый
беззнаковый

ui

GLuint

Целый
беззнаковый

 

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

Примитивы OpenGL

Все изображения строятся из отдельных примитивов, которые описываются с помощью набора вершин (Vertex). Примитивами OpenGl являются точки (одиночные вершины), линии (пары вершин), треугольники (три вершины), четырехугольники (четыре вершины) и полигоны (3 и более вершин).

Для указания на отрисовку одного или нескольких примитивов необходимо использовать так называемые командные скобки библиотеки OpenGl, которые  представляют собой специальные функции (не путать с  операторными скобками языков программирования!). Ошибка при использовании командных скобок не распознается компилятором, но может привести к непредсказуемым результатам работы программы.

Командные скобки в общем виде записываются следующим образом:

   glBegin (mode)

… // вершины и их атрибуты

glEnd;

где mode определяет правило соединения перечисленных вершин в графический примитив (см. таблица 2).

Вершины задаются своими координатами (количество координат зависит от пространства изображения) с помощью команд

 glVertex{2,3,4}{s,i,f,d}(koord1, koord2, …)

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

Пример:

Отрисовка трех точек в двумерной системе координат.

  glBegin(GL_POINTS); 

   glColor3d(1,0,0); 

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

   glColor2d(0,1); 

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

   glColor3d(0,0,1);    

   glVertex2d(-3.5); // третья точка

  glEnd();

 

Таблица 2 Возможные значения параметра mode

mode

Описание

GL_POINTS

Рисует N точек.

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

GL_LINES

Рисует N/2 линий.

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

GL_LINE_STRIP

Рисуется ломаная из N - 1 отрезков. Вершина  N и N+1 определяют отрезок N ломаной.

GL_LINE_LOOP

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

GL_TRIANGLES

Рисуется N/3 треугольников. Каждые три вершины N, N+1, N+2 определяют треугольник N.

GL_TRIANGLE_STRIP

Рисуется N-1  треугольников. Рисуются треугольники с общей стороной. Для нечетного N, элементы N, N+1, N+2 определяют треугольник N.

GL_TRIANGLE_FAN

Рисуется N - 2 треугольников. Рисует группу соединенных в одной вершине треугольников. Один треугольник определяется для каждого элемента после двух предыдущих. Два последних элемента соединяются с первым.

GL_QUADS

Рисуется  N/4 четырехугольника. Каждые четыре вершины задают четырехугольник.

GL_QUAD_STRIP

Рисуются четырехугольники с общей стороной, соединяя чётные элементы с чётными, а нечётные с нечётными.

GL_POLYGON

Рисуется полигон. Вершины полигона определяются элементами от 1 до N-го.

 

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

http://www.rsdn.ru/article/opengl/ogltut2/primitives.gif

Рисунок 2.  Примитивы OpenGL


Методика выполнения лабораторной работы

В течение занятия необходимо:

1)            изучить теоретическую информацию, предлагаемую в данном пособии;

2)            сформировать собственную блок-схему инициализации OpenGL, в зависимости от используемого программного обеспечения (примеры вариантов инициализации см. в разделе «Дополнительный материал» к данной лабораторной работе);

3)            выполнить инициализацию OpenGL;

4)            выполнить отрисовку типовых графических примитивов согласно варианту задания.

Варианты заданий

 

1.      Выполнить инициализацию OpenGL и отрисовать 4 точки, два треугольника и полигон.

2.      Выполнить инициализацию OpenGL и отрисовать 3 точки, треугольник, ленту треугольников и два четырехугольника.

3.      Выполнить инициализацию OpenGL и отрисовать две линии, ломаную, 2 треугольника и квадрат.

4.      Выполнить инициализацию OpenGL и отрисовать 5 точек, 2 ломаные, ленту треугольников и полигон.

5.      Выполнить инициализацию OpenGL и отрисовать 2 полигона из 6 вершин каждый, две замкнутые линии из 5 вершин и 3 точки.

6.      Выполнить инициализацию OpenGL и отрисовать 2 четырехугольника, 2 треугольника и 5 точек.

7.      Выполнить инициализацию OpenGL и отрисовать 2 отрезка, 3 треугольника и полигон.

8.      Выполнить инициализацию OpenGL и отрисовать 3 треугольника, ленту треугольников, ленту четырехугольников и 1 точку.

9.      Выполнить инициализацию OpenGL и отрисовать все примитивы по одному разу.

10.  Выполнить инициализацию OpenGL и отрисовать 2 полигона из 5 вершин каждый, две замкнутые линии из 7 вершин и 2 точки.

11.  Выполнить инициализацию OpenGL и отрисовать 1 четырехугольник, 2 треугольника, 4 точки и 1 отрезок.

12.  Выполнить инициализацию OpenGL и отрисовать 3 точки, 2 треугольника, ленту треугольников и четырехугольник.

13.  Выполнить инициализацию OpenGL и отрисовать 6 точек, треугольник, ленту треугольников и ленту четырехугольников.

14.  Выполнить инициализацию OpenGL и отрисовать 3 полигона, треугольник, ленту четырехугольников и 2 точки.

15.  Выполнить инициализацию OpenGL и отрисовать 2 полигона из 5 вершин каждый, две ломаные из 7 вершин и 2 треугольника.

 

Содержание отчета

Результатом выполнения лабораторной работы должен стать отчет (в печатном и электронном вариантах), состоящий из следующих пунктов:

1)  постановка задачи;

2)  блок-схема инициализации;

3)  программный код предлагаемого решения;

4)  скриншоты результатов работы программы.

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

·                отчет о лабораторной работе (в печатном виде);

·                программную реализацию решения.

Оценка (в баллах) выставляется за устный отчет студента по предоставленным преподавателю материалам.

Пример выполнения лабораторной работы

Постановка задачи:

1.     Выполнить инициализацию OpenGL.

2.      Изобразить  точки, линии, треугольник, многоугольники (минимум 5 различных примитивов).

 

Листинг программы:

//------------------------------------------------------------

#include <vcl.h>

#include <GL/gl.h>

#include <GL/glu.h>

#include <Gl/glaux.h>

#pragma hdrstop

 

#include "Unit1.h"

//------------------------------------------------------------

#pragma package(smart_init)

#pragma resource "*.dfm"

TForm1 *Form1;

HGLRC hGLRC; //контекст воспроизведения (связь с WinExplorer);

HDC hDC; //контекст окна (устройства) вывода;

//------------------------------------------------------------

//функция, устанавливающая параметры контекста воспроизведения OpenGL;

void SetDCPixelFormat(HDC dc)

{

        int pfnum;

        PIXELFORMATDESCRIPTOR pfd;

 

        pfd.nSize=sizeof(pfd); //размер структуры;

        pfd.nVersion=1;

        pfd.dwFlags=                         

//битовые флаги:

              PFD_DRAW_TO_WINDOW || //рисование в окне

              PFD_SUPPORT_OPENGL || //поддержка буфером OpenGL

              PFD_DOUBLEBUFFER;     //двойная буферизация;

     pfd.iPixelType=PFD_TYPE_RGBA;  //режим отображения цветов

 

 pfnum=ChoosePixelFormat(hDC, &pfd); //предлагаемый формат;

 SetPixelFormat(hDC, pfnum, &pfd); //устанавливаем формат

};

//------------------------------------------------------------

__fastcall TForm1::TForm1(TComponent* Owner)

        : TForm(Owner)

{

  hDC=GetDC(Handle);

  SetDCPixelFormat(hDC); //устанавливаем параметры OpenGL;

  hGLRC=wglCreateContext(hDC);//создаем контекст воспроизведения;

  wglMakeCurrent(hDC,hGLRC); //определяем контекст воспроизведения;

}

//------------------------------------------------------------

void __fastcall TForm1::FormClick(TObject *Sender)

{

        glClearColor(0.1,0.1,0.1,0); //очищение экрана и заполнение цветом;

        glViewport(0,0,ClientWidth,ClientHeight); //область вывода;

 

glColor3f(1, 1, 1);

glBegin(GL_POINTS);

                glVertex2f(-0.6, -0.55); ;

                glVertex2f(-0.55, -1);

                glVertex2f(-0.5, -0.55);

                glVertex2f(0.6, -0.55);

                glVertex2f(0.55, -1);

                glVertex2f(0.5, -0.55);

        glEnd();

glColor3d(1,0,0);     

glBegin(GL_TRIANGLES); // рисуем треугольник

  glVertex3d(-4,2,0);

  glVertex3d(-3,2.9,0);

  glVertex3d(-2,2,0);

 glEnd();

glBegin(GL_TRIANGLE_STRIP); // рисуем ленту треугольников

  glColor3d(0,1,0);

  glVertex3d(1,2,0);

  glVertex3d(0,2.9,0);

  glVertex3d(-1,2,0);

  glVertex3d(0,1.1,0);

 glEnd();

SwapBuffers(hDC);

}

//------------------------------------------------------------

void __fastcall TForm1::FormDestroy(TObject *Sender)

{

        wglMakeCurrent(0,0);

        wglDeleteContext(hGLRC);

        ReleaseDC(Handle, hDC);

        Form1->Close();       

}


Дополнительный материал

Инициализация OpenGL в Microsoft Visual Studio ‘13

 

// initOpenGL.cpp: определяет точку входа для приложения.

#include "stdafx.h"

#include "initOpenGL.h"

#include <math.h>

/* Добавим свои переменные ******************/

/*ссылка на поверхность рисования на которой будет все рисование *********/

HDC hDC;

/*ссылка на OpenGL через которую будем передавать параметр **/

HGLRC hGLRC;

/*ссылка на окно в котором будет происходить рисование ******/

HWND hWnd;

/************************************************************/

#define MAX_LOADSTRING 100

// Глобальные переменные:

HINSTANCE hInst;                      // текущий экземпляр

TCHAR szTitle[MAX_LOADSTRING];         // Текст строки заголовка

TCHAR szWindowClass[MAX_LOADSTRING];// имя класса главного окна

// Отправить объявления функций, включенных в этот модуль кода:

ATOM               MyRegisterClass(HINSTANCE hInstance);

BOOL               InitInstance(HINSTANCE, int);

LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);

INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

 

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,

                     _In_opt_ HINSTANCE hPrevInstance,

                     _In_ LPTSTR    lpCmdLine,

                     _In_ int       nCmdShow)

{

     UNREFERENCED_PARAMETER(hPrevInstance);

     UNREFERENCED_PARAMETER(lpCmdLine);

 

     // TODO: разместите код здесь.

     MSG msg;

     HACCEL hAccelTable;

 

     // Инициализация глобальных строк

     LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);

     LoadString(hInstance, IDC_INITOPENGL, szWindowClass, MAX_LOADSTRING);

     MyRegisterClass(hInstance);

 

     // Выполнить инициализацию приложения:

     if (!InitInstance (hInstance, nCmdShow))

     {

         return FALSE;

     }

     hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_INITOPENGL));

     // Цикл основного сообщения:

     while (GetMessage(&msg, NULL, 0, 0))

     {

     if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))

         {

              TranslateMessage(&msg);

              DispatchMessage(&msg);

         }

     }

     return (int) msg.wParam;

}

//

//  ФУНКЦИЯ: MyRegisterClass()

//  НАЗНАЧЕНИЕ: регистрирует класс окна.

//

ATOM MyRegisterClass(HINSTANCE hInstance)

{

     WNDCLASSEX wcex;

     wcex.cbSize = sizeof(WNDCLASSEX);

     wcex.style              = CS_HREDRAW | CS_VREDRAW;

     wcex.lpfnWndProc    = WndProc;

     wcex.cbClsExtra         = 0;

     wcex.cbWndExtra         = 0;

     wcex.hInstance      = hInstance;

     wcex.hIcon              = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_INITOPENGL));

     wcex.hCursor       = LoadCursor(NULL, IDC_ARROW);

     wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);

     wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_INITOPENGL);

     wcex.lpszClassName  = szWindowClass;

     wcex.hIconSm       = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

     return RegisterClassEx(&wcex);

}

//

//   ФУНКЦИЯ: InitInstance(HINSTANCE, int)

//   НАЗНАЧЕНИЕ: сохраняет обработку экземпляра и создает главное окно.

//   КОММЕНТАРИИ:

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

 //    а также  создается и выводится на экран главное окно программы.

//

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

{

   hInst = hInstance; // Сохранить дескриптор экземпляра в глобальной переменной

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,

      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)

   {

      return FALSE;

   }

   /*Весь код инициализации будет находиться здесь **********/

   PIXELFORMATDESCRIPTOR pfd = {

        sizeof(PIXELFORMATDESCRIPTOR),

        1,

        PFD_SUPPORT_OPENGL |

        PFD_DRAW_TO_WINDOW |

        PFD_DOUBLEBUFFER,

        PFD_TYPE_RGBA,

        32,

        0, 0, 0, 0, 0, 0,

        0,

        0,

        0,

        0, 0, 0, 0,

        16,

        0,

        0,

        PFD_MAIN_PLANE,

        0,

        0, 0, 0,

   };

   /************************************************************/

   /*получаем ссылку на поверхность где будем рисовать */

   hDC = GetDC(hWnd);

   /*Задаем параметры точек*/

   int pixelFormat = ChoosePixelFormat(hDC, &pfd);

   /*задаем параметры точки*/

   SetPixelFormat(hDC, pixelFormat, &pfd);

   /*сообщаем OpenGL где будем рисовать*/

   hGLRC = wglCreateContext(hDC);

   /*создаем контекст рисования*/

   wglMakeCurrent(hDC, hGLRC);

   /*****************************************************/

   ShowWindow(hWnd, nCmdShow);

   UpdateWindow(hWnd);

   return TRUE;

}

//

//  ФУНКЦИЯ: WndProc(HWND, UINT, WPARAM, LPARAM)

//  НАЗНАЧЕНИЕ:  обрабатывает сообщения в главном окне.

//  WM_COMMAND - обработка меню приложения

//  WM_PAINT   - закрасить главное окно

//  WM_DESTROY - ввести сообщение о выходе и вернуться.

//

 

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

     int wmId, wmEvent;

     PAINTSTRUCT ps;

     HDC hdc;

     switch (message)

     {

     case WM_COMMAND:

         wmId    = LOWORD(wParam);

         wmEvent = HIWORD(wParam);

         // Разобрать выбор в меню:

         switch (wmId)

         {

         case IDM_ABOUT:

              DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);

              break;

         case IDM_EXIT:

              DestroyWindow(hWnd);

              break;

         default:

              return DefWindowProc(hWnd, message, wParam, lParam);

         }

         break;

     case WM_PAINT:

         hdc = BeginPaint(hWnd, &ps);

     // выполняем отрисовку примитивов

/************************************************************/

         glClear(GL_COLOR_BUFFER_BIT);

         glColor3ub(145, 30, 66);

         glBegin(GL_TRIANGLES);

         glVertex2f(0.0f, 0.8f); //верхняя вершина

         glVertex2f(-0.1f, 0.4f); //левая вершина

         glVertex2f(0.1f, 0.4f); //правая вершина

         glEnd();

         //рисуем прямоугольник

         glColor3ub(220, 380, 52);

         glRectf(-0.1f, -0.4f, 0.1f, 0.4f);

         glBegin(GL_POLYGON);

         glColor3f(1.0, 0.0, 0.0);

         glVertex2f(0.1, -0.4);

         glVertex2f(0.2, -0.7);

         glVertex2f(0.05, -0.5);

         glVertex2f(-0.05, -0.5);

         glEnd();

         glBegin(GL_POLYGON);

         glVertex2f(-0.05, -0.5);

         glVertex2f(-0.2, -0.7);

         glVertex2f(-0.1, -0.4);

         glVertex2f(0.1, -0.4);

         glEnd();

         glFlush();

         SwapBuffers(hDC);

         EndPaint(hWnd, &ps);

              break;

     case WM_DESTROY:

         PostQuitMessage(0);

         break;

     default:

         return DefWindowProc(hWnd, message, wParam, lParam);

     }

     return 0;

}

 

// Обработчик сообщений для окна "О программе".

INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)

{

     UNREFERENCED_PARAMETER(lParam);

     switch (message)

     {

     case WM_INITDIALOG:

         return (INT_PTR)TRUE;

 

     case WM_COMMAND:

         if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)

         {

              EndDialog(hDlg, LOWORD(wParam));

              return (INT_PTR)TRUE;

         }

         break;

     }

     return (INT_PTR)FALSE;

}

 

 

 

 

 

 

 

 

 

 

Инициализация OpenGL с использованием GLUT

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

Минимальная программа, которая создает окно и что-нибудь рисует там, состоит из следующих шагов:

1. Инициализация GLUT

2. Установка параметров окна.

3. Создание окна.

4. Установка функций, отвечающих за рисование в окне и изменение формы окна.

5. Вход в главный цикл GLUT.

Рассмотрим все 5 пунктов подробнее.

Сначала необходимо скачать библиотеку glut.h с официального сайта OpenGL и подключить ее:

 #include <GL/glut.h>

1. Инициализация GLUT производится командой: 

void glutInit(int *argcp, char **argv); 

где *argcp – указатель на количество аргументов в командной строке, а **argv – указатель на массив аргументов. Обычно эти значения берутся из главной функции программы: int main(int argc, char *argv[]).

2.  Установка параметров окна.

Прежде всего, необходимо указать размеры окна:

void glutInitWindowSize(int width, int height);

где width – ширина окна в пикселях, height – высота окна в пикселях. По умолчанию размеры окна 300x300.

Далее задать положение создаваемого окна относительно верхнего левого угла экрана с помощью функции:

void glutInitWindowPosition(int x, int y);

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

void glutInitDisplayMode (unsigned int mode);

В  качестве аргумента в эту функцию необходимо передать одну из следующих констант (см. таблица 3) или их комбинацию (с помощью побитового ИЛИ).

Таблица 3

Константа

Значение

GLUT_RGB

Для отображения графической информации используются 3 компоненты цвета RGB

GLUT_RGBA

То же что и RGB, но используется также 4 компонента ALPHA (прозрачность)

GLUT_INDEX

Цвет задается не с помощью RGB компонентов, а с помощью палитры. Используется для старых дисплеев, где количество цветов, например, 256

GLUT_SINGLE

Вывод в окно осуществляется с использованием 1 буфера. Обычно используется для статического вывода информации

GLUT_DOUBLE

Вывод в окно осуществляется с использованием 2 буферов. Применяется для анимации, чтобы исключить эффект мерцания

GLUT_ACCUM

Использовать также буфер накопления (Accumulation Buffer). Этот буфер применяется для создания специальных эффектов, например отражения и тени

GLUT_ALPHA

Использовать буфер ALPHA. Этот буфер, как уже говорилось, используется для задания 4-го компонента цвета – ALPHA. Обычно применяется для таких эффектов, как прозрачность объектов и антиалиасинг

GLUT_DEPTH

Создать буфер глубины. Этот буфер используется для отсечения невидимых линий в 3D пространстве при выводе на плоский экран монитора

GLUT_STENCIL

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

GLUT_STEREO

Этот флаг используется для создания стереоизображений. Используется редко, так как для просмотра такого изображения нужна специальная аппаратура

 

Механизм двойной буферизации устанавливается в процессе инициализации аргументом функции glutInitDisplayMode(). Вместо константы GLUT_SINGLE нужно задать константу GLUT_DOUBLE. Переключение буферов выполняется функцией glutSwapBuffers(). Все операторы формирования изображения включатся в функцию display(), но при использовании двойной буферизации в этой функции сначала нужно очистить рабочий буфер, вызвав команду glClear(), а последним оператором вызвать функцию переключения буферов glutSwapBuffers().

Например:

void glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);

3.  Создание окна.

После того как окно установлено, необходимо его создать:

int glutCreateWindow(const char *title);  

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

4. Установка функций, отвечающих за рисование в окне и изменение формы окна.

Функция для рисования, которая всегда будет вызываться операционной системой, чтобы нарисовать (перерисовать) содержимое окна:

void glutDisplayFunc(void (*func)(void));

аргумент этой функции – указатель на функцию, которая будет отвечать за рисование в окне. Например, чтобы функция void Draw(void), определенная в вашей программе, отвечала за рисование в окне, надо присоединить ее к GLUT следующим образом:

glutDisplayFunc(Draw);

Функция, отслеживающая изменения окна:

void glutReshapeFunc(void (*func)(int width, int height));

аргумент которой  – это указатель на функцию, отвечающую за изменение размеров окна, которая должна принимать два параметра width и height, соответственно ширина и высота нового (измененного) окна.

5. Вход в главный цикл GLUT.

Этот цикл запускает на выполнение и обеспечивает взаимосвязь между операционной системой и теми функциями, которые отвечают за окно, получают информацию от устройств ввода/вывода. Для того, чтобы перейти в главный цикл GLUT, надо указать:

void glutMainLoop(void);

Команда glFlush() гарантирует, что команда рисования будет выполнена немедленно,  а не сохранена в буфере.

Функции библиотеки GLUT реализуют так называемый событийно-управляемый механизм. Это означает, что есть некоторый внутренний цикл, который запускается после соответствующей инициализации и обрабатывает одно за другим все события, объявленные во время инициализации. К событиям относятся: щелчок мыши, закрытие окна, изменение свойств окна, передвижение курсора, нажатие клавиши, и «пустое» (idle) событие, когда ничего не происходит. Для проведения периодической проверки совершения того или иного события надо зарегистрировать функцию, которая будет его обрабатывать.

void glutKeyboardFunc(void (*func)(unsigned int key, int x, int y);

Определяет функцию (func), которая вызывается, когда нажата клавиша на клавиатуре. Возвращаемые параметры:

key – сгенерированный клавиатурой ASCII код;

x,y – координаты положения мыши в координатах отобража­емого окна, в момент, когда была нажата кнопка на клавиатуре.

void glutMouseFunc(void (*func)(int button, int state, int x, int y));

Определяет функцию (func), которая вызывается, когда кнопка мыши нажата или отпущена. Возвращаемый функцией параметр button может принимать значения GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON, или GLUT_RIGHT_BUTTON. Значение параметра state есть GLUT_UP или GLUT_DOWN в зависимости от того, была ли кнопка мыши нажата или отпущена,  x and y параметры указывают на координаты в текущем окне, где находилась мышь в момент нажатия или отпускания кнопки.

void glutMotionFunc(void (*func)(int x, int y));

Определяет функцию (func), которая вызывается, когда указатель мыши перемещается в пределах окна при нажатой одной или более кнопке,    x and y параметры указывают на координаты в текущем окне, где находилась мышь в момент начала события void glutPostRedisplay(void);

Отмечает текущее окно как требующее перерисовки. На следующем шаге работы программы будет вызвана функция, зарегистрированная в glutDisplayFunc().

void glutIdleFunc (void (*func) (void));

glutIdleFunc() задает функцию, которая будет вызываться каждый раз, когда нет событий от пользователя.

 

Пример инициализации с Glut

Программа выполняет инициализацию Opengl и отрисовывает чайник с помощью специальной встроенной функции glutSolidTeapot(width). Так как в сцене будет отрисован трехмерный предмет, то, соответственно, необходимо добавить в алгоритм инициализации установки для определения трехмерной сцены. В тексте программы все соответствующие строки закомментированы для лучшего понимания процесса. На данном этапе этого достаточно. Сам механизм определения и расчета трехмерных сцен подробно будет рассмотрен позднее.

#include <windows.h>

#include <GL/gl.h>

#include <GL/glu.h>

#include <GL/glaux.h>

#include <GL/glut.h>

void resize(int width,int height)

{

}   

void  display(void)

{

       glColor3d(1,1,0);      // установка цвета

  glutSolidTeapot(0.8); //отрисовка чайника

     glFlush();

}

void init(void)

{

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

 glEnable(GL_LIGHTING); //включаем режим расчета освещения

 glEnable(GL_LIGHT0); //включаем источник света

      glEnable(GL_DEPTH_TEST); //включаем режим проверки

                             //глубины сцены

 glClearColor (0.0, 0.0, 0.0, 0.0);

 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);                  

  //обращаемся к матрице проекций

glMatrixMode( GL_PROJECTION );

glLoadIdentity(); //приводим ее к единичному виду

    //устанавливаем заданный параллелепипед видимости

      glOrtho(-5.0,5.0,-5.0,5.0,2.0,12.0);  

     //определяем положение наблюдателя (камеры)

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

//обращаемся к модельно-видовой матрице

 glMatrixMode( GL_MODELVIEW); }

int main(int argc,char ** argv)

{

  glutInitDisplayMode(GLUT_SINGLE| GLUT_RGB | GLUT_DEPTH);

  glutInitWindowPosition(50,10);

  glutInitWindowSize(400,400);

  glutCreateWindow(«Hello»);

  glutReshapeFunc(resize);

  init();

   glutDisplayFunc(display);

  glutMainLoop();

  return 0;

  }

 

Контрольные вопросы

1.                 Что понимается под контекстом устройства?

2.                 Что представляет собой контекст воспроизведения?

3.                 Какие основные фрагменты должна содержать минимальная программа OpenGL.

4.                 Что входит в понятие формат пиксела?

5.                 Какие библиотечные файлы должны быть подключены к программе для работы с OpenGL?

6.                 Какая цветовая модель используется при определении цвета в данной программе?

7.                 Какие параметры следует указать, чтобы цвет фона был зеленым?

8.                  Для чего нужна функция масштабирования сцены?

9.                 В какой секции кода можно записывать команды рисования сцены?

10.             Какое правило необходимо соблюдать, чтобы иметь возможность перехода в полноэкранный режим?


ЛАБОРАТОРНАЯ РАБОТА №2

OpenGL: Разработка приложения для визуализации связного набора двумерных примитивов   

 

Цель работы

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

 

Теоретическая информация

 

Использование функций glBegin и glEnd

Функции для рисования заключаются между командными скобками glBegin  и glEnd. Командные скобки библиотеки OpenGl представляют собой специальные функции (не имеющие никакого отношения к операторным скобкам языков программирования). Ошибка при использовании командных скобок не распознается компилятором, но может привести к непредсказуемым результатам работы программы.

Внутри командных скобок могут находиться любые операторы языка и многие функции OpenGL. Главное назначение командных скобок – задание режима (примитива) для команд glVertex (вершина), определяющих координаты вершин для рисования примитивов OpenGL. Аргументами функции glBegin могут быть стандартные константы OpenGl, определяющие примитивы библиотеки: GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP, GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS, GL_QUAD_STRIP, GL_POLYGON. В программе имена констант должны быть записаны именно так: в верхнем регистре.

Команды, устанавливающие размер точки, толщину и тип линии, включение и отключение режима сглаживания (англ. anti-aliasing) должны стоять вне командных скобок.

 

Включение и отключение режимов в Opengl

Для установки (включения)  специализированных режимов обработки и вывода изображений в Opengl,  необходимо использовать функцию glEnable() с соответствующей константой в качестве аргумента. Для отключения режима  – соответственно,  функцию glDisable() с той же константой.

Функции glEnable/glDisable включают/выключают множество опций, но следует учитывать, что некоторые опции влекут за собой большие вычисления и, следовательно, изрядно затормаживают приложение, поэтому без надобности не стоит их включать.

 

Вывод точек

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

С каждой вершиной ассоциируются ее атрибуты. В число основных атрибутов входят:

·    размер;

·    положение вершины в пространстве;

·    цвет вершины;

·    вектор нормали;

·    координаты текстуры

Рассмотрим каждый из атрибутов подробнее.

Атрибуты точек

·    Размер

Когда вы отрисовываете точку, ее размер по умолчанию равен одному пикселю. Для изменения размера точки используется функция

 void glPointSize(GLfloat size),

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

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

GLfloat sizes[2];// диапазон размеров поддерживаемых точек

GLfloat step;   // поддерживаемый инкремент размеров точек

// Получаем диапазон размеров поддерживаемых точек

// и размер шага

glGetFloatv(GL_POINT_SIZE_RANGE,sizes);

glGetFloatv(GL_POINT_SIZE_GRANULARITY,&step);

 

Здесь массив размеров будет содержать два элемента – наименьшее и наибольшее возможное значение glPointsize. Кроме того, шаг переменной будет равен наименьшему шагу, возможному между размерами точек. Спецификация OpenGL требует поддержки только одного размера точек – 1,0. Программная реализация OpenGL от Microsoft, например, позволяет менять размер точек от 0,5 до 10,0 с минимальным размером шага 0,125. Задание размера, не входящего в диапазон, не интерпретируется как ошибка. Вместо этого используется наибольший или наименьший поддерживаемый размер, ближайший к заданному значению. Точки, в отличие от других геометрических объектов, не меняются при делении на коэффициент перспективы. Т.е. они не становятся меньше при удалении от точки наблюдения, и не становятся больше при приближении к наблюдателю. Точки всегда являются квадратными. Даже используя glPointsize для увеличения размера точек, вы просто получите большие квадраты! Чтобы увидеть круглые точки, нужно использовать механизм сглаживания, о котором будет сказано ниже.

·    Положение вершины в пространстве

Для рисования вершины используется команда glVertex, с помощью которой и задаются координаты для отрисовки. В общем виде команда записывается следующим образом

void glVertex[2 3 4][s i f d] (type coords)
либо

void glVertex[2 3 4][s i f d]v (type *coords)

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

 glVertex2f(0, 0)

 Буква f в названии данной функции определяет тип ее аргументов, в данном случае это вещественные числа типа float.

Если для отрисовки используется трехмерная сцена, то точка в пространстве определяется либо тремя координатами, например:

glVertex3f(0.5, 0.3,-0.7),

либо четырьмя координатами:

glVertex4f(0.5, 0.3,-0.7, 0.5)

На самом деле, каждая из этих команд задает четыре координаты для одной вершины: x, y, z, w, так называемые однородные координаты. Такой подход необходим для управления объектом в трехмерном объеме, но мы об этом поговорим позже. А сейчас вам достаточно знать, что, в зависимости от суффикса, программист может самостоятельно задавать определенную часть из 4 возможных координат, а остальные устанавливаются компилятором по умолчанию: координата z устанавливается равной 0, координата w – равной 1.

Координатные оси расположены так, что точка (0,0) находится в центре экрана, ось x направлена влево, ось y – вверх, а ось z – из экрана. Это расположение осей мировой системы координат, в которой задаются координаты вершин объекта, другие системы координат будут рассмотрены позже.

Команды glVertex должны размещаться между командными скобками. При этом количество точек может быть любым, зависящим от режима отрисовки примитива (см. ниже).

Аргументом функции glBegin для рисования точек является константа GL_POINTS.

·    Цвет

Цвет отдельных вершин или примитивов может устанавливаться как вне командных скобок, так и внутри них. Для установки цвета используется команда

      void glColor[3 4][b s i f] (GLtype components)

     void glColor[3 4][b s i f]v (GLtype components).

Цифра 3 или 4 в суффиксе команды означает число аргументов. В случае, если установлен при инициализации флаг RGB, то цвет формируется как сумма 3 components: красного, зеленого и синего цветов в указанной пропорции. Если же был установлен флаг RGBA, то первые три параметра задают R, G, B компоненты цвета, а последний параметр будет определять коэффициент непрозрачности (альфа-компонента), с помощью которого можно управлять интенсивностью цвета в изображении.

 Если в названии команды указан тип ‘f’ (float), то значения всех параметров должны принадлежать отрезку [0,1], при этом по умолчанию значение альфа-компоненты устанавливается равным 1.0, что соответствует полной непрозрачности. Тип ‘ub’ (unsigned byte) подразумевает, что значения должны лежать в отрезке [0,255].

По умолчанию значения компонент задаются в виде вещественных чисел в интервале [0; 1], например

glColor3f(0.3f, 0.5f, 0.1f)

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

                      glColor3i(214748647, 214748647,  214748647).

Однако предпочтительно использовать команду в вещественной форме, т.к. OpenGL хранит данные именно в вещественном формате.

 

Таблица значений аргументов для задания основных цветов :

glColor3f(0.0, 0.0, 0.0);           black

glColor3f(1.0, 0.0, 0.0);           red

glColor3f(0.0, 1.0, 0.0);           green

glColor3f(1.0, 1.0, 0.0);           yellow

glColor3f(0.0, 0.0, 1.0);           blue

glColor3f(1.0, 0.0, 1.0);           magenta

glColor3f(0.0, 1.0, 1.0);           cyan

glColor3f(1.0, 1.0, 1.0);           white

 

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

void glShadeModel (GLenum  mode)

где при  mode = GL_SMOOTH  интерполяция включается (установка по умолчанию), а при mode = GL_FLAT, – отключается.

Остальные атрибуты – вектор нормали и текстурные координаты – будут рассмотрены позже, в соответствующих темах.

Примеры рисования точек:

 // рисуем точки

  glPointSize(2); 

  glBegin(GL_POINTS); 

   glColor3d(1,0,0); 

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

   glColor3d(0,1,0); 

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

   glColor3d(0,0,1);     // третья

   glVertex3d(-3.5,4,0);

  glEnd();

 

// десять точек по диагонали

glBegin (GL_POINTS);
For i := 0 to 9 do
glVertex2f (i / 5 - 1, i / 5 - 1); 
glEnd
;

          //
сто точек со случайными координатами и цветами
glBegin (GL_POINTS);
For i := 1 to 100 do begin
glColor3f (random, random, random);

 glVertex2f (random * 2 - 1, random * 2-1); 
end; 
glEnd;

 

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

 

xpos: GLfloat; // координаты курсора в системе координат OpenGL

ypos: GLfloat;
Затем создать обработчик события MouseMove формы
xpos: = 2 * X / ClientWidth - 1;
ypos: = 2 * (ClientHeight - Y) / ClientHeight - 1;
Refresh; // перерисовываем окно

 

В обработчике события Paint необходимо описать локальную целочисленную переменную i и содержательную часть кода привести к виду

 

For i: = 1 to 30 do begin // тридцать точек
glColor3f (random, random, random); // случайного цвета
glBegin (GL_POINTS}; // со случайными координатами

glVertex2f (xpos + 0. 2 * random * sin (random (360)),
ypos + 0. 2 * random * cos (random (3 60))); 
glEnd; 
end;

 

Данный код позволит наблюдать появление облака разноцветных точек  при движении курсора по поверхности формы. Обратите внимание, что обработчик движения мыши заканчивается вызовом Refresh – принудительной перерисовкой окна при каждом движении курсора.

 

Вывод  линий

Для рисования линий существует три режима: одиночные линии, ломаная, замкнутая ломаная.

Одиночная линия определяется двумя вершинами. Если требуется нарисовать несколько одиночных линий, то между командными скобками должны быть описаны координаты пар вершин, т.е. количество команд glVertex между командными скобками должно быть четным. В случае, если количество вершин нечетно – последняя вершина игнорируется.

Аргументом функции glBegin для рисования одиночных линий является константа GL_LINES.

Если требуется нарисовать ломаную линию, то в командных скобках используют константу GL_LINE_STRIP. Вершины, перечисленные между командными скобками, интерпретируются следующим образом: конечная точка первой линии является начальной точкой следующего звена ломаной и т.д. Количество вершин может быть как четным, так и нечетным.  Ширина и тип ломаной линии задаются так же, как и для одиночных линий.

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

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

Для задания толщины линии используется команда glLineWidth(). Аргументом является натуральное число, определяющее толщину в пикселах. Так же, как и при установке размера точки, команду установки толщины линии записывают за пределами командных скобок.

Для изменения типа линии используется команда

void glLineStipple (GLint factor, GLushort pattern)

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

00000000000000001111111111111111

Важно: команда glLineStipple должна быть указана вне операторных скобок.

ПРИМЕР:

glColor3f(1.0, 0.0, 0.0);      // Установили красный цвет

glEnable(GL_LINE_SMOOTH);// Включение режима сглаживания

glLineWidth(3);      // Установили толщину линии 3 пиксела

glLineStipple(1, 0xF0F0);      // Тип линии – пунктирная

glEnable(GL_LINE_STIPPLE);// Разрешить изменение типа линии

glBegin(GL_LINES);        // Режим рисования – одиночные линии

glVertex2f(-0.5, -0.7);   // Начало первой линии

glVertex2f(0.0, 0.0);     // Конец первой линии

glVertex2f(0.1, 0.9);     // Начало второй линии

glVertex2f(0.3, -0.5);         // Конец второй линии

glEnd();                  // Конец рисования

glDisable(GL_LINE_SMOOTH);// Отключение режима сглаживания

 

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

Для рисования отдельных треугольников константа командных скобок: GL_TRIANGLES. Количество вершин, перечисленных между командными скобками должно быть кратно трем. Каждые три вершины определяют  треугольник (рис. 3), поэтому лишние вершины в списке игнорируются.

                                                     а2

 

 

                                       а1                             а3

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

 


Лента треугольников GL_TRIANGLE_STRIP используется, если изображение может быть построено с помощью нескольких треугольников, имеющих смежные стороны (рис. 4).

Здесь сторона  a2a3 является общей стороной для первого и второго треугольников, сторона a3a4 – общей стороной второго и третьего треугольников и т.д. Если такую фигуру описывать с помощью одиночных треугольников, то необходимо задавать координаты всех вершин всех треугольников: a1, a2, a3, a2, a3, a4, a3, a4, a5, a4, a5, a6 – всего 12 вершин. Использование ленты треугольников позволяет не дублировать вершины при описании их координат. Изображенная на рисунке фигура может быть представлена лентой треугольников, координаты вершин перечисляются в следующем порядке: a1, a2, a3, a4, a5, a6 – достаточно 6 вершин.


В случае, если треугольники имеют одну общую вершину, целесообразно использовать «веер треугольников» GL_TRIANGLE_FAN (рис. 5). При описании вершин первой в списке должна стоять общая вершина (а1), а остальные вершины в списке распределяются следующим образом: первые три вершины определяют первый треугольник; первая, третья и четвертая – второй; первая, четвертая и пятая – третий; и т.д. Если в списке имеется N вершин, то будет изображено N-2 треугольника.

Схематично порядок обхода вершин при отрисовке разного типа треугольников можно представить следующим образом (рис. 6):

Рисунок 6.  Обход вершин треугольников

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

Константа командных скобок для рисования отдельных четырехугольников: GL_QUADS.

                                   а    

 

 

Рисунок 7.  Отрисовка четырехугольника

 

Четырехугольник определяется группой из 4-х вершин, следовательно, количество вершин, записанных между командными скобками, должно быть кратно 4. Каждая четверка вершин определяет отдельный четырехугольник. Лишние вершины игнорируются.

Если изображение создается из связанных четырехугольников (каждая пара четырехугольников имеет общую сторону), то используется примитив «лента четырехугольников» - GL_QUAD_STRIP (рис.8).

tmp8E4A-390.png (909×269) 

Рисунок 8.  Примеры применения ленты четырехугольников

 

Четырехугольник задается для каждой, кроме двух первых, пары вершин. Вершины 2n-1, 2n, 2n+2 и 2n+1 задают четырехугольник n. Всего отобразится (N-2)/2 четырехугольников. Заметим, что порядок сборки связных четырехугольников отличается от порядка, применяемого для независимых четырехугольников.

Порядок обхода вершин четырехугольников представлен на рисунке 8: а – независимые четырехугольники, б – лента четырехугольников.

Рисунок 9.  Порядок обхода вершин четырехугольников

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

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

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


Список вершин для данного многоугольника: a1, a2, a3, a4, a5, a6, a7 (рис. 10).

Важное уточнение: При отрисовке многоугольников следует иметь в виду наличие лицевых (передних) и обратных (задних) граней у каждой поверхности.

Грань считается лицевой, если вершины перечисляются в направлении против часовой стрелки (в положительном направлении для левосторонней системы координат), и обратной, если обход вершин производится по часовой стрелке (в отрицательном направлении для левосторонней системы координат).

Opengl предоставляет возможность управлять тем, какой тип граней отображать на экране. Для этого сначала надо установить соответствующий режим вызовом команды glEnable (GL_CULL_FACE), а затем выбрать тип отображаемых граней с помощью команды

void glCullFace (GLenum mode),

при  mode  = GL_FRONT  из изображения удаляются все лицевые грани,

 при mode  ­= GL_BACK – из изображения удаляются все обратные грани (установка по умолчанию).

Построение невыпуклых полигонов

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

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

Рисунок 11. Отрисовка невыпуклого многоугольника

 

Следовательно, фигура может быть описана как 2 примитива типа GL_POLIGON.  Оптимальным будет разбиение невыпуклой фигуры на треугольники, поскольку построение треугольников, как правило, реализовано на аппаратном уровне.

Режим вывода многоугольников

Многоугольники можно отрисовать в нескольких видах: в точечном, в контурном виде (без заливки), и с заливкой. Для задания способа рисования  используется функция   

void PolygonMode ( enum face, enum mode ),

где face устанавливает тип многоугольников и может принимать значения

·      GL_FRONT – для лицевых граней;

·     GL_BACK – для обратных граней;

·     GL_FRONT_AND_BACK для всех граней.

mode указывает, как будет рисоваться многоугольник.

·     GL_POINT – при таком режиме будут отображаться только вершины многоугольников.

·     GL_LINE – при таком режиме многоугольник будет представляться набором отрезков.

·     GL_FILL – при таком режиме многоугольники будут закрашиваться текущим цветом с учетом освещения, и этот режим установлен по умолчанию.

Описанная команда помещается перед командными скобками.

Пример:

 glColor3f(0,1.0,0);

 glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);

 glBegin(GL_TRIANGLES);

 glVertex2dv(xy2[0]);

 glVertex2dv(xy2[1]);

 glVertex2dv(xy2[2]);

 glEnd();

 glPolygonMode(GL_BACK,GL_LINE);

 glBegin(GL_TRIANGLE_FAN);

 glVertex2dv(xy2[3]);

 glVertex2dv(xy2[4]);

 glVertex2dv(xy2[5]);

 glVertex2dv(xy2[6]);

 glEnd();

Массив xy2 описан как

double xy2[10][2];

и проинициализирован функцией coords2 (вызывается в main):

void coords2()

{

 double ip2, *ipart2=&ip2;

 for(int i=0;i<zx;i++)

   for(int j=0;j<2;j++)

 xy2[i][j]=modf(pow(3.14,random(7)),ipart2);

}

 

Включение и отключение режима сглаживания

Для установки режима сглаживания, впрочем, как и для установки любого другого режима в Opengl, перед командными скобками необходимо вызвать функцию glEnable() с соответствующей константой в качестве аргумента, а после командных скобок – функцию glDisable() с той же константой.

Говоря непосредственно о режиме сглаживания, выбор константы определяется примитивом, сглаживание которого должно быть включено (или отключено):

 

·    GL_POINT_SMOOTH (сглаживание для точек),

·    GL_LINE_SMOOTH (сглаживание линий),

·    GL_POLYGON_SMOOTH (сглаживание для полигонов).

 

Зачем нужен этот режим? Например, размер, заданный в glPointSize, не является точным размером точки в пикселях, а приблизительным диаметром окружности, содержащей все пиксели, используемые для рисования точки. Вы указываете OpenGL рисовать точки как улучшенные (т.е. маленькие, закрашенные окружности), разрешая их сглаживание. Эта технология вместе со сглаживанием линий относятся к категории защиты от наложения (antialiasing). Защита от наложения это технология, позволяющая сглаживать зазубренные края и округлять углы (рис. 12).

 

Основы программирования OpenGL в Borland С++Builder и Delphi. Простейшие объектыОсновы программирования OpenGL в Borland С++Builder и Delphi. Простейшие объекты

   а)                                                                                                              б)

Рисунок 12.  Отрисовка точек с:

а – отключенным режимом сглаживания,

б – включенным режимом

сглаживания

 

Режимы закрашивания для многоугольников

Если при отрисовке  многоугольников для разных вершин примитивов были заданы различные цвета, то фигуры окрашиваются градиентно, т.е. с плавным переходом цветов от вершины к вершине (рис.13). Это происходит из-за того, что по умолчанию способ сглаживания (смешивания, тонирования) задан плавным.

Основы программирования OpenGL в Borland С++Builder и Delphi. Простейшие объекты

Рисунок 13.  Градиентное закрашивание

 

Чтобы изменить способ сглаживания, необходимо перед командными скобками вызвать функцию

glShadeModel (GL_FLAT)

В этом случае связанные фигуры окрашиваются по правилу старшинства цвета второго примитива.

 

Методика выполнения лабораторной работы

Данная лабораторная работа является логичным продолжением предыдущей лабораторной работы, на которой для заданной информационной системы были выделены варианты ее использования и построены usе case диаграммы. Поэтому для реализации используется задание, полученное на предыдущей лабораторной работе.

Для успешного выполнения лабораторной работы необходимо использовать результаты предыдущей работы.

 

Варианты заданий

1.      Построить точки, расположенные в вершинах правильного n-угольника. Установить режим сглаживания для точек. Экспериментально определить максимальный размер точки, при котором возможно сглаживание.

2.      Используя примитив для вывода линий нарисовать правильный n-угольник. Изменить тип и ширину линий. 

3.      Используя примитив для вывода ломаной линии нарисовать фигуру, изображенную на рис.1 (см. таблица 4).

4.      Используя примитив для вывода замкнутой ломаной нарисовать фигуру, изображенную на рис.2 (см. таблица 4).

5.      Построить фигуру, изображенную на рис.2 (см. таблица 4), разбив ее на треугольники (каждый треугольник окрашен случайным цветом). Выполните три варианта построений с использованием примитивов:

         А)  треугольник;

         Б)  лента треугольников;

         В)  веер треугольников.

6.      Используя примитив для вывода многоугольников построить правильный n-угольник.

7.      Построить невыпуклый многоугольник, изображенный на рис.3, представив его в виде совокупности отдельных многоугольников, назначив каждому многоугольнику свой цвет. Посмотреть результат работы программы для различных способов тонирования.

8.      Изменить программу предыдущей задачи таким образом, чтобы

·                  лицевые грани изображались только вершинами;

·                  лицевые грани изображались закрашенными, а обратные – линиями;

Таблица 4

Вариант 1

N=5

 

 

 

 

 

 

                  

 

 

 

Вариант 2

N=7

 

 

 

 

 

 

Вариант 3

N=6

 

 

 

 

 

 

Вариант 4

N=4

 

 

 

 

 

 

Вариант 5

N=8

 

 

 

 

 

 

 

Содержание отчета

 

Результатом выполнения лабораторной работы должен стать отчет (в печатном и электронном вариантах), состоящий из следующих пунктов:

1)     постановка задачи;

2)    перечень функций Opengl, использованных в предлагаемом решении;

3)     блок-схема алгоритма отрисовки сцены;

4)     программный код предлагаемого решения;

5)     скриншоты результатов работы программы.

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

·       отчет о лабораторной работе (в печатном виде);

·       программную реализацию решения.

Оценка (в баллах)  выставляется за устный отчет студента по предоставленным преподавателю материалам.

 

Пример выполнения лабораторной работы

 

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

    case WM_PAINT:

        {

            PAINTSTRUCT ps;

            HDC hdc = BeginPaint(hWnd, &ps);

               glClearColor(0, 100, 130, 255);

             

              //домик

              glColor3ub(255, 0, 0);

              glBegin(GL_TRIANGLES);//крыша

              glVertex2f(-0.6f, 0.6f); //верхняя вершина

              glVertex2f(-0.9f, 0.2f); //левая вершина

              glVertex2f(-0.3f, 0.2f); //правая вершина

              glEnd();

              glColor3ub(0, 0, 255);

              glBegin(GL_POLYGON);//стена

              glVertex2f(-0.79f, 0.2f);

              glVertex2f(-0.79f, -0.4f);

              glVertex2f(-0.41f, -0.4f);

              glVertex2f(-0.41f, 0.2f);

              glEnd();

              glColor3ub(100, 130, 255);

              glBegin(GL_POLYGON);//окно

              glVertex2f(-0.7f, 0.1f);

              glVertex2f(-0.7f, -0.15f);

              glVertex2f(-0.5f, -0.15f);

              glVertex2f(-0.5f, 0.1f);

              glEnd();

              glColor3ub(0, 0, 255);

              glLineWidth(5);

              glBegin(GL_LINES);//рама окна

              glVertex2f(-0.6f, 0.1f);

              glVertex2f(-0.6f, -0.15f);

              glVertex2f(-0.6f, 0.0f);

              glVertex2f(-0.5f, 0.0f);

              glEnd();

             

         GLfloat the;//окно в крыше

         GLfloat pi = acos(-1.0);

         GLfloat radius = 2.0f; // радиус

         GLfloat step = 0.5f; // чем больше шаг тем хуже диск

              glColor3ub(100, 130, 255);

              glBegin(GL_TRIANGLE_FAN);

              for (GLfloat a = 0.0f; a < 360.0f; a += step) {

                   theta = 2.0f * pi * a / 180.0f;

                   glVertex2f(radius * cos(theta)/50-0.6f, radius * sin(theta)/25+0.4f);

              }

              glEnd();

 

              //ёлки-иголки

              float x = 0.0f, y=0.0f;

              for (int i = 0; i < 4; i++)

              {

                   glColor3ub(0, 150, 0);

                   glBegin(GL_TRIANGLES);

                   glVertex2f(0.0f, 0.5f-y);

                   glVertex2f(-0.1f-x, 0.3f-y);

                   glVertex2f(0.1f+x, 0.3f-y);

                   glEnd();

                   x += 0.05f; y += 0.15;

              }

              x = 0.0f, y = 0.0f;

              for (int i = 0; i < 4; i++)

              {

                   glColor3ub(0, 100, 0);

                   glBegin(GL_TRIANGLES);

                   glVertex2f(0.3f, 0.6f - y);

                   glVertex2f(0.2f - x, 0.4f - y);

                   glVertex2f(0.4f + x, 0.4f - y);

                   glEnd();

                   x += 0.03f; y += 0.12;

              }

              glColor3ub(150, 100, 0);

              glLineWidth(50);

              glBegin(GL_LINES);

              glVertex2f(0.0f, -0.1f);

              glVertex2f(0.0f, -0.35f);

              glVertex2f(0.3f, 0.1f);

              glVertex2f(0.3f, -0.1f);

              glEnd();

              glColor3ub(255, 237, 0);

              glBegin(GL_TRIANGLE_FAN);

              for (GLfloat a = 0.0f; a < 360.0f; a += step) {

                   the = 2.0f * pi * a / 180.0f;

glVertex2f(radius*cos(theta)/20+0.8f,radius*sin(the)/10+0.6f);

              }

              glEnd();

              glLineWidth(50);

               glBegin(GL_LINES);

              glVertex2f(0.5f, 0.6f);

              glVertex2f(0.68f, 0.6f);

              glVertex2f(0.55f, 0.35f);

              glVertex2f(0.7f, 0.5f);

              glVertex2f(0.6f, 0.1f);

              glVertex2f(0.72f, 0.4f);

              glVertex2f(0.75f, 0.05f);

              glVertex2f(0.8f, 0.35f);

              glVertex2f(0.9f, 0.35f);

              glVertex2f(0.98f, 0.15f);

              glVertex2f(0.6f, 0.9f);

              glVertex2f(0.7f, 0.72f);

              glEnd();

        

              SwapBuffers(hDC);

            EndPaint(hWnd, &ps);

        }

        break;

    case WM_DESTROY:

        PostQuitMessage(0);

        break;

    default:

        return DefWindowProc(hWnd, message, wParam, lParam);

    }

    return 0;

}

 

Скриншот работы программы:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Дополнительный материал

 

// отрисовка диска 1 вариант

void drawCircle(float x, float y, float r, int amountSegments)

{

         glBegin(GL_LINE_LOOP);

         for (int i = 0; i < amountSegments; i++)

         {

              float angle = 2.0 * 3.1415926 * float(i) / float(amountSegments);

              float dx = r * cosf(angle);

              float dy = r * sinf(angle);

              glVertex2f(x + dx, y + dy);

         }

         glEnd();

    

}

 

/*********************************************************/

 

// отрисовка диска 2 вариант

GLfloat theta;

GLfloat pi = 3.14f;

GLfloat radius = 0.1f; // радиус

GLfloat step = 3.0f; // чем больше шаг, тем хуже диск

// рисуем диск по часовой стрелке GL_CW

glBegin(GL_TRIANGLE_FAN);

for (GLfloat a = 0.0f; a < 360.0f; a += step)

 {

     theta = 1.0f * pi * a / 180.0f;

     glColor4f(a / 360.0f, 1.0f, 1.0f - a / 360.0f, 1.0f);

     glVertex2f(radius * cos(theta)/2, radius * sin(theta));

}

glEnd();

 

/**********************************************************/

 

// десять точек по диагонали

glBegin (GL_POINTS);
For i := 0 to 9 do
glVertex2f (i / 5 - 1, i / 5 - 1); 
glEnd;
/*********************************************************/


//100 точек со случайными координатами и цветами:
glBegin (GL_POINTS);
For i := 1 to 100 do begin
glColor3f (random, random, random); glVertex2f (random * 2 - 1, random * 2-1); 
end; 
glEnd;

 

/*********************************************************/

 

// точки рядом с курсором при движении

xpos: GLfloat; // координаты курсора в системе координат 
OpenGL ypos: GLfloat;

// создать обработчик события MouseMove формы
xpos: = 2 * X / ClientWidth - 1;
ypos: = 2 * (ClientHeight - Y) / ClientHeight - 1;
Refresh; //
перерисовываем окно

// в обработчике Paint описать локальную целочисленную

//переменную i и

For i: = 1 to 30 do begin // тридцать точек
glColor3f (random, random, random); // случайного цвета
glBegin (GL_POINTS};

 // со случайными координатами вокруг курсора
glVertex2f (xpos + 0. 2 * random * sin (random (360)),
ypos + 0. 2 * random * cos (random (3 60))); 
glEnd; 
end;

/*Если все сделано правильно, то при движении курсора по поверхности формы рядом с курсором должно появляться облачко разноцветных точек. Обратите внимание, что обработчик движения мыши заканчивается вызовом Refresh - принудительной перерисовкой окна при каждом движении курсора.*/

 

/**********************************************************/

 

Кроме рассмотренных стандартных примитивов в библиотеках GLU и GLUT описаны более сложные фигуры,

·    такие, как сфера, цилиндр, диск (в GLU)

·    и сфера, куб, конус, тор, тетраэдр, додекаэдр, икосаэдр, октаэдр и, внезапно,  чайник (в GLUT).

Utah_teapot_simple_2

 

Рисунок 14. Чайник из библиотеки giut.h

 

Автоматическое наложение текстуры предусмотрено только для фигур из библиотеки.

Например, чтобы нарисовать сферу или цилиндр, надо сначала создать объект специального типа GLUquadricObj с помощью команды

GLUquadricObj* gluNewQuadric (void);

а затем вызвать соответствующую команду:

void gluSphere (GLUquadricObj * qobj, GLdouble radius,

                GLint slices, GLint stacks)

void gluCylinder (GLUquadricObj * qobj,

                  GLdouble baseRadius,

                  GLdouble topRadius,

                  GLdouble height, GLint slices,

                  GLint stacks),

где параметр slices задает число разбиений вокруг оси z, а stacks – вдоль оси z.

Важно отметить, что для корректного построения перечисленных примитивов необходимо удалять невидимые линии и поверхности, для чего надо включить соответствующий режим вызовом команды glEnable (GL_DEPTH_TEST).

Контрольные вопросы

1.  Что такое командные скобки, каково их назначение?

2.  Какие константы библиотеки OpenGL могут быть параметрами функции glBegin?

3.  Что такое антиэлайзинг (anti-aliasing), для чего он служит?

4.  Какие режимы существуют для рисования линий?

5.  Какие режимы существуют для изображения треугольников, чем они различаются?

6.  Что такое выпуклые и невыпуклые многоугольники?

7.  Каким образом можно построить невыпуклый многоугольник?

8.  Чем отличаются лицевые и обратные грани?

9.  Какая команда изменяет способ тонирования?

10.  Какие режимы вывода многоугольников вам известны? В каком месте программы должна быть записана команда, изменяющая режим вывода многоугольников?


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

 

1)    Тарасов И. Opengl в России. – Режим доступа :   http://www.helloworld.ru/texts/comp/games/opengl/opengl2/index.html; (скачать : http://www.read.in.ua/book120840/?razdel=11&p=47).

2)    OpenGL. – Режим доступа : https://www.opengl.org.

3)    Верма Р. Д. Введение в Opengl . – Москва : Горячая линия – Телеком, 2015. – 303 с.

4)    Баяковский Ю.М., Игнатенко А.В., Фролов А.И. Графическая библиотека OpenGL.:  Учебно-методическое пособие. – Москва : ВМиК МГУ, 2003. – 132 с. 

5)    Гайдуков С. Профессиональное программирование трехмерной графики на С++. – Санкт-Петербург : БХВ-Петербург, 2004. – 736 с.

6)     Боресков А. Графика трехмерной компьютерной игры на основе OpenGL. – Москва : Диалог-МИФИ, 2005.

7)    Херн Д., Бейкер М. Компьютерная графика и стандарт OpenGL. – Москва : ИД «Вильямс», 2005.

8)    Программирование компьютерной графики средствами OpenG :  Документация, статьи, советы. – Режим доступа : opengl.gamedev.ru.

 

 

 

 

 

 

 

 

Оглавление

ВВЕДЕНИЕ. 3

OpenGL: инициализация, построение двумерных примитивов в отдельном окне  6

Теоретическая информация. 6

Методика выполнения лабораторной работы. 25

Варианты заданий. 25

Содержание отчета. 26

Пример выполнения лабораторной работы. 27

Дополнительный материал. 30

Контрольные вопросы.. 43

OpenGL: Разработка приложения для визуализации связного набора двумерных примитивов. 44

Теоретическая информация. 44

Методика выполнения лабораторной работы. 62

Варианты заданий. 62

Содержание отчета. 64

Пример выполнения лабораторной работы. 65

Дополнительный материал. 69

Контрольные вопросы.. 72

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

 

 

 

 

 

 

 

 

 

 

 

 

 



Скачано с www.znanio.ru



[1] В программе на C++ указатель записан в виде &pfd, в программе на Delphi следует записать @pfd.

[2] В Delphi вместо  символа «|»  используется «or».

[3] В Delphi шаблон запишется в виде, например $F0F0.