Практикум прикладного программирования на C# в среде VS.NET 2005

Простая программа на OpenGL

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

Первая программа OpenGL

Файлы к лабораторной работе Вы можете скачать здесь.

Реализация библиотеки Microsoft OpenGL (Open Graphics Library) включает в себя следующие компоненты:

  • Набор базовых команд (около 300) для описания форм объектов, преобразования координат, управления освещением, цветом, текстурой, туманом, вывода растровых картинок и т.д. Имена базовых команд в реализации на C начинаются с префикса gl
  • Библиотеку утилит ( GLU - библиотеку). Команды этой библиотеки дополняют базовые функции OpenGL и позволяют выполнять триангуляцию многоугольников, создавать и выводить стандартные фигуры (сферы, цилиндры, ...), строить сплайновые кривые и поверхности, обрабатывать ошибки. Имена команд библиотеки утилит в реализации на C начинаются с префикса glu
  • Дополнительную библиотеку OpenGL ( AUX - библиотеку). Библиотека содержит функции управления окнами, обработки событий, управления цветовой палитрой, вывода стандартных 3D -объектов (тор, тетраэдр, ...), управления двойной буферизацией. Имена команд этой библиотеки в реализации на C начинаются с префикса aux
  • Функции, соединяющие OpenGL с Windows ( WGL - функции). Эти функции управляют контекстом воспроизведения, списками команд, шрифтами. Имена WGL - функций начинаются с префикса wgl
  • Win32 - функции для управления форматов пикселов и двойной буферизацией. Они заменяют библиотеку AUX.
  • Создание заготовки приложения OpenGL

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

  • Выполните команду меню оболочки File/New/Project
  • В появившемся окне мастера создания заготовки проекта задайте имя проекта Simple, выберите каталог размещения проекта и другие установки, как показано на рисунке
  • Оставьте установки мастера по умолчанию и щелкните на кнопке Finish
  • Переименуйте функцию _tmain() файла Simple.cpp в main(), уберите параметры в заголовке определения функции main() и возвращаемое значение, чтобы код выглядел так
  • // Simple.cpp : Defines the entry point for the console application.
    //
      
    #include "stdafx.h"
      
      
    int main()
    {
    }

    Теперь необходимо сообщить линковщику о необходимости использования библиотечных файлов OpenGL (хотя это и не обязательно, поскольку далее мы используем вспомогательный заголовочный файл glut.h, где все это будет прописано). Для этого:

  • Выполните команду меню оболочки Project/Simple Properties и в открывшемся диалоговом окне в поле Linker/Input/Additional Dependencies введите разделенные пробелами наименования библиотек
  • opengl32.lib glu32.lib glaux.lib

    Необходимые подключемые файлы OpenGL

  • GL.h
  • GLU.h
  • GLAux.h
  • хранятся по адресу:

    C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\Include\gl

    Необходимые статические библиотеки OpenGL

  • OpenGL32.Lib
  • GLU32.Lib
  • GLAux.Lib
  • хранятся в файловой системе компьютера по адресу:

    C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\Lib

    Необходимые динамические библиотеки OpenGL

  • OPENGL32.DLL
  • glu32.dll
  • хранятся в системном каталоге по адресу:

    C:\WINNT\system32

    Но нам понадобится дополнительно еще одна статическая библиотека glut32.lib и одна динамическая библиотека glut32.dll, а также один заголовочный файл glut.h - все они находятся в каталоге Source, прилагаемом к этой лабораторной работе!!!

  • Скопируйте из каталога Source данной лабораторной работы файл glut32.lib в каталог статических библиотек, файл glut32.dll в каталог динамических библиотек, а файл glut.h - в каталог размещения подключаемых файлов Вашего компьютера (примерные адреса размещения перечислены выше)
  • Через панель Solution Explorer оболочки откройте файл stdafx.h, который создал мастер проекта, и добавьте в самое его начало после инструкции #pragma once код подключения стандартных заголовочных файлов
  • // stdafx.h : include file for standard system include files,
    // or project specific include files that are used frequently, but
    // are changed infrequently
    //
      
    #pragma once
      
    #include <windows.h>
    #include <gl/gl.h>
    #include <gl/glu.h>
    #include <gl/glut.h>  
      
    #define WIN32_LEAN_AND_MEAN    // Exclude rarely-used stuff from Windows headers
    #include <stdio.h>
    #include <tchar.h>
      
      
      
    // TODO: reference additional headers your program requires here

    Упражнение 1. Создание простого графического окна (Simple)

  • Заполните файл реализации Simple.cpp следующим кодом
  • // Simple.cpp : Defines the entry point for the console application.
    //
      
      
    #include "stdafx.h"
      
    // Прототипы функций
    void RenderScene(void);
    void SetupRC(void);
      
    // Точка входа приложения
    void main(void)
    {
      glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
      glutCreateWindow("Simple");
      glutDisplayFunc(RenderScene);
      SetupRC();
      glutMainLoop();
    }
      
    //****************************************************
    // Функция обратного вызова для рисования сцены
    void RenderScene(void)
    {
      // Окно очищается текущим цветом,
      // установленным функцией glClearColor()
      glClear(GL_COLOR_BUFFER_BIT);
      // В буфер вводятся команды рисования
      glFlush();
    }
      
    //****************************************************
    // Устанавливается состояние инициализации
    void SetupRC(void)
    {
      glClearColor(0.0F, 0.0F, 1.0F, 1.0F);// RGB + alpha-канал
    }
  • Запустите приложение на выполнение
  • Оболочка выдаст предупреждение, что данная форма проекта является устаревшей.

  • Установите флажок Do not show this dialog again и щелкните на кнопке 19_16
  • Должен получиться такой результат

    Разбор кода Simple

    Наша простая программа содержит вызовы четырех функций библиотеки GLUT (с префиксом glut ) и три вызова "чистых" функций OpenGL (с префиксом gl ). Вспомогательная библиотека GLUT ( OpenGL Utility Toolkit - набор инструментов OpenGL ) написана Марком Килгардом (Mark Kilgard) и предназначена для межплатформенных примеров программирования и демонстраций. В нее входят графические элементы пользовательского интерфейса, включая использование меню, управление другими окнами, обеспечение поддержки джойстика и др. Ее использование как расширение OpenGL содействует обучению и такому написанию программ OpenGL, чтобы программист не занимался деталями конкретной среды, а сосредоточился на написании основного кода графического приложения. Библиотеку GLUT можно бесплатно найти в Internete.

    Сама наша программа делает немного. Она создает стандартное окно графического интерфейса пользователя ( GUI ) с надписью Simple и синим фоном. В файл stdafx.h помещены подключения заголовков прототипов функций, используемых в программе.

    Строка

    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);

    устанавливает режим отображения графического окна при его создании. Соединение макросов в параметрах вызова функции задает окно с одиночной буферизацией ( GLUT_SINGLE ) и режим цвета RGB с альфа-каналом ( GLUT_RGBA или GLUT_RGB ). Альтернативным режимом задания цветов является режим использования индексированных цветов ( GLUT_INDEX ). Одиночная буферизация означает, что все команды рисования выполняются прямо в отображаемом окне (на контексте, холсте). Существует еще двойная буферизация (задается параметром GLUT_DOUBLE ), когда команды рисования выполняются в буфере вне экрана, а затем быстро отображаются в окне.

    Строка

    glutCreateWindow("Simple");

    создает графическое окно с надписью в строке заголовка из параметра вызова.

    Строка

    glutDisplayFunc(RenderScene);

    устанавливает нашу функцию RenderScene как функцию обратного вызова (call back), в которой можно размещать вызовы функций визуализации OpenGL.

    Строка

    SetupRC();

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

    Строка

    glutMainLoop();

    вводит приложение в цикл сообщений. Она должна быть вызвана только один раз и работает до завершения приложения. Она обрабатывает обрабатывает все сообщения, связанные с операционной системой (управление курсором, нажатие клавиш и т.д.).

    Строка

    glClearColor(0.0F, 0.0F, 1.0F, 1.0F);// RGB + alpha-канал

    устанавливает цвет модели RGB, используемый для очистки окна. Получая код цвета, Windows и OpenGL преобразуют его в ближайшую точную величину, поддерживаемую конкретной видеоаппаратурой. Последний аргумент в вызове этой функции определяет величину альфа-канала - уровень прозрачности (1 - нeпрозрачный, 0 - абсолютно прозрачный).

    Строка

    glClear(GL_COLOR_BUFFER_BIT);

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

    Обычно решение о подвижке очереди команд принимает сама операционная система. Но строка

    glFlush();

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

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

    Упражнение 2. Рисование прямоугольника внутри окна (GLRect)

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

  • В панели Solution Explorer щелкните правой кнопкой мыши на узле проекта и через контекстное меню выполните команду Add/New Item
  • В открывшемся окне задайте имя файла GLRect, а тип и размещение выберите в соответствии со снимком
  • Нажмите кнопку Add и к проекту добавится новый пустой файл C++
  • Исключите из проекта прежний файл Simple.cpp. Для этого в панели Solution Explorer щелкните правой кнопкой мыши на этом файле и через контекстное меню выполните команду Remove
  • Файл можно исключить из проекта и клавишей Del. В этом случае оболочка выдаст диалоговое окно

    Кнопка Delete удалит файл физически, а кнопка Remove только исключит файл из проекта. Физически на диске он останется, но в проекте участвовать не будет. В последующем его всегда можно будет вновь добавить к проекту.

  • Щелкните на пиктограмме Show All Files панели Solution Explorer и убедитесь, что файл Simple.cpp действительно существует
  • Заполните файл GLRect.cpp следующим кодом
  • #include "stdafx.h"
      
    // Прототипы функций
    void RenderScene(void);
    void SetupRC(void);
    void ChangeSize(int, int);
      
    //**********************************************************
    // Точка входа приложения
    void main()
    {
      glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
      glutInitWindowSize(800, 600);
      glutCreateWindow("GLRect");
      glutDisplayFunc(RenderScene);
      glutReshapeFunc(ChangeSize);
      SetupRC();
      glutMainLoop();
    }
      
    //**********************************************************
    // Вызывается для рисования сцены
    void RenderScene(void)
    {
      // Очищаем окно текущим цветом очистки
      glClear(GL_COLOR_BUFFER_BIT);
      
      // Устанавливаем текущим цветом рисования красный
      // в модели     R   G     B
      glColor3f(1.0f, 0.0f, 0.0f);
      
      // Рисуем прямоугольник, заполненный текущим цветом
      glRectf(-50.0f, 50.0f, 50.0f, -50.0f);
      
      // Выполняет всю очередь текущих команд
      glFlush();
    }
      
    //**********************************************************
    // Устанавливаем состояние визуализации
    void SetupRC(void)
    {
        // Устанавливаем цвет очистки синий непрозрачный
        glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
    }
      
    //**********************************************************
    // Вызывается библиотекой GLUT при изменении размеров окна
    void ChangeSize(int width, int height)
    {
      GLfloat aspectRatio;
      
      // Предотвращаем деление на нуль
      if(height == 0)
        height = 1;
      
      // Устанавливаем поле просмотра с размерами окна
        glViewport(0, 0, width, height);
      
      // Устанавливает матрицу преобразования в режим проецирования
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      
      // Устанавливаем размеры перспективы (отсекающего объема)
      // (left, right, bottom, top, near, far)
      aspectRatio = (GLfloat)width / (GLfloat)height;
        if (width <= height)
        glOrtho (-100.0, 100.0, -100 / aspectRatio,
            100.0 / aspectRatio, 
            1.0, -1.0);
        else 
        glOrtho (-100.0 * aspectRatio, 
            100.0 * aspectRatio, 
            -100.0, 100.0, 
            1.0, -1.0);
      
      // Восстановливает матрицу преобразования в исходный режим вида
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
    }
  • Запустите приложение и получите результат
  • Пояснения кода GLRect

    При программировании с использованием библиотеки OpenGL часто более удобно пользоваться псевдонимами встроенных типов языка C. Эти типы определены в заголовочном файле GL.h

    typedef unsigned int GLenum;
    typedef unsigned char GLboolean;
    typedef unsigned int GLbitfield;
    typedef signed char GLbyte;
    typedef short GLshort;
    typedef int GLint;
    typedef int GLsizei;
    typedef unsigned char GLubyte;
    typedef unsigned short GLushort;
    typedef unsigned int GLuint;
    typedef float GLfloat;
    typedef float GLclampf;
    typedef double GLdouble;
    typedef double GLclampd;
    typedef void GLvoid;

    Вызов

    glutInitWindowSize(800, 600);

    устанавливает начальные размеры графического окна.

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

    Масштабирование рисунка под размеры окна

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

    Функция обратного вызова

    void ChangeSize(int width, int height)

    вызывается всякий раз при изменении размеров окна и принимает новые параметры ширины и высоты клиентской области окна.

    Код

    // Предотвращаем деление на нуль
      if(height == 0)
        height = 1;

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

    Внутри клиентской области функция

    void glViewport(GLint x, GLint y, GLsizei width, GLsizei height)

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

    В нашем обращении

    // Устанавливаем поле просмотра с размерами окна
        glViewport(0, 0, width, height);

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

    void ChangeSize(int width, int height)

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

    Описание трехмерного объекта представляется в трехмерной системе координат объекта. Наиболее простая - ортогональная система координат. В OpenGL базовой принята правая система координат (большой палец правой руки направлен по X, указательный по Y, средний по Z ).

    Кодом

    // Рисуем прямоугольник, заполненный текущим цветом
      glRectf(-50.0f, 50.0f, 50.0f, -50.0f);

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

    Синтаксис функции glRect() - нарисовать плоский прямоугольник
    Цель: Нарисовать плоский прямоугольник
    Варианты:
  • void glRectd(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2) ;
  • void glRectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2) ;
  • .............................................
  • Описание: Приведенная функция предлагает простой метод задания прямоугольника через координаты противоположных вершин. Прямоугольник задается в плоскости x0y при z = 0 системы координат объекта.
    Параметры:
  • x1, y1 - задает левый верхний угол прямоугольника
  • x2, y2 - задает правый нижний угол прямоугольника
  • Если не устанавливать отсекающий объем ( перспективу ) при ортогональном проецировании, то в поле просмотра будут помещены (предварительно преобразованные в растр) все точки прямоугольника. При этом размеры прямоугольника, установленные функцией glRectf() не будут иметь никакого значения - прямоугольник растянется по всему полю просмотра.

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

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

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

    aspectRatio = (GLfloat)width / (GLfloat)height;
        if (width <= height)
        glOrtho (-100.0, 100.0, -100 / aspectRatio,
            100.0 / aspectRatio, 
            1.0, -1.0);
        else 
        glOrtho (-100.0 * aspectRatio, 
            100.0 * aspectRatio, 
            -100.0, 100.0, 
            1.0, -1.0);

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

    Приведенный код определения отсекающего объема (перспективы) окаймляется следующими вызовами

    // Устанавливает матрицу преобразования в режим проецирования
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      
      .......................................................
          
      // Восстановливает матрицу преобразования в исходный режим вида
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();

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

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

    Упражнение 3. Анимация прямоугольника с помощью OpenGL и GLUT (Bounce)

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

    Добавьте к проекту новый файл. Для этого:

  • В панели Solution Explorer щелкните правой кнопкой мыши на узле проекта и через контекстное меню выполните команду Add/New Item
  • В открывшемся окне задайте имя файла Bounce, а тип и размещение выберите в соответствии со снимком
  • Нажмите кнопку Add и к проекту добавится новый пустой файл C++
  • Удалите из проекта прежний файл GLRect.cpp. Для этого в панели Solution Explorer щелкните правой кнопкой мыши на этом файле и через контекстное меню выполните команду Remove
  • Заполните файл Bounce.cpp следующим кодом
  • #include "stdafx.h"
      
    // Прототипы глобальных функций
      void RenderScene(void);
      void SetupRC(void);
      void ChangeSize(int, int);
      void TimerFunction(int);
      
    // Глобальные переменные:
      
      // Начальная позиция и размер прямоугольника (будет квадрат)
      GLfloat x = 0.0f;
      GLfloat y = 0.0f;
      GLfloat rsize = 25;
      
      // Величина шага в направлениях x и y (в пикселах)
      GLfloat xstep = 3.0f;
      GLfloat ystep = 3.0f;
      
      // Текущие ширина и высота клиентской области окна приложения
      GLfloat windowWidth;
      GLfloat windowHeight;
      
    //**********************************************************
    // Точка входа приложения
    void main(int argc, char* argv[])
    {
      glutInit(argc, argv);
      glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
      glutInitWindowSize(600,300);
      glutCreateWindow("Bounce");
      glutDisplayFunc(RenderScene);
      glutReshapeFunc(ChangeSize);
      glutTimerFunc(33, TimerFunction, 1);
      
      SetupRC();
      
      glutMainLoop();
    }
      
    //**********************************************************
    // Рисование сцены
    void RenderScene(void)
    {
      // Очистка окна текущим цветом
      glClear(GL_COLOR_BUFFER_BIT);
      
      // Установка цвета рисования в модели RGB (красный)
      glColor3f(1.0f, 0.0f, 0.0f);
      
      // Рисование и заливка красным
      glRectf(x, y, x + rsize, y - rsize);
      
      // Переключение буфера с очисткой очереди команд
      glutSwapBuffers();
    }
      
    //**********************************************************
    // Вызывается по таймеру библиотекой GLUT в спокойном состоянии,
    // когда не меняются размеры окна приложения и нет перемещений
    void TimerFunction(int value)
    {
      // Меняем направление перемещения на противоположное
      // при подходе к левому или правому краю окна
      if(x > windowWidth - rsize || x < -windowWidth)
        xstep = -xstep;
      
      // Меняем направление перемещения на противоположное
      // при подходе к верхнему или нижнему краю окна
      if(y > windowHeight || y < -windowHeight + rsize)
        ystep = -ystep;
      
      // Перемещаем прямоугольник в новую позицию
      x += xstep;
      y += ystep;
      
      // Проверка на случай, если прямоугольник окажется больше окна  
      // или за пределами  объема отсекающего параллелепипеда
      if(x > (windowWidth - rsize + xstep))
        x = windowWidth - rsize - 1;
      else if(x < -(windowWidth + xstep))
        x = -windowWidth - 1;
      
      if(y > (windowHeight + ystep))
        y = windowHeight - 1; 
      else if(y < -(windowHeight - rsize + ystep))
        y = -windowHeight + rsize - 1;
      
      // Перерисовываем сцену с новыми координатами
      glutPostRedisplay();
      // Запускаем новый таймер
      glutTimerFunc(33,TimerFunction, 1);
    }
      
    //**********************************************************
    // Установка состояния рендеринга (визуализации)
    void SetupRC(void)
    {
      // Устанавливаем синий цвет очистки 
      glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
    }
      
    //**********************************************************
    // Вызывается библиотекой GLUT при изменении размеров окна
    // приложения. Осталась такой же, как в примере GLRect,
    // только в вызовы glOrtho() поставили переменные
    void ChangeSize(int width, int height)
    {
      GLfloat aspectRatio;
      
      // Предотвращаем деление на нуль
      if(height == 0)
        height = 1;
      
      // Устанавливаем поле просмотра с размерами окна
      glViewport(0, 0, width, height);
      
      // Устанавливает матрицу преобразования в режим проецирования
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      
      // Устанавливаем размеры перспективы (отсекающего объема)
      // (left, right, bottom, top, near, far)
      aspectRatio = (GLfloat)width / (GLfloat)height;
      if (width <= height) 
      {
        windowWidth = 100;
        windowHeight = 100 / aspectRatio;
        glOrtho (-100.0, 100.0, 
            -windowHeight, windowHeight, 1.0, -1.0);
      }
      else 
      {
        windowWidth = 100 * aspectRatio;
        windowHeight = 100;
        glOrtho (-windowWidth, windowWidth, 
            -100.0, 100.0, 1.0, -1.0);
      }
      
      // Восстановливает матрицу преобразования в исходный режим вида
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
    }
  • Запустите приложение и убедитесь, что результат совпадает с задуманным
  • Пояснения кода Bounce

    Библиотечная функция таймера

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

    glutTimerFunc(33, TimerFunction, 1);

    Первый раз этот вызов осуществляется из функции main(), а затем повторяется в сервисной функции обратного вызова TimerFunction().

    Синтаксис вызова этой GLUT -библиотечной функции следующий:

    glutTimerFunc(unsigned int msecs, void (*func)(int value), int value);

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

    Двойная буферизация

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

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

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

    Механизм двойной буферизации при формировании изображений мы устанавливаем в функции main() вызовом

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);

    с флагом GLUT_DOUBLE вместо GLUT_SINGLE, как это было ранее. В таком режиме функция рисования сцены

    void RenderScene(void){...}

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

    // Переключение буфера с очисткой очереди командglutSwapBuffers();

    сделает этот буфер рабочим - и это новенькое наконец-то и увидит пользователь.

    Заметьте, что теперь функцию glFlush() мы уже не вызываем. Она уже не нужна, поскольку, выполняя переключение буферов, мы неявно вызываем функцию очистки очереди команд.

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

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

  • Внесите в текст программы Bounce следующие изменения
  • //**********************************************************
    // Точка входа приложения
    void main(int argc, char* argv[])
    {
    ...............................................
      // glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
      glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    ...............................................
    }
      
    //**********************************************************
    // Рисование сцены
    void RenderScene(void)
    {
    ...............................................
      // Переключение буфера с очисткой очереди команд
      // glutSwapBuffers();
      glFlush();}

    Остальные пояснения к коду Bounce приведены в комментариях листинга программы.

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

    Работу преподавателю сдавайте так: поочередно присоединяйте к проекту файлы

  • Simple.cpp
  • GLRect.cpp
  • Bounce.cpp
  • или сразу заготовьте три варианта исполнимых файлов, но будьте готовы к выполнению просьбы по внесению определенных изменений.

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