Загрузка...
 
Печать

OpenGL - Библиотека GLUT


Продолжение. Начало здесь: OpenGL Уч.курс

В "голом" OpenGL отсутствуют средства для организации интерфейса пользователя: никаких окошек, курсоров мыши, реакции на нажатия клавиш и т.д. Причины этого кроются в кросплатформенности OpenGL. На все операционные системы инетрфейсов не напишешься: этот процесс сильно различается на разных ОС. Но специальная надстройка GLUT (OpenGL Utility Toolkit), поставяемая вместе с NVIDIA SDK, всегда придёт на помощь. Она позволяет:1
  • Создавать многооконные приложения
  • Обрабатывать сообщения, используя процедуры обратного вызова (в ОС Windows - т.н. Callback function)
  • Работать с устройствами ввода информации (мышь, клавиатура и др.)
  • Использовать таймеры
  • Создавать всплывающие (pop-up) меню
  • Работать со шрифтами и изображениями
и многое другое.
Программа, использующая GLUT версии 3.7 может работать в MS Windows 95/NT и выше, UNIX, OS/2 и других ОС. Если приложение использует только возможности GLUT, то для её переноса в любую другую ОС достаточно лишь перекомпилировать исходный код без единого изменения. Достаточно лишь использовать makefile от нужного компилятора.
Само собой из-за подобной универсальности страдает функциональность и GLUT-приложение не может в полной мере использовать все возможности своей ОС. Но в целом GLUT является оптимальным выбором для написания небольших программ.
Кроме того практически все примеры NVIDIA SDK 5 написаны с применением GLUT. Поэтому её изучение будет полезно любому игрокодеру.

Содержание



Простейшая GLUT-программа под ОС MS Windows


Настраиваем MS Visual C++ 2010 для работы с GLUT

Создадим пустой Проект Win32.
Весь процесс подробно расписан здесь: Создание приложений (Cpp, Win32)

  • Стартуй MS Visual C++ 2010 (подойдут и другие версии этой IDE) и cоздай новый проект, выбрав в Главном меню IDE Файл->Создать->Проект.
О том, где взять и как установить MS Visual C++ 2010 читаем здесь.
  • В появившемся окне "Создать проект" выбираем: "Проект Win32", в строке "Имя" пишем название проекта (в нашем случае GLUTTest01). Строки "Расположение" и "Имя Решения" заполняются автоматически (при необходимости изменяем). Жмём "OK", "Далее".
Image
  • На странице мастера "Параметры приложения": оставляем "Приложение Windows" и отмечаем пункт "Пустой проект". Жмём "Готово".
Image
Проект создан. Так как это "Пустой проект", он не содержит в себе никаких файлов. В левой части главного окна MS Visual C++ 2010 расположен "Обозреватель решений". (Если его нет, в главном меню выбираем: Вид->Другие окна->Обозреватель решений. Или комбинация горячих клавиш Ctrl+Alt+L.) В Обозревателе решений видна древовидная структура Проектов, входящих в данное Решение. Чуть ниже названия Проекта видим специально заготовленные папки (в MSVC++2010 они называются "фильтры").
  • В фильтр "Файлы исходного кода" добавляем файл Main.cpp.
Добавленный файл Main.cpp сразу же открывается в редакторе кода.

Изменяем набор символов по умолчанию

Закрыть
noteПримечание

В MS Visual C++ 2010 в настройках по умолчанию стоит набор (кодировка) символов UNICODE. В MS Visual C++ 6.0 - напротив, по умолчанию стоит кодировка ANSI (многобайтовая). Данная настройка сильно влияет на типы используемых переменных, что приводит к заметным различиям в исходном коде.
Несмотря на то, что во всех случаях рекомендуется использовать кодировку UNICODE, поддерживаемую во всех современных ОС семейства MS Windows (начиная с Win 2000/XP), большинство книг по программированию игр на классическом C++ придерживаются именно многобайтовой кодировки. Чтобы сильно не переделывать исходные коды под UNICODE, все наши игровые Проекты мы настроим под многобайтовую кодировку. Для этого...

  • В Обозревателе решений щёлкаем по названию только что созданного Проекта GLUTTest01.
  • Во всплывающем меню выбираем "Свойства"
  • В появившемся окне установки свойств Проекта жмём "Свойства конфигурации", в правой части в строке "Набор символов" выставляем значение "Использовать многобайтовую кодировку".
  • Жмём ОК.
Image

Добавляем glut32.dll в C:\Windows\System32

Данный файл расположен в папке с установленным NVIDIA SDK 5. В нашем случае путь: C:\NVSDK\OpenGL\dll\Debug\
О том, где взять и как установить NVIDIA SDK 5 читай здесь: OpenGL Уч.курс
  • Скопируй расположенный по данному пути файл glut32.dll в подкаталог System32 каталога с установленной ОС Windows (по умолчанию C:\Windows\System32).
Данный файл необходимо размещать в System32 на всех компьютерах с устанавливаемым GLUT-приложением. Иначе без него оно работать не будет. Обычно соответствующую директиву прописывают в скрипте инсталлятора.

Прописываем пути к GLUT

Прежде чем начать компилировать исходные коды, немного настроим MS Visual C++ 2010, указав пути к подсоединяемым библиотекам (.lib) и заголовочным файлам (.h) OpenGL. Для других версий MS Visual C++ процедура ничем не отличается, кроме расположения соответствующих меню и их внешнего вида. Начиная с версии 8.0 (MS Visual Studio 2005), при создании нового Проекта IDE автоматически создаёт для него Решение.
"Решением" (от англ. solution) в принятой у Microsoft терминологии называется группа Проектов, объединённых общей темой. То есть, в одно Решение может входить несколько Проектов. В нашем случае это неважно, и в нашем Решении будет один Проект (пока один). Обрати внимание, что по умолчанию все Решения и Проекты сохраняются в каталог c:\users\<имя пользователя>\documents\visual studio 2010\Projects (при желании можно изменить).
  • В Обозревателе решений щёлкаем по названию только что созданного Проекта GLUTTest01.
  • Во всплывающем меню выбираем "Свойства".
Image
  • В появившемся окне установки свойств Проекта раскрываем древовидную структуру раздела "Свойства конфигурации", щёлкнув по чёрному треугольнику слева от надписи.
  • В раскрышемся списке выбери Каталоги VC++. В правой части этой страницы расположены пути ко всевозможным каталогам. Здесь нас интересуют только 2 строки: Каталоги включения и Каталоги библиотек.
  • Щёлкаем левой кнопкой мыши по пункту Каталоги включения. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."
Image
  • В появившемся меню "Каталоги включения" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к заголовочным (include) файлам OpenGL. В нашем случае это C:\NVSDK\OpenGL\include\. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода. Жмём "ОК".
Image
В папке Include содержится множество подкаталогов. Но пути до конкретных заголовков мы пропишем в исходном коде, отделяя каждую папку слэшем (читай ниже).
  • Щёлкаем левой кнопкой мыши по пункту Каталоги библиотек. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."
  • В появившемся меню "Каталоги библиотек" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к 32-разрядным версиям файлов библиотек (lib) DirectX SDK. В нашем случае это C:\NVSDK\OpenGL\lib\Debug\. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода. Жмём "ОК".
Image
  • На Странице свойств тоже жмём "ОК". Готово.

Прописываем в компоновщике директиву /ENTRY:"mainCRTStartup"

Она является обязательной при линковке любой OpenGL-программы.
  • В Обозревателе решений щёлкаем по названию только что созданного Проекта GLUTTest01.
  • Во всплывающем меню выбираем "Свойства".
Image
  • В появившемся окне "Страницы свойств" раскрываем древовидную структуру раздела "Свойства конфигурации", щёлкнув по чёрному треугольнику слева от надписи.
  • Чуть ниже таким же образом раскрываем подраздел "Компоновщик" и выбираем пункт "Командная строка".
  • В правой части раздела "Командная строка" ветки "Компоновщик" (=Linker) в строке "Дополнительные параметры" прописываем строку:
/ENTRY:"mainCRTStartup"

Image

Прописываем в компоновщике директиву /FORCE:MULTIPLE

Во многих исходниках NVIDIA SDK объявления и реализации одних и тех же функций часто расположены в одном файле. Это сильно упрощает написание простых демонстрационных программ. Но при использовании GLH в многомодульном проекте при его компиляции могут возникнуть многочисленные сообщения об ошибках из-за того, что в проекте окажется множество реализаций одной и той же функции. Компоновщик просто не сможет выбрать нужную.
Для исправления данного "недуга" указываем в настройках компоновщика строку /FORCE:MULTIPLE, заставляющую компоновщик принудительно использовать в программе первую попавшуюся реализацию функции и игнорировать остальные.
  • ТАМ ЖЕ (В окне "Страницы свойств", в правой части раздела "Командная строка" ветки "Компоновщик" (=Linker) в строке "Дополнительные параметры") через пробел прописываем строку:
/FORCE:MULTIPLE

  • На Странице свойств тоже жмём "ОК". Готово.
Всё, теперь можно смело кодить под OpenGL!

Пишем код простейшего GLUT-приложения

  • В файл исходного кода Main.cpp добавь следующий код:
Main.cpp
#include <glh\GL\glut.h>

// Вывод изображения на экран
void Display()
{
	glClear(GL_COLOR_BUFFER_BIT);
	// Смена переднего и заднего буферов экрана
	glutSwapBuffers();
}

int main(int argc, char* argv[])
{
	glutInit(&argc, argv);
	// Задаём параметры окна
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
	// Задаём размеры окна
	glutInitWindowSize(640, 480);
	// Задаём позицию окна
	glutInitWindowPosition(100, 100);
	// Создём окно
	glutCreateWindow("My first OpenGL Application");

	glClearColor(0.5, 0.5, 0.75, 1);

	// Задаём функцию обратного вызова изображения на экран
	glutDisplayFunc(Display);
	glutMainLoop();

	return 0;
}

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

  • Сохрани Решение (Файл -> Сохранить все).

Компилируем Проект GLUTTest01

  • Скомпилируй Проект/Решение (кнопка с зелёным треугольником на Панели инструментов MSVC++ или <F5> на клавиатуре).
Image

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

Исследуем код Main.cpp

Функция
glutInit(&argc, argv);
инициализирует библиотеку GLUT. Вот её параметры:
  • argcp - указатель на кол-во аргументов в командной строке;
  • argvv - указатель на массив аргументов.
Обычно эти значения берутся из главной функции программы - main. В случае когда программа создаётся исключительно под Win32 оба этих параметра можно оставить по умолчанию.

Функция
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
устанавливает формат пикселов окна программы.
Её прототип выглядит так:
void glutInitDisplayMode(unsigned int mode);

Единственный параметр mode представляет собой битовую маску, каждый бит которой соответствует определённому атрибуту формата пикселов. Обычно здесь указываются предопределённые константы, объединяемые оператором логического ИЛИ (|). Вот список возможных констант битовых масок:
Константа Описание
GLUT_RGBA Необходим RGBA-видеорежим.
GLUT_RGB Необходим RGB-видеорежим без альфа-канала (канала прозрачности).
GLUT_INDEX Индексированные цвета. Они же палитры (palette). Целочисленные идентификаторы цветов присутствуют в виде индексов в ограниченной цветовой палитре.
GLUT_SINGLE Включает поддержку одиночной буферизации.
GLUT_DOUBLE Включает поддержку двойной буферизации.
GLUT_SINGLE Включает буфер т.н. "аккумулятора".
GLUT_ALPHA Включает поддержку альфа-канала.
GLUT_DEPTH Включает поддержку буфера глубины.
GLUT_STENCIL Включает поддержку буфера шаблонов.

В нашем случае выбран режим RGBA и включена прддержка двойной буферизации.

Далее устанавливаем размеры окна (640х480 точек) и его положение на экране (100 точек по вертикали и 100 по горизонтали от верхнего левого угла экрана).

Функция
glutCreateWindow(char *name);
создаёт окно. В её единственном параметре указывается текст заголовка окна.

Библиотека GLUT является событийно-ориентированной. Любая GLUT-программа общается с внешним миром посредством механизма функций обратного вызова (callback function). Сначала программа передаёт библиотеке GLUT адрес функции обратного вызова, обрабатывающей данное событие, а затем, при наступлении этого события, библиотека (glut32.dll) вызывает указанную функцию обратного вызова, передавая ей при необходимости его (события) параметры.
Функция обратного вызова
void glutDisplayFunc(void (*func) (void));
отвечает за перерисовку экрана. Её единственный параметр func является указателем на функцию обратного вызова, отвечающую за обновление содержимого экрана. В Win32 по факту эта функция является обработчиком события WM_PAINT. В нашем случае в качестве функции обратного вызова стоит Display(), которая очищает экран и выводит полученное изображение, используя функцию glutSwapBuffers(). При использовании флага одиночной буферизации (GLUT_SINGLE) команду glutSwapBuffers() заменяют на glFlush().

Функция
glutMainLoop();
запускает цикл обработки сообщений.
Строка
return 0;
особого функционала не несёт и является, скорее, данью традиции. Ведь функция glutMainLoop() никогда не возвращает управление программе.

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

Работа с мышью и клавиатурой


Организуем закрытие окна по нажатию Esc

Любая хорошая Windows-программа должна уметь обрабатывать нажатие клавиш на клавиатуре. Организуем закрытие окна программы (= выход из программы) по нажатию клавиши Esc на клавиатуре.
Для регистрации обработчика сообщений от клавиатуры в GLUT используется команда glutKeyboardFunc, прототип которой выглядит так:
void glutKeyboardFunc(void (*func) (unsigned char key, int x, int y));

Вот описание её параметров:
  • key - идентификатор нажатой клавиши;
  • x, y - координаты курсора (указателя) мыши в момент нажатия.

Изменения в Main.cpp

  • В самом начале, в блоке подключаемых заголовков, добавь строку:
#include <windows.h>

Данный заголовок понадобится для указания скан-кода нажатой клавиши (VK_ESCAPE) вместо её шестнадцатиричного представления.

  • ДО начала функции main() добавь реализацию функции Keyboard():
void Keyboard(unsigned char key, int x, int y)
{
	switch(key)
	{
		case VK_ESCAPE:
			{
				exit(0);
				break;
			}
	}
}


  • В Main.cpp найди следующий фрагмент:
Фрагмент Main.cpp
...

	// Задаём функцию обратного вызова изображения на экран
	glutDisplayFunc(Display);
	glutMainLoop();

	return 0;
}

  • Перед строкой glutMainLoop(); добавь команду вызова функции Keyboard(), написанной выше:
glutKeyboardFunc(Keyboard);


После всех внесённых изменений исходный код Main.cpp выглядит так:
Main.cpp
#include <windows.h>
#include <glh\GL\glut.h>

// Вывод изображения на экран
void Display()
{
	glClear(GL_COLOR_BUFFER_BIT);
	// Смена переднего и заднего буферов экрана
	glutSwapBuffers();
}

void Keyboard(unsigned char key, int x, int y)
{
	switch(key)
	{
		case VK_ESCAPE:
			{
				exit(0);
				break;
			}
	}
}

int main(int argc, char* argv[])
{
	glutInit(&argc, argv);
	// Задаём параметры окна
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
	// Задаём размеры окна
	glutInitWindowSize(640, 480);
	// Задаём позицию окна
	glutInitWindowPosition(100, 100);
	// Создём окно
	glutCreateWindow("My first OpenGL Application");

	glClearColor(0.5, 0.5, 0.75, 1);

	// Задаём функцию обратного вызова изображения на экран
	glutDisplayFunc(Display);
	glutKeyboardFunc(Keyboard);  // Обработчик нажатий клавиш на клавиатуре
	glutMainLoop();

	return 0;
}


  • Скомпилируй Проект/Решение (кнопка с зелёным треугольником на Панели инструментов MSVC++ или <F5> на клавиатуре).
Image

В случае успешной компиляции на экране появится всё то же окно. При нажатии Esc на клавиатуре окно закрывается и программа завершает свою работу.
Как видим, дополнить GLUT-приложение необходимым функционалом совсем нетрудно. Главное - понять что и куда добавлять.

Выводим в заголовке окна текущие координаты курсора мыши

Для регистрации обработчика перемещения указателя мыши используются две команды:
  • glutMotionFunc - для обработки сообщений перемещения грызуна при нажатой кнопке мыши;
  • glutPassiveMotionFunc - для обработки сообщений перемещения грызуна при отжатой кнопке мыши;
Вот их прототипы:
void glutMotionFunc(void (*func) (int x, int y));
void glutPassiveMotionFunc(void (*func) (int x, int y));

В качестве параметров оба этих обработчика принимают координаты (x, y) указателя мыши.

Изменения в Main.cpp

  • В самом начале, в блоке подключаемых заголовков, после строки #include <gl\glut.h> добавь строку:
#include <stdio.h>

Нужен для получения/представления строковых переменных с помощью функции sprintf().
  • ДО начала функции main() добавь реализацию функции Keyboard():
void MousePassiveMotion(int x, int y)
{
	char buf[80];
	sprintf(buf,"Mouse coords is: x=%d; y=%d", x, y);
	glutSetWindowTitle(buf);
}

Для установки заголовка окна используется функция glutSetWindowTitle:
void glutSetWindowTitle(char *name);

В единственном параметре name передаётся обновлённый заголовок окна.
  • В Main.cpp найди следующий фрагмент:
Фрагмент Main.cpp
...

	// Задаём функцию обратного вызова изображения на экран
	glutDisplayFunc(Display);
	glutKeyboardFunc(Keyboard);  // Обработчик нажатий клавиш на клавиатуре
	glutMainLoop();

	return 0;
}

  • Перед строкой glutMainLoop(); добавь команду вызова функции MousePassiveMotion(), написанной выше:
glutPassiveMotionFunc(MousePassiveMotion);


После всех внесённых изменений исходный код Main.cpp выглядит так:
Main.cpp
#include <windows.h>
#include <glh\GL\glut.h>
#include <stdio.h>

// Вывод изображения на экран
void Display()
{
	glClear(GL_COLOR_BUFFER_BIT);
	// Смена переднего и заднего буферов экрана
	glutSwapBuffers();
}

void Keyboard(unsigned char key, int x, int y)
{
	switch(key)
	{
		case VK_ESCAPE:
			{
				exit(0);
				break;
			}
	}
}

void MousePassiveMotion(int x, int y)
{
	char buf[80];
	sprintf(buf,"Mouse coords is: x=%d; y=%d", x, y);
	glutSetWindowTitle(buf);
}

int main(int argc, char* argv[])
{
	glutInit(&argc, argv);
	// Задаём параметры окна
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
	// Задаём размеры окна
	glutInitWindowSize(640, 480);
	// Задаём позицию окна
	glutInitWindowPosition(100, 100);
	// Создём окно
	glutCreateWindow("My first OpenGL Application");

	glClearColor(0.5, 0.5, 0.75, 1);

	// Задаём функцию обратного вызова изображения на экран
	glutDisplayFunc(Display);
	glutKeyboardFunc(Keyboard); // Обработчик нажатий клавиш на клавиатуре
	glutPassiveMotionFunc(MousePassiveMotion); // Обработчик перемещений грызуна при отжатой кнопке мыши
	glutMainLoop();

	return 0;
}


  • Сохрани Решение (Файл -> Сохранить все).
  • Скомпилируй Проект/Решение (F5).
В случае успешной компиляции на экране появится всё то же окно. По нажатию Esc оно закрывается. При наведении на него курсора мыши заголовок окна меняется, отображая текущие координаты (x, y) курсора мыши. Причём при зажатой кнопке мыши координаты в заголовке не обновляются. Чтобы они обновлялись в том числе при нажатой кнопке мыши...
  • Перед строкой glutMainLoop(); добавь вторую команду вызова функции MousePassiveMotion(), написанной выше:
glutMotionFunc(MousePassiveMotion);

Здесь мы прописываем её при регистрации обработчика перемещений грызуна при зажатой кнопке мыши glutMotionFunc.
  • Добавь в код соответствующий комментарий.
Вот его прототип:
void glutMouseFunc(void (*func) (int button, int state, int x, int y));

Здесь:
  • button - идентификатор кнопки мыши (GLUT_LEFT_BUTTON для левой кнопки, GLUT_MIDDLE_BUTTON для средней, GLUT_RIGHT_BUTTON для правой кнопки мыши);
  • state - состояние кнопки мыши (GLUT_DOWN - нажата, GLUT_UP - отжата);
  • x, y - координаты указателя мыши.

  • Сохрани Решение (Файл -> Сохранить все).
  • Скомпилируй Проект/Решение (F5).

Генерация 3D-примитивов

В библиотеке GLUT есть набор функций для рисования наиболее распространённых объектов (примитивов):
  • сфера
  • куб
  • тор
  • додекаэдр
  • октаэдр
  • тетраэдр
  • икосаэдр
  • чайник и др.
Все команды рисования представлены в двух версиях:
  • solid - для рисования закрашенных объектов
  • wire - для рисования каркасных объектов.
Вот краткий список команд рисования:
Команды рисования сферы:
void glutSolidSphere(GLdouble radius, GLint slices, GLint stacks);
void glutWireSphere(GLdouble radius, GLint slices, GLint stacks);

где:
  • radius - радиус сферы
  • slices - количество делений вокруг оси z (аналогично географическим параллелям)
  • stacks - кол-во делений вдоль оси z (аналогично географическим меридианам)
Команды рисования куба:
void glutSolidCube(GLdouble size);
void glutWireCube(GLdouble size);

где:
  • size - размер ребра куба
Команды рисования конуса:
v
oid glutSolidCone(GLdouble base, GLdouble height, GLint slices, GLint stacks);
void glutWireCone(GLdouble base, GLdouble height, GLint slices, GLint stacks);

где:
  • base - радиус основания конуса
  • height - высота конуса
  • slices - кол-во делений вокруг оси z (аналогично географическим параллелям)
  • stacks - кол-во делений вдоль оси z (аналогично географическим меридианам)
Команды рисования тора:
void glutSoildTorus(GLdouble innerRadius, GLdouble outerRadius, GLint nsides, GLint rings);
void glutWireTorus(GLdouble innerRadius, GLdouble outerRadius, GLint nsides, GLint rings);

где:
  • innerRadius - внутренний радиус тора
  • outerRadius - внешний радиус тора
  • sides - число сторон у каждого радиального сечения тора
  • rings - число радиальных сечений тора.
Команды рисования додекаэдра радиусом (квадратный корень из 3):
void glutSolidDodecahedron(void);
void glutWireDodecahedron(void);

Команды рисования октаэдра радиусом 1:
void glutSolidOctahedron(void);
void glutWireOctahedron(void);

Команды рисования тетраэдра радиусом (квадратный корень из 3):
void glutSolidTetrahedron(void);
void glutWireTetrahedron(void);

Команды рисования икосаэдра радиусом 1:
void glutSolidIcosahedron(void);
void glutWireIcosahedron(void);

Команды рисования чайника:
void glutSolidTeapot(GLdouble size);
void glutWireTeapot(GLdouble size);

где:
  • size - размер чайника.

Рисуем трёхмерный чайник

Продолжаем работать в Проекте GLUTTest01.

Изменения в Main.cpp

  • В Main.cpp (в любом месте, но ДО главной функции программы main) добавь реализацию новой функции Init:
void Init()				// Инициализация OpenGL
{
	glClearColor(0.5, 0.5, 0.75, 1);	// Цвет фона

	glEnable(GL_LIGHTING);	// Включаем расчёт освещения
	glEnable(GL_LIGHT0); // Включаем первый источник света

    glEnable(GL_NORMALIZE); //делаем нормали одинаковой величины во избежание артефактов
	glEnable(GL_DEPTH_TEST);
    glEnable(GL_COLOR_MATERIAL);
    
	list=glGenLists(1);
	
	glNewList(list, GL_COMPILE);			//Создание дисплейного списка объекта (чайника)
		glutSolidTeapot(0.5);
	glEndList();
}

Здесь всё ясно из комментариев. В GLUT поддерживается до 8 источников света одновременно (GL_LIGHT0...GL_LIGHT7). Командой glEnable(GL_LIGHT0) мы активируем первый из них. По идее после включения он сразу должен начать светить на объект. Но на деле, даже будучи включенным, он требует настройки (об этом ниже).
Т.к. команда очистки экрана glClearColor повторяется в функции main,
  • Убери из функции main строку
glClearColor(0.5, 0.5, 0.75, 1);

В функции Init() мы используем список (list) выводимых примитивов. Можно и без него, но в образовательных целях пусть он остаётся. Но пока он нигде не объявлен.
  • В самом начале Main.cpp, чуть ниже списка подключаемых заголовочных файлов, поставь объявление переменной list:
GLuint list=0;

В реализации функции Init видим, что единственным элементом списка будет примитив glutSolidTeapot(0.5) размером 0,5 ед.
  • В функции main добавь вызов функции Init сразу после создания окна:
Фрагмент Main.cpp
...
	glutInitWindowPosition(100, 100);
	// Создём окно
	glutCreateWindow("My first OpenGL Application");

	Init();
...

  • В функции main исправь заголовок создаваемого окна:
Фрагмент Main.cpp
...
	// Создём окно
	glutCreateWindow("GLUT Teapot");
...

  • В фукции main задай третий параметр создаваемого окна:
Фрагмент Main.cpp
...
int main(int argc, char* argv[])
{
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);	// Задаём параметры окна
...

  • Найди реализацию функции Display() и измени её так:
Фрагмент Main.cpp
...
void Display()	// Вывод изображения на экран
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glPushMatrix();
	glCallList(list); // Вывод объекта на экран
	glPopMatrix();

	glutSwapBuffers(); // Смена переднего и заднего буферов экрана
}
...

  • В реализацию функции Keyboard() добавь условный переход удаления листа объектов по нажатию Esc:
Фрагмент Main.cpp
...
void Keyboard(unsigned char key, int x, int y)
{
	switch(key)
	{
		case VK_ESCAPE:
			{
				if(list)
					glDeleteLists(list,1);	// Удаляем дисплейный список
				exit(0);
				break;
			}
	}
}
...


После всех внесённых изменений исходный код Main.cpp выглядит так:
Main.cpp
#include <windows.h>
#include <glh\GL\glut.h>
#include <stdio.h>

GLuint list=0;

void Init()				// Инициализация OpenGL
{
	glClearColor(0.5, 0.5, 0.75, 1);	// Цвет фона

	glEnable(GL_LIGHTING);	// Включаем расчёт освещения
	glEnable(GL_LIGHT0); // Включаем первый источник света

    glEnable(GL_NORMALIZE); //делаем нормали одинаковой величины во избежание артефактов
	glEnable(GL_DEPTH_TEST);
    glEnable(GL_COLOR_MATERIAL);
    
	list=glGenLists(1);
	
	glNewList(list, GL_COMPILE);			//Создание дисплейного списка объекта (чайника)
		glutSolidTeapot(0.5);
	glEndList();
}

void Display()	// Вывод изображения на экран
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glPushMatrix();
	glCallList(list); // Вывод объекта на экран
	glPopMatrix();

	glutSwapBuffers(); // Смена переднего и заднего буферов экрана
}

void Keyboard(unsigned char key, int x, int y)
{
	switch(key)
	{
		case VK_ESCAPE:
			{
				if(list)
					glDeleteLists(list,1);	// Удаляем дисплейный список
				exit(0);
				break;
			}
	}
}

void MousePassiveMotion(int x, int y)
{
	char buf[80];
	sprintf(buf,"Mouse coords is: x=%d; y=%d", x, y);
	glutSetWindowTitle(buf);
}

int main(int argc, char* argv[])
{
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);	// Задаём параметры окна
	glutInitWindowSize(640, 480);		// Задаём размеры окна
	glutInitWindowPosition(100, 100);	// Задаём позицию окна
	glutCreateWindow("GLUT Teapot");	// Создём окно

	Init();

	// Задаём функцию обратного вызова вывода изображения на экран
	glutDisplayFunc(Display);
	glutKeyboardFunc(Keyboard); // Обработчик нажатий клавиш на клавиатуре
	glutPassiveMotionFunc(MousePassiveMotion); // Обработчик перемещений грызуна при отжатой кнопке мыши
	glutMotionFunc(MousePassiveMotion); // Обработчик перемещений грызуна при зажатой кнопке мыши

	glutMainLoop();

	return 0;
}


  • Сохрани Решение (Файл -> Сохранить все).
  • Скомпилируй Проект/Решение (F5).
При успешной компиляции на экране появится окно с чёрным силуэтом чайника. Чайник не освещён даже при включённом источнике света. Сейчас мы это исправим.

Освещаем трёхмерный чайник


Изменения в Main.cpp

  • В функции Init(), сразу после включения источника света командой glEnable(GL_LIGHT0), добавь следующие строки:
// Настраиваем источник света GL_LIGHT0
	float light_ambient[4] = {0.5, 0.5, 0, 0};
	float light_diffuse[4] = {1.0,1.0,1.0,1.0};
	float light_specular[4] = {0.0,1.0,0.0,1.0};
	float light_position[4] = {1.0,1.0,1.0,0.0};

	glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);

В первом блоке кода определяем RGBA-цвета (интенсивность от 0 до 1). Во втором присваиваем каждый из параметров освещения (ambient, diffuse, specular, position) источнику GL_LIGHT0.

  • Сохрани Решение (Файл -> Сохранить все).
  • Скомпилируй Проект/Решение (F5).
При успешной компиляции на экране появится окно с освещённым чайником.
  • Поиграй с различными значениями параметров освещённости, каждый раз перекомпилируя код и наблюдая за изменениями.
Напомним, что примитив glutSolidTeapot, указанный в функции Init, может быть заменён на любой другой, описанный выше.
  • В фунции Init подставь другие примитивы, изученные ранее.
Возможные значения параметров каждого из низ нетрудно найти в Интернете.
  • После каждого изменения перекомпилируй код и наблюдай за работой приложения.

Изучаем параметры света

Параметр Описание
Diffuse Параметр GL_DIFFUSE, наверное, наиболее точно совпадает с тем, что вы привыкли называть «цветом света». Он определяет RGBA-цвет диффузного света, который отдельный источник света добавляет к сцене.2
Ambient Параметр GL_AMBIENT влияет на цвет зеркального блика на объекте. В реальном мире на объектах вроде стеклянной бутылки имеется зеркальный блик соответствующего освещению цвета (часто белого).
Specular Параметр GL_SPECULAR влияет на интенсивность зеркального блика на объектах.
Position Параметр GL_POSITION (x, y, z, w) имеет 3 значения положения и одно, указывающее на то, какой источник света будет использоваться. Первые 3 (x, y, z) параметра понятны, а 4-й (w) параметр указывает, будет ли использоваться бесконечно удаленный свет или точечный. Если значение w = 0, то источник света бесконечно удаленный (что-то вроде солнца). Если w = 1, то этот источник света точечный (что-то вроде лампочки). Если w = 0, то первые 3 параметра - это вектор от центра системы координат (0,0,0).


Размещаем окно в центре экрана


Изменения в Main.cpp

  • В самом начале Main.cpp, после списка заголовков, подключаемых директивой #include, добавь следующие строки:
using namespace std;

int WindowWidth=640;	// Ширина окна
int WindowHeight=480;	// Высота окна

Директива using namespace std на процесс центровки окна никак не влияет, но заметно уменьшает размер исполняемого файла .exe путём "сужения" набора используемых команд заголовка windows.h.
Вынести объявление оконных параметров в специальные переменные - обычная практика Windows-программистов. Так их будет проще найти и при необходимости изменять.
Если помнишь, сейчас размеры окна жёстко задаются в функции main специальным оператором glutInitWindowSize. Чтобы не было путаницы...
  • Найди в функции main оператор glutInitWindowSize и измени его так:
glutInitWindowSize(WindowWidth, WindowHeight);		// Задаём размеры окна

Теперь значения берутся из объявленных выше переменных.
  • Найди в функции main оператор glutInitWindowPosition и измени его так:
glutInitWindowPosition((glutGet(GLUT_SCREEN_WIDTH)-WindowWidth)/2,
		                   (glutGet(GLUT_SCREEN_HEIGHT)-WindowHeight)/2); // Размещаем окно в центре экрана

Функция glutGet предназначена для получения различных параметров окна и экрана. Вот лишь некоторые её константы-идентификаторы:
Константа-идентификатор Описание
GLUT_WINDOW_X X-координата верхнего левого угла окна.
GLUT_WINDOW_Y Y-координата верхнего левого угла окна.
GLUT_WINDOW_WIDTH Ширина окна.
GLUT_WINDOW_HEIGHT Высота окна.
GLUT_WINDOW_CURSOR Текущий курсор мыши.
GLUT_SCREEN_WIDTH Ширина экрана.
GLUT_SCREEN_HEIGHT Высота экрана.
GLUT_ELAPSED_TIME Число милисекунд, прошедших с момента первого вызова функции glutInit.
GLUT_MENU_NUM_ITEMS Число элементов в главном меню окна.

С полным списком можно ознакомиться здесь: https://www.opengl.org/resources/libraries/glut/spec3/node70.html(external link)
  • Сохрани Решение (Файл -> Сохранить все).
  • Скомпилируй Проект/Решение (F5).
При успешной компиляции на экране появится окно приложения, расположенное в центре экрана.

Вращаем чайник при зажатой левой кнопке мыши


Изменения в Main.cpp

  • В начале листинга, чуть ниже объявлений WindowWidth и WindowHeight, объявим переменные углов поворота по осям X и Y и кое что ещё:
GLfloat  rx=0;			// Угол поворта сцены вокруг оси X
GLfloat  ry=0;			// Угол поворта сцены вокруг оси Y

int mx,my;				// Координаты мыши
bool ldown=false;		// Нажата левая клавиша мыши?
bool rdown=false;		// Нажата правая клавиша мыши?

  • В реализации функции Display() между строками glPushMatrix() и glCallList(list) добавь след. строки:
glRotatef(rx,1,0,0);
glRotatef(ry,0,1,0);

Осталось только прописать изменение переменных rx и ry по нажатию левой кнопки мыши (ЛКМ).
  • В любом месте листинга, но ДО функции main добавь функцию Mouse():
void Mouse(int button, int state, int x, int y)		//Обработка щелчков мыши
{
	if (button==GLUT_LEFT_BUTTON)		//Левая кнопка
	{
		switch (state)
		{
			case GLUT_DOWN:		//Если нажата
				ldown=true;		//установить флаг
				mx=x;			//Запомнить координаты
				my=y;
				break;
			case GLUT_UP:
				ldown=false;
				break;
		}
	}
}

  • В функции main ДО строки glutMainLoop добавь вызов функции Mouse:
glutMouseFunc(Mouse);

  • В любом месте листинга, но ДО функции main добавь функцию MouseMotion():
void MouseMotion(int x, int y)	//Перемешение мыши
{
	if (ldown)		// Левая кнопка
	{
		rx+=0.5*(y-my);	//Изменение угола поворота
		ry+=0.5*(x-mx);
		mx=x;
		my=y;
		glutPostRedisplay();	//Перерисовать экран
	}
}

Функция MouseMotion вызывается через оператор glutMotionFunc, добавленный нами ранее.
  • В функции main найди оператор glutMotionFunc и подставь в его параметр функцию MouseMotion:
glutMotionFunc(MouseMotion); // Обработчик перемещений грызуна при зажатой кнопке мыши


После всех внесённых изменений исходный код Main.cpp выглядит так:
Main.cpp
#include <windows.h>
#include <glh\GL\glut.h>
#include <stdio.h>

using namespace std;

int WindowWidth=640;	// Ширина окна
int WindowHeight=480;	// Высота окна

GLfloat  rx=0;			// Угол поворта сцены вокруг оси X
GLfloat  ry=0;			// Угол поворта сцены вокруг оси Y
int mx,my;				// Координаты мыши
bool ldown=false;		// Нажата левая клавиша мыши?
bool rdown=false;		// Нажата правая клавиша мыши?

GLuint list=0;

void Init()				// Инициализация OpenGL
{
	glClearColor(0.5, 0.5, 0.75, 1);	// Цвет фона

	glEnable(GL_LIGHTING);	// Включаем расчёт освещения
	glEnable(GL_LIGHT0); // Включаем первый источник света

	// Настраиваем источник света GL_LIGHT0
	float light_ambient[4] = {0.5, 0.5, 0, 0};
	float light_diffuse[4] = {1.0,1.0,1.0,1.0};
	float light_specular[4] = {0.0,1.0,0.0,1.0};
	float light_position[4] = {1.0,1.0,1.0,0.2};

	glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glEnable(GL_NORMALIZE); //делаем нормали одинаковой величины во избежание артефактов
	glEnable(GL_DEPTH_TEST);
    glEnable(GL_COLOR_MATERIAL);
    
	list=glGenLists(1);
	
	glNewList(list, GL_COMPILE);			//Создание дисплейного списка объекта (чайника)
		glutSolidTeapot(0.5);
	glEndList();
}

void Display()	// Вывод изображения на экран
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glPushMatrix();
	glRotatef(rx,1,0,0);
	glRotatef(ry,0,1,0);
	glCallList(list); // Вывод объекта на экран
	glPopMatrix();

	glutSwapBuffers(); // Смена переднего и заднего буферов экрана
}

void Keyboard(unsigned char key, int x, int y)
{
	switch(key)
	{
		case VK_ESCAPE:
			{
				if(list)
					glDeleteLists(list,1);	// Удаляем дисплейный список
				exit(0);
				break;
			}
	}
}

void Mouse(int button, int state, int x, int y)		//Обработка щелчков мыши
{
	if (button==GLUT_LEFT_BUTTON)		//Левая кнопка
	{
		switch (state)
		{
			case GLUT_DOWN:		//Если нажата
				ldown=true;		//установить флаг
				mx=x;			//Запомнить координаты
				my=y;
				break;
			case GLUT_UP:
				ldown=false;
				break;
		}
	}
}

void MouseMotion(int x, int y)	//Перемешение мыши
{
	if (ldown)		// Левая кнопка
	{
		rx+=0.5*(y-my);	//Изменение угола поворота
		ry+=0.5*(x-mx);
		mx=x;
		my=y;
		glutPostRedisplay();	//Перерисовать экран
	}
}

void MousePassiveMotion(int x, int y)
{
	char buf[80];
	sprintf(buf,"Mouse coords is: x=%d; y=%d", x, y);
	glutSetWindowTitle(buf);
}

int main(int argc, char* argv[])
{
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);	// Задаём параметры окна
	glutInitWindowSize(WindowWidth, WindowHeight);		// Задаём размеры окна
	glutInitWindowPosition((glutGet(GLUT_SCREEN_WIDTH)-WindowWidth)/2, 
		                   (glutGet(GLUT_SCREEN_HEIGHT)-WindowHeight)/2);	// Размещаем окно в центре экрана

	glutCreateWindow("GLUT Teapot");	// Создём окно

	Init();

	// Задаём функцию обратного вызова вывода изображения на экран
	glutDisplayFunc(Display);
	glutKeyboardFunc(Keyboard); // Обработчик нажатий клавиш на клавиатуре
	glutPassiveMotionFunc(MousePassiveMotion); // Обработчик перемещений грызуна при отжатой кнопке мыши
	glutMotionFunc(MouseMotion); // Обработчик перемещений грызуна при зажатой кнопке мыши
	glutMouseFunc(Mouse);

	glutMainLoop();

	return 0;
}


  • Сохрани Решение (Файл -> Сохранить все).
  • Скомпилируй Проект/Решение (F5).
При успешной компиляции появится окно программы с чайником. Если в области окна зажать ЛКМ и подвигать мышь в разные стороны чайник будет вращаться по осям X и Y, следуя движениям мыши. Красотища!

Перемещаем чайник в окне при зажатой правой кнопке мыши

Согласно замысла, плоскость перемещения объекта при движении мыши вверх-вниз будет изменяться с одной (вверх вниз, по оси Y) на другую (ближе-дальше, по оси Z) по нажатию <F1> на клавиатуре.

Изменения в Main.cpp


  • В начале листинга, чуть ниже других ранее объявленных переменных, добавим несколько переменных, необходимых для смены плоскости перемещения:
GLfloat  tx=0;			// Сдвиг по оси X
GLfloat	 ty=0;			// Y
GLfloat	 tz=-1;			// Z
GLint	 tt=0;			// Активная плоскось: 0 - XY, 1 - XZ

const string tstr[2]={"Translate XY", "Translate XZ"};  // Выводит в заголовок выбранную плоскость перемещения.


  • В списке подключаемых заголовков добавь чуть ниже остальных:
#include <string>


  • В реализации функции Mouse(), чуть ниже блока ЛКМ, допишем блок кода, добавляющий нажатие правой кнопки мыши (ПКМ):
if (button==GLUT_RIGHT_BUTTON)	//Правая кнопка
	{
		switch (state)
		{
			case GLUT_DOWN:
				rdown=true;
				mx=x;
				my=y;
				break;
			case GLUT_UP:
				rdown=false;
				break;
		}
	}


  • В реализации функции MouseMotion() пропишем реакцию на событие, когда rdown=true:
if (rdown)	//Правая
	{
		tx+=0.01*(x-mx);	//Перемещение вдоль активной плоскости
		if (tt)
			tz+=0.01*(y-my);
		else
			ty+=0.01*(my-y);
		mx=x;
		my=y;
		glutPostRedisplay();
	}


  • Добавь реализацию функции KeyboardSpecial(), отвечающей за реакцию на нажатие саецклавиш, в любом месте, но ДО функции main():
void KeyboardSpecial(int key, int x, int y)
{
	switch (key)
	{
		case GLUT_KEY_F1:	//Если нажата клавиша F1
		{
			tt=(tt+1)%2;	//Смена плоскости перемещения
			glutSetWindowTitle(tstr[tt].c_str());	//Изменение заголовка
		}

	}
}


  • В функции main(), после вызова функции Init(), добавь вызов функции KeyboardSpecial():
glutSpecialFunc(KeyboardSpecial);	// Обработчик нажатий специальных клавиш на клавиатуре


  • В функции Display() добавь след. строку, отвечающую за изменение матрицы трансляции (=перемещения) в зависимости от текущих координат:
glTranslatef(tx,ty,tz);		//Перемещение и поворт объекта

Теперь функция Display() выглядит так:
Фрагмент Main.cpp
...
void Display()	// Вывод изображения на экран
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glPushMatrix();
	glRotatef(rx,1,0,0);
	glRotatef(ry,0,1,0);
	glCallList(list); // Вывод объекта на экран
	glPopMatrix();

	glutSwapBuffers(); // Смена переднего и заднего буферов экрана
}
...


После всех внесённых изменений Main.cpp выглядит так:
Main.cpp
#include <windows.h>
#include <glh\GL\glut.h>
#include <stdio.h>
#include <string>

using namespace std;

int WindowWidth=800;	// Ширина окна
int WindowHeight=600;	// Высота окна

GLfloat  rx=0;			// Угол поворта сцены вокруг оси X
GLfloat  ry=0;			// Угол поворта сцены вокруг оси Y
GLfloat  tx=0;			// Сдвиг по оси X
GLfloat	 ty=0;			// Y
GLfloat	 tz=-1;			// Z
GLint	 tt=0;			// Активная плоскось: 0 - XY, 1 - XZ

const string tstr[2]={"Translate XY", "Translate XZ"};	// Выводит в заголовок выбранную плоскость перемещения.

int mx,my;				// Координаты мыши
bool ldown=false;		// Нажата левая клавиша мыши?
bool rdown=false;		// Нажата правая клавиша мыши?

GLuint list=0;

void Init()				// Инициализация OpenGL
{
	glClearColor(0.5, 0.5, 0.75, 1);	// Цвет фона

	glEnable(GL_LIGHTING);	// Включаем расчёт освещения
	glEnable(GL_LIGHT0); // Включаем первый источник света

	// Настраиваем источник света GL_LIGHT0
	float light_ambient[4] = {0.5, 0.5, 0, 0};
	float light_diffuse[4] = {1.0,1.0,1.0,1.0};
	float light_specular[4] = {0.0,1.0,0.0,1.0};
	float light_position[4] = {1.0,1.0,1.0,0.2};

	glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glEnable(GL_NORMALIZE); //делаем нормали одинаковой величины во избежание артефактов
	glEnable(GL_DEPTH_TEST);
    glEnable(GL_COLOR_MATERIAL);
    
	list=glGenLists(1);
	
	glNewList(list, GL_COMPILE);			//Создание дисплейного списка объекта (чайника)
		glutSolidTeapot(0.5);
	glEndList();
}

void Display()	// Вывод изображения на экран
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glPushMatrix();
		glTranslatef(tx,ty,tz);	//Перемещение объекта
		glRotatef(rx,1,0,0);	//Поворот объекта
		glRotatef(ry,0,1,0);

		glCallList(list); // Вывод объекта на экран
		glPopMatrix();

	glutSwapBuffers(); // Смена переднего и заднего буферов экрана
}

void Keyboard(unsigned char key, int x, int y)
{
	switch(key)
	{
		case VK_ESCAPE:
			{
				if(list)
					glDeleteLists(list,1);	// Удаляем дисплейный список
				exit(0);
				break;
			}
	}
}

void KeyboardSpecial(int key, int x, int y)
{
	switch (key)
	{
		case GLUT_KEY_F1:	//Если нажата клавиша F1
		{
			tt=(tt+1)%2;	//Смена плоскости перемещения
			glutSetWindowTitle(tstr[tt].c_str());	//Изменение заголовка
		}

	}
}

void Mouse(int button, int state, int x, int y)		//Обработка щелчков мыши
{
	if (button==GLUT_LEFT_BUTTON)		//Левая кнопка
	{
		switch (state)
		{
			case GLUT_DOWN:		//Если нажата
				ldown=true;		//установить флаг
				mx=x;			//Запомнить координаты
				my=y;
				break;
			case GLUT_UP:
				ldown=false;
				break;
		}
	}

	if (button==GLUT_RIGHT_BUTTON)	//Правая кнопка
	{
		switch (state)
		{
			case GLUT_DOWN:		//Если нажата
				rdown=true;		//установить флаг
				mx=x;			//Запомнить координаты
				my=y;
				break;
			case GLUT_UP:
				rdown=false;
				break;
		}
	}
}

void MouseMotion(int x, int y)	//Перемешение мыши
{
	if (ldown)		// Левая кнопка
	{
		rx+=0.5*(y-my);	//Изменение угола поворота
		ry+=0.5*(x-mx);
		mx=x;
		my=y;
		glutPostRedisplay();	//Перерисовать экран
	}
		if (rdown)	//Правая
	{
		tx+=0.01*(x-mx);	//Перемещение вдоль активной плоскости
		if (tt)
			tz+=0.01*(y-my);
		else
			ty+=0.01*(my-y);
		mx=x;
		my=y;
		glutPostRedisplay();
	}
}

void MousePassiveMotion(int x, int y)
{
	char buf[80];
	sprintf(buf,"Mouse coords is: x=%d; y=%d", x, y);
	glutSetWindowTitle(buf);
}

int main(int argc, char* argv[])
{
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);	// Задаём параметры окна
	glutInitWindowSize(WindowWidth, WindowHeight);		// Задаём размеры окна
	glutInitWindowPosition((glutGet(GLUT_SCREEN_WIDTH)-WindowWidth)/2, 
		                   (glutGet(GLUT_SCREEN_HEIGHT)-WindowHeight)/2);	// Размещаем окно в центре экрана

	glutCreateWindow("GLUT Teapot");	// Создём окно

	Init();

	// Задаём функцию обратного вызова вывода изображения на экран
	glutDisplayFunc(Display);
	glutKeyboardFunc(Keyboard); // Обработчик нажатий клавиш на клавиатуре
	glutSpecialFunc(KeyboardSpecial);	// Обработчик нажатий специальных клавиш на клавиатуре
	glutPassiveMotionFunc(MousePassiveMotion); // Обработчик перемещений грызуна при отжатой кнопке мыши
	glutMotionFunc(MouseMotion); // Обработчик перемещений грызуна при зажатой кнопке мыши
	glutMouseFunc(Mouse);

	glutMainLoop();

	return 0;
}


  • Сохрани Решение (Файл -> Сохранить все).
  • Скомпилируй Проект/Решение (F5).
При успешной компиляции появится окно программы с чайником. Если в области окна зажать ЛКМ и подвигать мышь в разные стороны чайник будет вращаться по осям X и Y, следуя движениям мыши. При зажатой ПКМ чайник будет двигаться вверх-вниз. При смене режима (F1) чайник будет то приближаться, то отдаляться.

К следующей главе: OpenGL - Библиотека GLH

Источники:


1. Гайдуков С. OpenGL. Профессиональное программирование трехмерной графики на C++. - БВХ-Петербург. 2004
2. http://esate.ru/uroki/OpenGL/uroki_opengl/_p4077/


Contributors to this page: slymentat .
Последнее изменение страницы Вторник 03 / Сентябрь, 2019 11:54:42 MSK автор slymentat.

Помочь проекту

Яндекс-деньги: 410011791055108