Создание графических моделей с помощью Open Graphics Library

Структура приложений, использующих OpenGL

Показывать лекцию целиком

Цель лекции: Создать базовые приложения с использованием OpenGL на C# и на Object Pascal (Delphi).

Примеры

Пример № 3.1 Базовые приложения на C# (WinForms) и Object Pascal (Delphi), использующие библиотеку OpenGL.

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

  • Инициализация библиотеки OpenGL.
  • Подготовительные операции для формирования изображения библиотекой OpenGL (при необходимости).
  • Определение области вывода изображения.
  • Основная функция формирования изображения.
  • Обработка событий мыши и клавиатуры для реализации интерактивности в программе (при необходимости).
  • Завершение работы c библиотекой OpenGL.
  • Инициализация OpenGL в библиотеке OpenTK

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

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

    В следующем разделе рассмотрена инициализация библиотеки OpenGL в приложении на Object Pascal (Delphi). Аналогичную процедуру инициализации выполняет и библиотека OpenTK, чтобы в этом убедиться, достаточно заглянуть в исходный код этой библиотеки.

    Инициализация библиотеки OpenGL в приложениях на Object Pascal (Delphi)

    Перед началом использования команд OpenGL необходимо выполнить инициализацию библиотеки OpenGL. Основной задачей данного этапа является определение основных параметров работы библиотеки OpenGL, которые в дальнейшем не могут быть изменены без завершения работы с библиотекой OpenGL и её повторной инициализации. На этом этапе выполняется определение доступных возможностей аппаратуры для вывода изображения. Хотя библиотека OpenGL является кросплатформенной, но инициализация является специфичной для каждой платформы, поэтому на этом этапе используются функции операционной системы Windows. Инициализация библиотеки может выполняться в обработчике события OnCreate формы, на которой будет выводиться изображение. Для возможности использования функций и процедур библиотеки OpenGL необходимо указать модуль opengl.pas в разделе uses каждого модуля, в котором они будут использоваться. Ниже приведен пример инициализации библиотеки OpenGL.

    implementation
    {$R *.dfm}
    Uses OpenGL;
    Const
      //угол поворота изображения при нажатии на клавишу
      AngleDl=5; 
    // глобальные переменные
    var
      hrc: HGLRC;
      dc: HDC;
      // переменные для хранения текущих углов поворота изображения
      AngleX, AngleY, AngleZ: integer;
    procedure TForm1.FormCreate(Sender: TObject);
    var PFD: TPixelFormatDescriptor;
      i: integer;
    begin
      // получение контекста устройства с помощью системной функции GetDC
      dc:=GetDC(handle);
      // обнуление структуры pfd
      FillChar(PFD, SizeOf(PFD),0);
      pfd.dwFlags  := PFD_DRAW_TO_WINDOW 
        or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER;
      // запрос у операционной системы наиболее подходящего формата пиксела
      i:=ChoosePixelFormat(dc, @PFD);
      if i=0 then
        raise Exception.Create('Не определен формат пиксела !');
      // установка в качестве текущего формата пиксела, полученного с помощью 
      // ф-ии  ChoosePixelFormat
      if not SetPixelFormat(dc, i, @PFD) then
        raise Exception.Create('Ошибка при установке формата пиксела !');
      // создание контекста воспроизведения для библиотеки OpenGL
      // и сохранение его   дескриптора в глобальной переменной
      hrc:=wglCreateContext(dc);
      if hrc=0 then
        raise Exception.Create('Ошибка создания контекста воспроизведения !');
      // установка текущего контекста воспроизведения
      if not wglMakeCurrent(dc, hrc) then
        raise 
          Exception.Create('Невозможно установить текущий контекст воспроизведения !');
      glEnable(GL_DEPTH_TEST);
    end;
    

    В листинге 3.1 объявляются глобальные переменные ). Большинство процедур и функций в листинге 3.1 не входят в состав OpenGL и являются функциями операционной системы. В частности функции, названия которых начинаются с последовательности символов "wgl", также являются системными и предназначены для поддержки возможностей OpenGL в операционной системе Windows.

    При разработке программ на языке программирования Object Pascal c использованием библиотеки визуальных компонентов (VCL) системные функции использовать приходится довольно редко, так как набор классов библиотеки VCL позволяет этого избежать. При работе с библиотекой OpenGL потребуется несколько системных функций. В процедуре FormCreate с помощью системной функции GetDC выполняется получение дескриптора контекста устройства. Функция GetDC позволяет получить параметры вывода изображения, которые используются для формы. Информация о форме передается в функцию с помощью дескриптора формы, который возвращается свойством формы Handle.

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

    procedure FillChar(var Dest; count: Integer; Value: Char);
    

    Процедура имеет следующие параметры:

  • Dest - переменная, область памяти которой требуется заполнить значением Value;
  • Count – размер заполняемой области памяти в байтах;
  • Value – значение байта, которое должно быть скопировано в каждый байт памяти, определяемой параметром Dest.
  • В листинге 3.1 для определения размера структуры PFD используется функция

    function SizeOf(X): Integer;
    

    Функция возвращает размер в байтах переменной или типа данных, переданных ей в качестве параметра X.

    Для работы с библиотекой OpenGL операционная система определяет наиболее подходящие параметры вывода изображения, основные из которых перечислены ниже:

  • Тип используемой цветовой палитры: индексная или RGBA. В примерах будет использована только RGBA палитра, которая используется по умолчанию.
  • Глубина цвета или количество бит, которое будет использоваться для хранения цвета каждого пиксела.
  • Использование вторичного буфера.
  • Число бит в буфере глубины.
  • Размер буфера трафарета.
  • Эти параметры задаются с помощью значений полей структуры TPixelFormatDescriptor. Для разных приложений могут потребоваться различные варианты сочетаний этих параметров. Некоторые сочетания могут быть невозможны или не поддерживаться аппаратной платформой или операционной системой. Для того чтобы определить доступный вариант сочетания этих параметров реализуется диалог между программой и операционной системой: программа указывает, какие параметры ей необходимы и передает запрос операционной системе на возможность их использования. Операционная система анализирует возможности оборудования и возвращает приложению наиболее подходящую конфигурацию из возможных вариантов. Программа может проанализировать эту конфигурацию и определить возможность её использования. После выбора приложением подходящей конфигурации для работы библиотеки OpenGL эти параметры устанавливаются текущими.

    Для запроса у операционной системы наиболее подходящей конфигурации библиотеки OpenGL для работы приложения используется функция

    function ChoosePixelFormat(DC: HDC; p2: PPixelFormatDescriptor): Integer;
    

    Назначение параметров функции:

  • DC – контекст устройства, полученный с помощью системной функции GetDC.
  • p2 – указатель на переменную типа TPixelFormatDescriptor. В переменной предварительно должны быть указаны параметры библиотеки OpenGL, которые требуются для работы приложения.
  • Функция возвращает номер наиболее подходящей конфигурации библиотеки OpenGL. Операционная система Windows предоставляет ограниченное количество возможных вариантов параметров работы библиотеки OpenGL. Каждое из таких сочетаний имеет свой уникальный целочисленный номер. Один из таких номеров и возвращает функция ChoosePixelFormat.

    В выше приведенном примере () поля структуры TPixelFormatDescriptor в переменной PFD заполняются нулевыми значениями с помощью процедуры FillChar, и затем поле dwFlags инициализируется для использования библиотекой OpenGL вторичного буфера.

    Для заданных значений структуры TPixelFormatDescriptor операционная система возвращает номер конфигурации для библиотеки OpenGL, в которой:

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

    При использовании вторичного буфера библиотека OpenGL формирует изображение сначала во вторичном буфере. Вторичный буфер представляет собой блок памяти и непосредственно не отображается. После завершения формирования изображения во вторичном буфере он должен быть скопирован в область вывода изображения. Основным назначением вторичного буфера является увеличение скорости формирования изображения.

    Константы, используемые для задания значения поля dwFlags структуры TPixelFormatDescriptor, имеют следующие значения:

  • PFD_DRAW_TO_WINDOW – изображение формируется в окне (на форме). Значение должно быть указано обязательно.
  • PFD_SUPPORT_OPENGL – изображение формируется с библиотекой OpenGL. Значение должно быть указано обязательно.
  • PFD_DOUBLEBUFFER – для формирования изображения будет использоваться вторичный буфер.
  • Для отображения изображения, сформированного во вторичном буфере, используется системная функция:

    function SwapBuffers(DC: HDC): BOOL;
    

    Пример её использования приведен в листинге 3.3 .

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

    function SetPixelFormat(DC:HDC;PixelFormat:Integer;
    FormatDef: PPixelFormatDescriptor): BOOL;
    

    Назначение параметров функции:

    DC – контекст устройства, полученный с помощью системной функции GetDC.

    PixelFormat – номер формата пиксела, полученный с помощью функции ChoosePixelFormat.

    FormatDef – указатель на переменную типа TPixelFormatDescriptor, в которой должны быть значения, соответствующие выбранному номеру формата пиксела, который указан в качестве значения параметра PixelFormat.

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

    function wglCreateContext(DC: HDC): HGLRC;
    

    Функции передается единственный параметр DC – контекст устройства, полученный с помощью системной функции GetDC. Функция возвращает дескриптор контекста воспроизведения.

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

    function wglMakeCurrent(DC: HDC; p2: HGLRC): BOOL;
    

    Назначение параметров функции:

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

    В листинге 3.1 после инициализации библиотеки вызывается процедура glEnable со значением параметра GL_DEPTH_TEST, который включает тест глубины.

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

    Определение области вывода изображения и матрицы проекции

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

    Viewport (x,y: GLint; width, height: GLsizei)
    

    В приведённых ниже примерах выполняется определение матрицы проекции с помощью команды .

    Библиотека OpenTK, C#

    В библиотеке OpenTK для этого применяется статический метод класса GL (C#):

    void Viewport(int x, int y, int width, int height);
    

    Координаты области вывода задаются относительно компонента GLControl и область вывода ограничивается размерами этого компонента.

    Параметры команды имеют следующие значения:

  • x, y – координаты левой верхней вершины прямоугольной области отображения в координатах компонента GLControl;
  • width, height – ширина и высота области вывода в границах компонента GLControl.
  • Обычно вызов метода ).

    private void glControl1_Resize(object sender, EventArgs e)
    {
    GL.Viewport(0, 0, glControl1.Width, glControl1.Height);
    GL.MatrixMode(MatrixMode.Projection);
    GL.LoadIdentity();
    GL.Ortho(-1, 1, -1, 1, -1, 1);
    GL.MatrixMode(MatrixMode.Modelview);
    glControl1.Invalidate();
    }
    

    В конце листинга 3.2 вызывается метод компонента GLControl:

    void Invalidate();
    

    Метод инициализирует событие Paint компонента GLControl, в обработчике которого выполняется обновление изображения с учетом изменения размеров компонента.

    Object Pascal (Delphi)

    В программе на Object Pascal для реализации команды Viewport используется процедура:

    procedure glViewport (x,y: GLint; width, height: GLsizei);
    

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

    Параметры команды имеют следующие значения:

  • x, y – координаты левой верхней вершины прямоугольной области отображения в координатах окна;
  • width, height – ширина и высота области вывода в координатах окна.
  • Обычно вызов процедуры ). Это позволяет корректировать размеры области вывода в соответствии с размерами формы.

    procedure TForm1.FormResize(Sender: TObject);
    begin
      // Установка области вывода
      glViewPort (0, 0, ClientWidth, ClientHeight);
      // задание матрицы проекции
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity;
      glOrtho(-1, 1, -1, 1, -1, 1);
      glMatrixMode(GL_MODELVIEW);
      // перерисовка изображения
      InvalidateRect(Handle, nil, False);
    end;
    

    В листинге 3.3 выполняется вывод изображения на всю клиентскую часть формы, поэтому в качестве параметров процедуры glViewPort указываются размеры клиентской области формы: ClientWidth и ClientHeight.

    В конце обработчика ) вызывается системная функция:

    function InvalidateRect(hWnd: HWND; lpRect: PRect; bErase: BOOL): BOOL;
    

    Она используется для перерисовки изображения после изменения размера области вывода. Функция InvalidateRect посылает форме, дескриптор которой передается в качестве первого параметра hWnd, сообщение, которое приводит к возникновению события OnPaint. В обработчике события OnPaint формируется изображение. Второй параметр определяет прямоугольную область формы для обновления. Если это значение равно nil, то обновляется вся клиентская область формы. Последний параметр bErase определяет: будет ли операционная система обновлять фон формы. При выводе изображения средствами OpenGL это не требуется, поэтому этот параметр равен False.

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

    Основная функция формирования изображения

    Формирование изображения выполняется при каждом обновлении изображения формы в программе на Object Pascal (Delphi) и обновлении изображения компонента GLControl библиотеки OpenTK в программе на C#. Обычно для этого используется обработчик события формы OnPaint в программе на Object Pascal (Delphi) и обработчик события Paint компонента GLControl в программе на C#.

    Ниже приведены примеры соответствующих обработчиков событий ( и ).

    procedure TForm1.FormPaint(Sender: TObject);
    begin
      glClearColor (0.5, 0.5, 0.75, 1.0); // цвет фона
      // очистка буферов цвета и глубины
      glClear (GL_COLOR_BUFFER_BIT + GL_DEPTH_BUFFER_BIT);
      // поворот изображения
      glLoadIdentity;
      glRotatef(AngleX, 1.0, 0.0, 0.0);
      glRotatef(AngleY, 0.0, 1.0, 0.0);
      glRotatef(AngleZ, 0.0, 0.0, 1.0);
      // формирование изображения
      glBegin (GL_LINES);
        glColor3f(1, 0, 0); glVertex3f (-1, 0, 0); glVertex3f (1, 0, 0);
        glColor3f(0, 1, 0); glVertex3f (0, -1, 0); glVertex3f (0, 1, 0);
        glColor3f(0, 0, 1); glVertex3f (0, 0, -1); glVertex3f (0, 0, 1);
      glEnd;
      // завершение формирования изображения
      glFlush;
      glFinish;
      SwapBuffers(DC);
    end;
    
    private void glControl1_Paint(object sender, PaintEventArgs e)
    {
    GL.ClearColor(0.5f, 0.5f, 0.75f, 1.0f); // цвет фона
    // очистка буферов цвета и глубины
    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
    // поворот изображения
    GL.LoadIdentity();
    GL.Rotate(AngleX, 1.0, 0.0, 0.0);
    GL.Rotate(AngleY, 0.0, 1.0, 0.0);
    GL.Rotate(AngleZ, 0.0, 0.0, 1.0);
    // формирование изображения
    GL.Begin(BeginMode.Lines);
    GL.Color3(1f, 0f, 0f); GL.Vertex3(-1f, 0f, 0f); GL.Vertex3(1f, 0f, 0f);
    GL.Color3(0f, 1f, 0f); GL.Vertex3(0f, -1f, 0f); GL.Vertex3(0f, 1f, 0f);
    GL.Color3(0f, 0f, 1f); GL.Vertex3(0f, 0f, -1f); GL.Vertex3(0f, 0f, 1f);
    GL.End();
    // завершение формирования изображения
    GL.Flush();
    GL.Finish();
    glControl1.SwapBuffers();
    }
    

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

    В листингах 3.4 и сначала вызывается команда ClearColor, которая определяет цвет фона изображения. В качестве параметров указываются красная (параметр red), зеленая (параметр green) и синяя (параметр blue) составляющие цвета:

    ClearColor (red, green, blue, alpha: GLclampf)
    

    Значения параметров должны находиться в интервале от 0 до 1, включая граничные значения. Значение 0 соответствует минимальной интенсивности соответствующей составляющей цвета, значение 1 – максимальной интенсивности. Если значение параметра не соответствует указанному интервалу, то оно округляется до ближайшего граничного значения (0 или 1). Четвертый параметр alpha используется для реализации различных визуальных эффектов, например, прозрачности. Если подобные эффекты не используются, то параметр alpha должен быть равен 1.

    В Object Pascal для реализации команды ClearColor используется процедура:

    procedure glClearColor (red, green, blue, alpha: GLclampf);
    

    В C# для этого используется статический метод класса GL библиотеки OpenTK:

    void ClearColor(float red, float green, float blue, float alpha);
    

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

    Clear (mask: GLbitfield)
    

    Команда Clear выполняет очистку буферов, которые определяются в качестве параметра mask.

    В Object Pascal (Delphi) выполнение команды Clear реализуется с помощью процедуры:

    procedure glClear (mask: GLbitfield);
    

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

    В листинге 3.5 в качестве параметра процедуры glClear передается сумма двух констант:

    GL_COLOR_BUFFER_BIT – буфер цвета, в котором формируется изображение;

    GL_DEPTH_BUFFER_BIT – буфер глубины, который используется для отбора примитивов в зависимости от удалённости от точки наблюдения (см. раздел 3.1).

    В C# для реализации команды Clear используется статический метод класса GL библиотеки OpenTK:

    void Clear(ClearBufferMask mask);
    

    Значение параметра mask представляет собой перечисление типа ClearBufferMask. Для очистки буферов цвета и глубины используется сумма двух значений:

  • ClearBufferMask.ColorBufferBit - буфер цвета;
  • ClearBufferMask.DepthBufferBit – буфер глубины.
  • Далее в листингах 3.4 и формируется изображение трех отрезков разного цвета, направление которых совпадает с направлением декартовых осей координат в пространстве.

    Формирование отрезков начинается с вызова команды Begin.

    В Object Pascal для реализации этой команды используется процедура

    procedure glBegin (mode: GLenum);
    

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

    В C# для реализации команды Begin используется статический метод класса GL:

    void Begin(BeginMode mode);
    

    Формируемый примитив определяется с помощью значения перечисления BeginMode. Для формирования отрезков используется значение этого перечисления Lines.

    Затем выполняется вызов команды Vertex для задания координат вершин отрезков.

    В Object Pascal для реализации команды Vertex используется несколько процедур с различными параметрами. В примере используется одна из этих процедур, которая в качестве параметров принимает три вещественных значения:

    procedure glVertex3f (x,y,z: GLfloat);
    

    В C# для этого используется один из статических методов класса GL:

    void Vertex3(float x, float y, float z);
    

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

    Формирование отрезков завершается с помощью команды End.

    В Object Pascal команда реализована с помощью процедуры:

    procedure glEnd;
    

    В C# для реализации команды End используется статический метод класса GL:

    void End();
    

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

    В листинге 3.5 для выполнения этой команды вызывается процедура:

    procedure glColor3f (red, green, blue: GLfloat);
    

    В примере программы на C# () для этого используется один из статических методов класса GL:

    void Color3(float red, float green, float blue);
    

    В качестве параметров команды Color задаются красная (параметр red), зеленая (параметр green) и синяя (параметр blue) составляющие цвета.

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

    Команды Flush и Finish используются для завершения формирования изображения. Команда Flush информирует библиотеку OpenGL о том, что все параметры изображения были переданы. Так как изображение может быть довольно сложным, то для его формирования может потребоваться некоторое время. Команда Finish приостанавливает работу программы до завершения формирования изображения библиотекой OpenGL.

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

    Обработка событий мыши и клавиатуры

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

    В ниже приведенном примере клавиши "Insert" и "Delete" изменяют угол поворота вокруг оси , клавиши "Home" и "End" – вокруг оси OY, клавиши "Page Up" и "Page Down" – вокруг оси OZ.

    Object Pascal (Delphi)

    В приложениях на Object Pascal с использованием VCL (Delphi) для этого может использоваться обработчик события формы OnKeyDown. Для определения нажатой клавиши используются коды виртуальных клавиш операционной системы. Виртуальный код нажатой клавиши передаётся в обработчик события через параметр-переменную Key. Виртуальные коды клавиш объявлены как константы в модуле Windows.pas и начинаются с символов "VK_".

    В процедуре используются глобальные переменные ).

    procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    begin
      case Key of
        VK_INSERT: inc(AngleX, AngleDl);
        VK_DELETE: dec(AngleX, AngleDl);
        VK_HOME: inc(AngleY, AngleDl);
        VK_END: dec(AngleY, AngleDl);
        VK_PRIOR: inc(AngleZ, AngleDl);
        VK_NEXT: dec(AngleZ, AngleDl);
      end;
      InvalidateRect(Handle, nil, False);
    end;
    

    В конце процедуры выполняется системная процедура InvalidateRect, которая информирует операционную систему о необходимости обновления изображения формы. Это приводит к возникновению события формы OnPaint, в обработчике которого формируется изображение, но уже с новыми значениями углов поворота изображения вокруг осей координат.

    Библиотека OpenTK

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

    private void glControl1_KeyDown(object sender, KeyEventArgs e)
    {
    switch(e.KeyCode)
    {
    case Keys.Insert: AngleX += AngleDl; break;
    case Keys.Delete: AngleX -= AngleDl; break;
    case Keys.Home: AngleY += AngleDl; break;
    case Keys.End: AngleY -= AngleDl; break;
    case Keys.Prior: AngleZ += AngleDl; break;
    case Keys.Next: AngleZ -= AngleDl; break;
    }
    glControl1.Invalidate(); 
    }
    

    Для обновления изображения в конце события вызывается метод Invalidate компонента GLControl. В результате будет выполнен делегат события Paint и обновлено содержание компонента GLControl.

    Завершение работы c библиотекой OpenGL

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

    Object Pascal (Delphi)

    В приложениях Object Pascal для завершения работы с OpenGL необходимо выполнить следующую последовательность действий:

  • Освободить текущий контекст воспроизведения с помощью функции wglMakeCurrent. Для этого ей в качестве параметров передаются нулевые значения.
  • Удалить контекст воспроизведения, дескриптор которого был сохранен в глобальной переменной hrc. Удаление контекста воспроизведения выполняется с помощью функции wglDeleteContext.
  • Удалить контекст устройства операционной системы с помощью функции ReleaseDC. В качестве параметров функции передаются дескриптор окна приложения (свойство формы Handle) и дескриптор устройства (переменная hr). Глобальная переменная hr была инициализирована при запуске программы на этапе инициализации библиотеки OpenGL.
  • Эти действия можно выполнить в обработчике события закрытия формы OnDestroy.

    Фрагмент кода, выполняющий эти действия приведен ниже ().

    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      if not wglMakeCurrent(0, 0) then
        raise Exception.Create(
          'Невозможно освободить текущий контекст воспроизведения !');
      if not wglDeleteContext(hrc) then
        raise Exception.Create('Ошибка удаления контекста воспроизведения !');
      if ReleaseDC(Form1.Handle, dc)=0 then
        raise Exception.Create('Ошибка удаления контекста устройства');
    end;
    

    Библиотека OpenTK (С#)

    При использовании библиотеки OpenTK завершение работы с библиотекой OpenGL выполняет сама библиотека OpenTK. При этом она выполняет операции, аналогичные приведённым в листинге 3.8 для приложения на Object Pascal (Delphi).

    Может показаться, что приложенные усилия по разработке приложения на Object Pascal (Delphi) не соответствуют полученному результату, но это совсем не так. Созданная программа может использоваться в качестве каркаса для создания более сложных вариантов программ.

    Краткие итоги

    В лекции рассмотрена структура приложений, использующих библиотеку OpenGL для формирования изображений, на языке программирования C# с использованием библиотеки OpenTK и на языке программирования Object Pascal. Рассмотрено назначение вторичного буфера и теста глубины. В созданных приложениях реализована обработка клавиатуры для изменения изображения.

    Библиотека OpenTK скрывает практически весь процесс инициализации библиотеки OpenGL.

    В Object Pascal весь процесс инициализации OpenGL необходимо реализовать непосредственно в программе, при этом есть возможность полного контроля данного процесса.

    Вернуться к учебному плану