Файлы к лабораторной работе Вы можете скачать здесь.
Выполните
команду меню оболочки File/New/Project.
В появившемся
окне мастера создания заготовки проекта задайте имя проекта LightAndMaterial, выберите каталог размещения проекта, как
показано на снимке
После
нажатия кнопки OK в следующем окне оставьте установки мастера
по умолчанию и щелкните на кнопке Finish 
Переименуйте
функцию _tmain() файла LightAndMaterial.cpp в main(), измените в заголовке определения
функции main() тип // LightAndMaterial.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
void main(int argc, char* argv[])
{
}
Через
панель 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>
#include <iostream>
#include <tchar.h>
// TODO: reference additional headers your program requires here
В этой лабораторной работе мы опять выполним несколько упражнений, которые упакуем в одно приложение. Соответственно, для вызова каждого отдельного упражнения предусмотрим свой пункт контекстного меню. Код приложения будем постепенно наращивать по мере выполнения упражнений рассматриваемой темы.
Если не предпринимать специальных мер, то сцена с нарисованными объектами будет казаться неестественной, картинка не будет передавать пространственного характера смоделированных объектов. Плохое качество изображения объясняется тем, что при формировании поверхность объекта заливается одним и тем же цветом. Про реалистичное отображенияе объекта говорят, что цвет на нем имеет множество градаций или оттенков ( shades - тени).
Упрощенная структура глаза человека показана на рисунке.

Свет попадает в глаз через роговицу и хрусталик (своего рода линзу). Прозрачная роговица защищает хрусталик от повреждений. Радужная оболочка глаза играет роль диафрагмы, регулируя количество пропускаемого внутрь света (регулирует интенсивность освещения внутренней части глаза). Хрусталик проецирует изображение на двухмерную поверхность сетчатки - тыльной стороны глазного яблока. Расположенные на сетчатке фоточувствительные зрительные клетки - палочки и колбочки - играют роль приемников видимого спектра света в диапазоне длины волны 390 - 720 нанометров.
Палочки являются высокочувствительными приемниками излучения и функционируют в условиях слабого освещения (ночью), колбочки - при дневном освещении. Колбочки делятся на три цветовые группы, каждая из которых лучше воспринимает красный, зеленый и синий ( RGB - модель ). Смесь в разных пропорциях этих трех базовых цветов дает всю цветовую гамму цветов.
При цветовосприятии сказывается двойственная природа света.С одной стороны, свет - это электромагнитные волны с видимым спектром, равным цветам радуги. Вспомним школьную "запоминалку": Каждый Охотник Желает Знать Где Сидит Фазан (Красный-Оранжевый-Желтый-Зеленый-Голубой-Синий-Фиолетовый). На рисунке в центре приведена видимая часть спектра света, по краям которой расположены области света, невидимые невооруженным приборами глазом.

С другой стороны, свет представляют как частицу - фотон. Любой объект, освещаемый реальным источником света, бомбардируется миллиардами фотонов, как частиц света. Любой материал поверхности освещаемого объекта частично поглащает, а частично отражает фотоны разных частот видимого спектра (разного цвета). При взгляде на физическую поверхность глаз воспринимает совокупный цвет каждой точки этой поверхности в зависимости от распределения
Различные поверхности могут иметь разные свойства: блестящие
поверхности отражают почти весь свет в нужных направлениях,
матовые поверхности рассеивают фотоны равномерно в разные стороны.
В
Поскольку цвет задается тремя положительными кодами,
то все доступные цвета можно смоделировать объемом, который
можно назвать кубом цвета. Давайте
смоделируем куб цвета средствами
Заполните
файл LightAndMaterial.cpp следующим кодом// LightAndMaterial.cpp : Defines the entry point for the console application.
//
//**********************************************************
// Подключение стандартного файла с библиотекой OpenGL
#include "stdafx.h"
//**********************************************************
// Прототипы функций
void ExecuteMenu(int); // Контекстное меню первого уровня
void TimerFunc(int); // Обработчик события таймера
void SpecialKeys(int, int, int); // Обработка нажатия клавиш
void RenderScene(void); // Функция рендеринга
void ChangeSize(int, int); // Функция установки отсекающего объема
// Глобальная переменная выбранного варианта основного меню
int choice = 1;
// Глобальные переменные для создания вращения
// в градусах
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
GLfloat zRot = 0.0f;
GLint w, h; // Ширина и высота экрана
//**********************************************************
// Подключение файлов с упражнениями
#include "ColorCube.h" // Упражнение 1: "1) Куб цвета"
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
// Сбрасываем углы вращения прежнего варианта
xRot = yRot = zRot = 0;
// Запоминаем выбор в глобальную переменную
::choice = choice;
switch(::choice)
{
case 1:
ChangeSizeColorCube(w, h);
break;
}
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//**********************************************************
// Функция обратного вызова для рисования сцены
// для первого упражнения ColorCube
void RenderScene(void)
{
switch(::choice)
{
case 1:
RenderSceneColorCube();
break;
}
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
// для первого упражнения ColorCube
void ChangeSize(int width, int height)
{
w = width;
h = height;
switch(::choice)
{
case 1:
ChangeSizeColorCube(width, height);
break;
}
}
//**********************************************************
// Обработчик события таймера
void TimerFunc(int value)
{
glutPostRedisplay(); // Перерисовка сцены
glutTimerFunc(30, TimerFunc, 1); // Заряжаем новый таймер
}
//**********************************************************
// Управление с клавиатуры стрелками
// для задания новых значений матрицы поворота
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP) // Стрелка вверх
xRot -= 5.0f;
if(key == GLUT_KEY_DOWN)// Стрелка вниз
xRot += 5.0f;
if(key == GLUT_KEY_LEFT)// Стрелка влево
yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT)// Стрелка вправо
yRot += 5.0f;
xRot = (GLfloat)((const int)xRot % 360);
yRot = (GLfloat)((const int)yRot % 360);
// Вызвать принудительно визуализацию с помощью RenderScene()
glutPostRedisplay();
}
//**********************************************************
void main(int argc, char* argv[])
{
glutInit(argc, argv);
// Двойная буферизация, цветовая модель RGB, буфер глубины
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(300, 300); // Начальные размеры окна
glutCreateWindow("Свет и материалы"); // Заголовок окна
glutDisplayFunc(RenderScene); // Обновление сцены при разрушении окна
glutReshapeFunc(ChangeSize); // При изменении размера окна
glutTimerFunc(100, TimerFunc, 1); // Создали таймер
glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("1) Куб цвета", 1);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutMainLoop(); // Цикл сообщений графического окна
}
Добавьте
к проекту новый 
Заполните
файл ColorCube.h таким
кодом// Упражнение 1: "1) Куб цвета"
//**********************************************************
// Прототипы
void ColorCube();
void RenderSceneColorCube(void);
void ChangeSizeColorCube(int, int); // При изменении размеров окна
//**********************************************************
void ColorCube()
{
glPushMatrix();// Сохранить матрицу преобразования модели
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
glRotatef(zRot, 0.0f, 0.0f, 1.0f);
// Для анимации
xRot += 1.0f;
yRot += 1.0f;
zRot += 1.0f;
xRot = (GLfloat)((const int)xRot % 360);
yRot = (GLfloat)((const int)yRot % 360);
zRot = (GLfloat)((const int)zRot % 360);
// Рисуем шесть граней
glBegin(GL_QUADS);
// 1. Передняя грань
// Белый
glColor3ub((GLubyte) 255, (GLubyte)255, (GLubyte)255);
glVertex3f(50.0f,50.0f,50.0f);
// Желтый
glColor3ub((GLubyte) 255, (GLubyte)255, (GLubyte)0);
glVertex3f(50.0f,-50.0f,50.0f);
// Красный
glColor3ub((GLubyte) 255, (GLubyte)0, (GLubyte)0);
glVertex3f(-50.0f,-50.0f,50.0f);
// Сиреневый
glColor3ub((GLubyte) 255, (GLubyte)0, (GLubyte)255);
glVertex3f(-50.0f,50.0f,50.0f);
// 2. Задняя грань
// Голубой
glColor3f(0.0f, 1.0f, 1.0f);
glVertex3f(50.0f,50.0f,-50.0f);
// Зеленый
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(50.0f,-50.0f,-50.0f);
// Черный
glColor3f(0.0f, 0.0f, 0.0f);
glVertex3f(-50.0f,-50.0f,-50.0f);
// Синий
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(-50.0f,50.0f,-50.0f);
// 3. Верхняя грань
// Голубой
glColor3f(0.0f, 1.0f, 1.0f);
glVertex3f(50.0f,50.0f,-50.0f);
// Белый
glColor3f(1.0f, 1.0f, 1.0f);
glVertex3f(50.0f,50.0f,50.0f);
// Сиреневый
glColor3f(1.0f, 0.0f, 1.0f);
glVertex3f(-50.0f,50.0f,50.0f);
// Синий
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(-50.0f,50.0f,-50.0f);
// 4. Нижняя грань
// Зеленый
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(50.0f,-50.0f,-50.0f);
// Желтый
glColor3f(1.0f, 1.0f, 0.0f);
glVertex3f(50.0f,-50.0f,50.0f);
// Красный
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(-50.0f,-50.0f,50.0f);
// Черный
glColor3f(0.0f, 0.0f, 0.0f);
glVertex3f(-50.0f,-50.0f,-50.0f);
// 5. Левая грань
// Белый
glColor3f(1.0f, 1.0f, 1.0f);
glVertex3f(50.0f,50.0f,50.0f);
// Голубой
glColor3f(0.0f, 1.0f, 1.0f);
glVertex3f(50.0f,50.0f,-50.0f);
// Зеленый
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(50.0f,-50.0f,-50.0f);
// Желтый
glColor3f(1.0f, 1.0f, 0.0f);
glVertex3f(50.0f,-50.0f,50.0f);
// 6. Правая грань
// Сиреневый
glColor3f(1.0f, 0.0f, 1.0f);
glVertex3f(-50.0f,50.0f,50.0f);
// Синий
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(-50.0f,50.0f,-50.0f);
// Черный
glColor3f(0.0f, 0.0f, 0.0f);
glVertex3f(-50.0f,-50.0f,-50.0f);
// Красный
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(-50.0f,-50.0f,50.0f);
glEnd();
glPopMatrix();// Восстановить матрицу преобразования модели
// Переключаем буферы рисования
glutSwapBuffers();
}
//**********************************************************
// Функция обратного вызова для рисования сцены
// для первого упражнения ColorCube
void RenderSceneColorCube(void)
{
// Сбрасываем буфер цвета и буфер глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Рисуем сцену
ColorCube();
// Прокачка сообщений
glFlush();
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
// для первого упражнения ColorCube
void ChangeSizeColorCube(int width, int height)
{
// Индивидуальные настройки для упражнения
glClearColor(0.0F, 0.0F, 0.0F, 1.0F);// Фон черный непрозрачный
glEnable(GL_DEPTH_TEST); // Включили проверку глубины
glEnable(GL_DITHER); // Включить режим генерации полутонов
glShadeModel(GL_SMOOTH); // Режим градиентной закраски
glDisable(GL_CULL_FACE); // Выключить режим отсечения сторон
// Предотвращаем деление на нуль
if(height == 0)
height = 1;
// Устанавливаем поле просмотра с размерами окна
glViewport(0, 0, width, height);
// Устанавливает матрицу преобразования в режим проецирования
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Устанавливаем размеры перспективы (отсекающего объема)
GLfloat aspectRatio = (GLfloat)width / (GLfloat)height;// Для коррекции
gluPerspective(35.0f, aspectRatio, 1.0f, 1000.0f);
// Восстановливает матрицу преобразования в исходный режим вида
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Отодвигаем куб от начала координат вглубь оси 0z
glTranslatef(0.0f, 0.0f, -400.0f);
}
Разберитесь
с кодом, запустите упражнение, поуправляйте изображением
с помощью клавиш-стрелок, полюбуйтесь вращающимся кубом цвета.Снимки экрана могут быть такими
![]() |
![]() |
С точки зрения физики поверхность материального тела может
либо само излучать свет, либо отражать свет при попадании
на поверхность от внешнего источника, либо и излучать и отражать
свет одновременно. В общем случае если источник света освещает
несколько объектов сцены, то в
Для точного описания освещенности сцены нужно составить и
решить эти уравнения, что является неоправданно сложной задачей.
И как всегда в таких случаях, на помощь приходят допущения,
которые не так сильно влияют на реальную картину сцены, но
существенно упрощают задачу расчета освещенности. В графических
системах для расчета освещенности применяется локальная
модель заполнения, основанная на модели
отражения Фонга (Phong). Это на сегодняшний день вполне приемлемый
В локальной модели отражения предполагается, что каждый объект сцены находится как-бы изолированно от других объектов (отгорожен от них ширмой) и отражает в глаза наблюдателя только тот первичный свет, который попал на него от источника света. Такое упрощение позволяет сосредоточиться только на двух аспектах:
В компьютерной графике в роли глаза наблюдателя выступает картинная
плоскость и изображение формирует только
те лучи, которые должны достигать центра проецирования,
пройдя внутри зоны, ограниченной рамкой отсечения картинной

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



В
Можно считать, что фоновое освещение создается бесконечным множеством источников света, заполняющих весь объем сцены. Удобно считать, что направленный свет (диффузный и отраженный) создается прожектором ( spotlights ), бесконечно удаленным от поверхности освещения и генерирующим параллельные лучи. Примером параллельного источника света является Солнце.
В
Для задания всех параметров модели освещения используется команда glLightModel*(), которая имеет два аргумента: имя параметра модели освещения и значение для этого параметра. Синтаксис команды следующий:
void glLightModel{if}(GLenum pname, TYPE param);
void glLightModel{if}v(GLenum pname, TYPE *param);
Команда устанавливает pname (описание
приведено в таблице). Аргумент -
это собственно значение свойства, а если используется векторная
версия команды - указатель на набор значений. Невекторная версия
команды используется только для установки одиночных параметров
и неприменима для режима GL_LIGHT_MODEL_AMBIENT.
Значение параметра pname |
Значение по умолчанию | Описание |
|---|---|---|
GL_LIGHT_MODEL_AMBIENT |
(0.2, 0.2, 0.2, 1.0) | Фоновая |
GL_LIGHT_MODEL_LOCAL_VIEWER |
0.0 или GL_FALSE | Способ вычисления углов зеркального отражения (точка обзора локальная или в бесконечности) |
GL_LIGHT_MODEL_TWO_SIDE |
0.0 или GL_FALSE | Выбор между односторонним и двухсторонним освещением |
GL_LIGHT_MODEL_COLOR_CONTROL |
GL_SINGLE_COLOR | Вычисляется ли отраженный цвет отдельно от фонового или рассеянного |
В качестве иллюстрации выполним несколько примеров по моделированию поверхности реактивного самолета, в которых последовательно, сначала без использования освещения, затем применяя его в разных вариантах, рассмотрим особенности света и материалов.
Вначале мы просто нарисуем ряд цветных треугольников, составляющих поверхность модели реактивного самолета.
Добавьте
к проекту новый 
Заполните
файл Jet.h таким кодом// Упражнение 2: "2) Самолет без освещения"
//**********************************************************
// Прототипы
void Jet();
void RenderSceneJet(void);
void ChangeSizeJet(int, int); // При изменении размеров окна
//**********************************************************
// Рисовать самолет
void Jet()
{
// Сохранить матрицу в стеке и выполнить повороты
glPushMatrix();
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
glBegin(GL_TRIANGLES);
// Конус носа /////////////////////////////
// Белый
glColor3ub(255, 255, 255);
glVertex3f(0.0f, 0.0f, 60.0f);
glVertex3f(-15.0f, 0.0f, 30.0f);
glVertex3f(15.0f,0.0f,30.0f);
// Черный
glColor3ub(0,0,0);
glVertex3f(15.0f,0.0f,30.0f);
glVertex3f(0.0f, 15.0f, 30.0f);
glVertex3f(0.0f, 0.0f, 60.0f);
// Красный
glColor3ub(255,0,0);
glVertex3f(0.0f, 0.0f, 60.0f);
glVertex3f(0.0f, 15.0f, 30.0f);
glVertex3f(-15.0f,0.0f,30.0f);
// Тело самолета //////////////////////////
// Зеленый
glColor3ub(0,255,0);
glVertex3f(-15.0f,0.0f,30.0f);
glVertex3f(0.0f, 15.0f, 30.0f);
glVertex3f(0.0f, 0.0f, -56.0f);
// Желтый
glColor3ub(255,255,0);
glVertex3f(0.0f, 0.0f, -56.0f);
glVertex3f(0.0f, 15.0f, 30.0f);
glVertex3f(15.0f,0.0f,30.0f);
// Голубой
glColor3ub(0, 255, 255);
glVertex3f(15.0f,0.0f,30.0f);
glVertex3f(-15.0f, 0.0f, 30.0f);
glVertex3f(0.0f, 0.0f, -56.0f);
///////////////////////////////////////////
// Левое крыло
// Большой треугольник для основания крыла
// Серый
glColor3ub(128,128,128);
glVertex3f(0.0f,2.0f,27.0f);
glVertex3f(-60.0f, 2.0f, -8.0f);
glVertex3f(60.0f, 2.0f, -8.0f);
// Темно - серый
glColor3ub(64,64,64);
glVertex3f(60.0f, 2.0f, -8.0f);
glVertex3f(0.0f, 7.0f, -8.0f);
glVertex3f(0.0f,2.0f,27.0f);
// Светло - серый
glColor3ub(192,192,192);
glVertex3f(60.0f, 2.0f, -8.0f);
glVertex3f(-60.0f, 2.0f, -8.0f);
glVertex3f(0.0f,7.0f,-8.0f);
// Другое крыло верхней секции
// Темно - серый
glColor3ub(64,64,64);
glVertex3f(0.0f,2.0f,27.0f);
glVertex3f(0.0f, 7.0f, -8.0f);
glVertex3f(-60.0f, 2.0f, -8.0f);
///////////////////////////////////////////
// Хвост
// Розовый
glColor3ub(255,128,255);
glVertex3f(-30.0f, -0.50f, -57.0f);
glVertex3f(30.0f, -0.50f, -57.0f);
glVertex3f(0.0f,-0.50f,-40.0f);
// Верхняя левая сторона
// Коричневый
glColor3ub(255,128,0);
glVertex3f(0.0f,-0.5f,-40.0f);
glVertex3f(30.0f, -0.5f, -57.0f);
glVertex3f(0.0f, 4.0f, -57.0f);
// Верхняя правая сторона
// Коричневый
glColor3ub(255,128,0);
glVertex3f(0.0f, 4.0f, -57.0f);
glVertex3f(-30.0f, -0.5f, -57.0f);
glVertex3f(0.0f,-0.5f,-40.0f);
// Оборотная нижняя часть
// Белый
glColor3ub(255,255,255);
glVertex3f(30.0f,-0.5f,-57.0f);
glVertex3f(-30.0f, -0.5f, -57.0f);
glVertex3f(0.0f, 4.0f, -57.0f);
// Красный
glColor3ub(255,0,0);
glVertex3f(0.0f,0.5f,-40.0f);
glVertex3f(3.0f, 0.5f, -57.0f);
glVertex3f(0.0f, 25.0f, -65.0f);
// Красный
glColor3ub(255,0,0);
glVertex3f(0.0f, 25.0f, -65.0f);
glVertex3f(-3.0f, 0.5f, -57.0f);
glVertex3f(0.0f,0.5f,-40.0f);
// Серый
glColor3ub(128,128,128);
glVertex3f(3.0f,0.5f,-57.0f);
glVertex3f(-3.0f, 0.5f, -57.0f);
glVertex3f(0.0f, 25.0f, -65.0f);
glEnd(); // О самолете
glPopMatrix();
// Переключить буфер
glutSwapBuffers();
}
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderSceneJet(void)
{
// Сбрасываем буфер цвета и буфер глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Рисуем сцену
Jet();
// Прокачка сообщений
glFlush();
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
// для второго упражнения Jet
void ChangeSizeJet(int width, int height)
{
// Индивидуальные настройки для упражнения
glEnable(GL_DEPTH_TEST); // Включить тест глубины
glEnable(GL_CULL_FACE); // Отображать только лицевую сторону
glFrontFace(GL_CCW); // Считать лицевым обход против часовой стрелки
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);// Цвет фона окна
// Предотвращаем деление на нуль
if(height == 0)
height = 1;
// Устанавливаем поле просмотра с размерами окна
glViewport(0, 0, width, height);
// Устанавливает матрицу преобразования в режим проецирования
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Устанавливаем размеры ортогонального отсекающего объема
GLfloat aspectRatio = (GLfloat)width / (GLfloat)height;// Для соблюдения пропорций
GLfloat nRange = 80.0f;
if (width <= height)
glOrtho (-nRange, nRange, -nRange / aspectRatio, nRange / aspectRatio, -nRange, nRange);
else
glOrtho (-nRange * aspectRatio, nRange * aspectRatio, -nRange, nRange, -nRange, nRange);
// Восстановливает матрицу преобразования в исходный режим вида
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
Модифицируйте
файл LightAndMaterial.cpp следующим образом// LightAndMaterial.cpp : Defines the entry point for the console application.
//
//**********************************************************
// Подключение стандартного файла с библиотекой OpenGL
#include "stdafx.h"
//**********************************************************
// Прототипы функций
void ExecuteMenu(int); // Контекстное меню первого уровня
void TimerFunc(int); // Обработчик события таймера
void SpecialKeys(int, int, int); // Обработка нажатия клавиш
void RenderScene(void); // Функция рендеринга
void ChangeSize(int, int); // Функция установки отсекающего объема
// Глобальная переменная выбранного варианта основного меню
int choice = 1;
// Глобальные переменные для создания вращения
// в градусах
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
GLfloat zRot = 0.0f;
GLint w, h; // Ширина и высота экрана
//**********************************************************
// Подключение файлов с упражнениями
#include "ColorCube.h" // Упражнение 1: "1) Куб цвета"
#include "Jet.h" // Упражнение 2: "2) Самолет без освещения"
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
// Сбрасываем углы вращения прежнего варианта
xRot = yRot = zRot = 0;
// Запоминаем выбор в глобальную переменную
::choice = choice;
switch(::choice)
{
case 1:
ChangeSizeColorCube(w, h);
break;
case 2:
ChangeSizeJet(w, h);
break;
}
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
switch(::choice)
{
case 1:
RenderSceneColorCube();
break;
case 2:
RenderSceneJet();
break;
}
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
void ChangeSize(int width, int height)
{
w = width;
h = height;
switch(::choice)
{
case 1:
ChangeSizeColorCube(width, height);
break;
case 2:
ChangeSizeJet(width, height);
break;
}
}
//**********************************************************
// Обработчик события таймера
void TimerFunc(int value)
{
glutPostRedisplay(); // Перерисовка сцены
glutTimerFunc(30, TimerFunc, 1); // Заряжаем новый таймер
}
//**********************************************************
// Управление с клавиатуры стрелками
// для задания новых значений матрицы поворота
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP) // Стрелка вверх
xRot -= 5.0f;
if(key == GLUT_KEY_DOWN)// Стрелка вниз
xRot += 5.0f;
if(key == GLUT_KEY_LEFT)// Стрелка влево
yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT)// Стрелка вправо
yRot += 5.0f;
xRot = (GLfloat)((const int)xRot % 360);
yRot = (GLfloat)((const int)yRot % 360);
// Вызвать принудительно визуализацию с помощью RenderScene()
glutPostRedisplay();
}
//**********************************************************
void main(int argc, char* argv[])
{
glutInit(argc, argv);
// Двойная буферизация, цветовая модель RGB, буфер глубины
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(300, 300); // Начальные размеры окна
glutCreateWindow("Свет и материалы"); // Заголовок окна
glutDisplayFunc(RenderScene); // Обновление сцены при разрушении окна
glutReshapeFunc(ChangeSize); // При изменении размера окна
glutTimerFunc(100, TimerFunc, 1); // Создали таймер
glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("1) Куб цвета", 1);
glutAddMenuEntry("2) Самолет без освещения", 2);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutMainLoop(); // Цикл сообщений графического окна
}
Разберитесь
с кодом, запустите упражнение, поуправляйте изображением
с помощью клавиш-стрелок, полюбуйтесь самолетом.Снимки экрана могут быть такими
![]() |
![]() |
Обратите внимание, что в данном коде мы никакого освещения сцены не применяли. Самолет разукрашен автономными цветными поверхностями составляющих треугольников. Начнем реализацию освещения на самом простом примере - рисовании освещенной сферы.
Добавьте
к приложению новый // LightSphere.h
// Упражнение 3: "3) Рисование освещенной сферы"
//**********************************************************
// Прототипы
void SetLight();
void RenderSceneLightSphere();
void ChangeSizeLightSphere(int, int);
void SetLight()
{
GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};// Цвет блика
GLfloat mat_shininess[] = {50.0}; // Размер блика (обратная пропорция)
GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0};// Расположение источника
GLfloat white_light[] = {1.0, 1.0, 1.0, 1.0};// Цвет и интенсивность
// освещения, генерируемого источником
GLfloat lmodel_ambient[] = {0.5, 0.5, 0.5, 1.0};// Параметры фонового освещения
glClearColor(0.0, 0.0, 0.0, 1.0);
glShadeModel(GL_SMOOTH);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
}
//**********************************************************
void RenderSceneLightSphere()
{
// Сбрасываем буфер цвета и буфер глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();// Сохранить матрицу преобразования модели
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
glRotatef(zRot, 0.0f, 0.0f, 1.0f);
// Для анимации
xRot += 1.0f;
yRot += 1.0f;
zRot += 1.0f;
xRot = (GLfloat)((const int)xRot % 360);
yRot = (GLfloat)((const int)yRot % 360);
zRot = (GLfloat)((const int)zRot % 360);
// Рисуем сферу
glutSolidSphere(1.0, 20, 16);
glPopMatrix();// Восстановить матрицу преобразования модели
// Переключить буфер
glutSwapBuffers();
// Прокачка сообщений
glFlush();
}
//**********************************************************
void ChangeSizeLightSphere(int width, int height)
{
// Индивидуальные настройки освещения
SetLight();
// Предотвращаем деление на нуль
if(height == 0)
height = 1;
// Устанавливаем поле просмотра с размерами окна
glViewport(0, 0, width, height);
// Устанавливает матрицу преобразования в режим проецирования
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Устанавливаем размеры ортогонального отсекающего объема
GLfloat aspectRatio = (GLfloat)width / (GLfloat)height;// Для соблюдения пропорций
if (width <= height)
glOrtho (-1.5, 1.5, -1.5 / aspectRatio, 1.5 / aspectRatio, -10.0, 10.0);
else
glOrtho (-1.5 * aspectRatio, 1.5 * aspectRatio, -1.5, 1.5, -10, 10);
// Восстановливает матрицу преобразования в исходный режим вида
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
Модифицируйте
файл LightAndMaterial.cpp следующим образом// LightAndMaterial.cpp : Defines the entry point for the console application.
//
//**********************************************************
// Подключение стандартного файла с библиотекой OpenGL
#include "stdafx.h"
//**********************************************************
// Прототипы функций
void ExecuteMenu(int); // Контекстное меню первого уровня
void TimerFunc(int); // Обработчик события таймера
void SpecialKeys(int, int, int); // Обработка нажатия клавиш
void RenderScene(void); // Функция рендеринга
void ChangeSize(int, int); // Функция установки отсекающего объема
// Глобальная переменная выбранного варианта основного меню
int choice = 1;
// Глобальные переменные для создания вращения
// в градусах
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
GLfloat zRot = 0.0f;
GLint w, h; // Ширина и высота экрана
//**********************************************************
// Подключение файлов с упражнениями
#include "ColorCube.h" // Упражнение 1: "1) Куб цвета"
#include "Jet.h" // Упражнение 2: "2) Самолет без освещения"
#include "LightSphere.h" // Упражнение 3: "3) Рисование освещенной сферы"
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
// Сбрасываем углы вращения прежнего варианта
xRot = yRot = zRot = 0;
// Выключаем освещение
glDisable(GL_LIGHTING);
// Запоминаем выбор в глобальную переменную
::choice = choice;
switch(::choice)
{
case 1:
ChangeSizeColorCube(w, h);
break;
case 2:
ChangeSizeJet(w, h);
break;
case 3:
ChangeSizeLightSphere(w, h);
break;
}
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
switch(::choice)
{
case 1:
RenderSceneColorCube();
break;
case 2:
RenderSceneJet();
break;
case 3:
RenderSceneLightSphere();
break;
}
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
void ChangeSize(int width, int height)
{
w = width;
h = height;
switch(::choice)
{
case 1:
ChangeSizeColorCube(width, height);
break;
case 2:
ChangeSizeJet(width, height);
break;
case 3:
ChangeSizeLightSphere(width, height);
break;
}
}
//**********************************************************
// Обработчик события таймера
void TimerFunc(int value)
{
glutPostRedisplay(); // Перерисовка сцены
glutTimerFunc(30, TimerFunc, 1); // Заряжаем новый таймер
}
//**********************************************************
// Управление с клавиатуры стрелками
// для задания новых значений матрицы поворота
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP) // Стрелка вверх
xRot -= 5.0f;
if(key == GLUT_KEY_DOWN)// Стрелка вниз
xRot += 5.0f;
if(key == GLUT_KEY_LEFT)// Стрелка влево
yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT)// Стрелка вправо
yRot += 5.0f;
xRot = (GLfloat)((const int)xRot % 360);
yRot = (GLfloat)((const int)yRot % 360);
// Вызвать принудительно визуализацию с помощью RenderScene()
glutPostRedisplay();
}
//**********************************************************
void main(int argc, char* argv[])
{
glutInit(argc, argv);
// Двойная буферизация, цветовая модель RGB, буфер глубины
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(300, 300); // Начальные размеры окна
glutCreateWindow("Свет и материалы"); // Заголовок окна
glutDisplayFunc(RenderScene); // Обновление сцены при разрушении окна
glutReshapeFunc(ChangeSize); // При изменении размера окна
glutTimerFunc(100, TimerFunc, 1); // Создали таймер
glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("1) Куб цвета", 1);
glutAddMenuEntry("2) Самолет без освещения", 2);
glutAddMenuEntry("3) Рисование освещенной сферы", 3);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutMainLoop(); // Цикл сообщений графического окна
}
Разберитесь
с кодом, поэкспериментируйте с массивами функции SetLight(),
которая настраивает освещение сцены. Один из снимков результата
получился такой
Свет - это лишь один компонент общей картины при моделировании
освещенной сцены. Вторым компонентом является собственный цвет
поверхности объектов, которые определяется его отражательными
свойствами. Синий объект отражает преимущественно синие фотоны
и поглащает остальные. В реальном мире естественный свет является
белым (состоящий в равных долях из базовых
В компьютерном мире мы можем осветить объект, который в естественном свете способен быть синим (отражать синие фотоны), другим светом, в котором нет синей составляющей для отражения, и объект будет выглядеть черным. Поэтому для моделирования освещения объектов сцены нужно не только правильно задавать положение источников света и их свойства, но и определять правильные свойства самих освещаемых поверхностей объектов.
Свойства объектов, определяющие взаимодействие их поверхностей со светом, называются материалами. Материал определяет цвет и способ его отражения от освещенного объекта. Вместо того, чтобы сказать, что многоугольник красный, мы говорим, что многоугольник сделан из материала, отражающего преимущественно красный цвет. Тем самым мы подчеркиваем, что подразумевается добавление освещения в сцену и иная последовательность действий:
Согласно модели освещения
Как и источники света, материалы имеют различные фоновые ( ambient ),
рассеянные ( diffuse ) или отраженные ( )
цвета, которые определяют способность материала отражать ту
или иную diffuse, блик задан свойством ,
а оборотная сторона сферы отражает фоновый свет ambient.

При расчетах отдельные компоненты объединяются: фоновый свет источника и фоновый цвет материала, диффузный свет источника и рассеивающий цвет материала, коэффициент отражения света с бликовым пятном материала. "Фоновое" и "рассеивающее" свойства материала определяют цвет объекта и обычно если не одинаковы, то похожи. Отражающая способность обычно соответствует белому или серому свету, но так, чтобы наиболее яркие места на поверхности объекта по цвету совпадали с зеркальной составляющей источника. Например, красная полированная сфера в ярких солнечных лучах будет красной, но самые освещенные точки (блики) будут белыми. (Короче, Склифасовский!: фабрики рабочим - земля крестьянам; кесарю кесарево - пекарю пекарево; свет источникам - цвет материалам ; ...).
Свойства материала объекта определяют характер рассеивания
света. Поскольку взаимодействие между падающим светом и поверхностью
объекта довольно сложно, правильное задание свойств материала,
дающих нужный результат, - целое искусство. В нашем распоряжении
не только фоновый, рассеянный и отраженный цвета материала,
но и блеск, то есть насколько хорошо материал отполирован.
В упражнении рисования освещенной сферы ( LightSphere ) мы явно,
с помощью функции glMaterialfv(),
определили только два свойства материала - цвет блика и его
размер.
Большинство свойств материала концептуально (по смыслу) похожи
на свойства источников света. Механизм их указания помощью
функции glMaterialfv() подобен
установке параметров источника света. Команды задания свойств
материала имеют следующий синтаксис:
void glMaterial{if}(GLenum face, GLenum pname, TYPE param);
void glMaterial{if}v(GLenum face, GLenum pname, TYPE *param);
Команда задает текущие значения
свойств материала для использования при расчете освещенности. Аргумент может
принимать значения GL_FRONT, GL_BACK, GL_FRONT_AND_BACK в
соответствии с тем, к лицевым или оборотным сторонам объекта
применяется это свойство, или и к тем и другим вместе. Устанавливаемое
свойство материала идентифицируется именем pname (приведены
в таблице ниже), а его значение передается в аргументе .
В векторном варианте является
указателем на GL_SHININESS.
Значение параметра pname |
Значение по умолчанию | Описание |
|---|---|---|
GL_AMBIENT |
(0.2, 0.2, 0.2, 1.0) | Фоновый цвет материала |
GL_DIFFUSE |
(0.8, 0.8, 0.8, 1.0) | Рассеянный цвет материала |
GL_AMBIENT_AND_DIFFUSE |
- | Фоновый и рассеянный цвета материала, заданные
одним |
GL_SPECULAR |
(0.0, 0.0, 0.0, 1.0) | Отраженный цвет материала |
GL_SHININESS |
0.0 | Коэффициент зеркального отражения (0.0 - 128.0). Чем больше значение, тем меньше и ярче (более сфокусирован) зеркальный блик. |
GL_EMISSION |
(0.0, 0.0, 0.0, 1.0) | Излучаемый цвет материала |
GL_COLOR_INDEXES |
(0, 1, 1) | Индексы фонового, рассеянного и отраженного цветов |
Зеркальное отражение от объекта производит блики. В отличие от фонового и рассеянного отражения, то, что видит наблюдатель здесь, зависит от положения точки обзора. Представим, что мы смотрим на металлический шарик под солнечным светом. При движении головы блик, создаваемый солнечным светом на шарике, до некоторой степени будет передвигаться вместе с нами. Но если мы слишком далеко закинем голову, блик вообще исчезнет.
GL_SPECULAR задает цвет блика,
а GL_SHININESS -
Излучение. Задавая - цвет для
параметра GL_EMISSION, можно изобразить объект так, как будто
он испускает свет данного цвета. Поскольку большинство реальных
объектов (кроме источников света) сами не излучают свет, этот
эффект используется в основном для имитации ламп и других источников
света. Так, например, объект, нарисованный при таких текущих
настройках материала, как
GLfloat mat_emission[] = {0.8, 0.0, 0.2, 0.0};
glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
будет выглядеть красноватым.
Здесь нужно понимать, что хоть объект и будет казаться светящимся, но он не будет действовать как источник света, т.е. не будет давать отблески на соседние объекты. Для того, чтобы он действовал как источник света, нужно создать источник света и поместить его внутрь этого светящегося объекта.
Ко всем примитивам, заданным после вызова функции glMaterial*(),
применяются последние установленный
значения, пока не будет вызвана следующая функция glMaterial*().
Для того, чтобы нарисовать несколько объектов с различными
свойствами материала, непосредственно перед рисованием
каждого объекта нужно настраивать текущие свойства материала
вызовом функции glMaterial*() с
нужными параметрами. Продемонстрируем это на примере
Добавьте
к проекту новый 
Наполните
файл следующим кодом// MultiMaterial.h
// Упражнение 4: "4) Рисование с разными материалами"
//**********************************************************
// Прототипы
void SetLightMultiMaterial();
void RenderSceneMultiMaterial();
void ChangeSizeMultiMaterial(int, int);
// Глобальные массивы свойств материала и освещения
GLfloat no_mat[] = {0.0, 0.0, 0.0, 1.0};
GLfloat mat_ambient[] = {0.7, 0.7, 0.7, 1.0};// Параметры фонового освещения
GLfloat mat_ambient_color[] = {0.8, 0.8, 0.2, 1.0};
GLfloat mat_diffuse[] = {0.1, 0.5, 0.8, 1.0};
GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};// Цвет блика белый
GLfloat no_shininess[] = {0.0}; // Размер блика (обратная пропорция)
GLfloat low_shininess[] = {5.0}; // Размер блика (большая площадь)
GLfloat high_shininess[] = {100.0}; // Размер блика (маленькая площадь - большой фокус)
GLfloat mat_emission[] = {0.8, 0.0, 0.2, 0.0};// Красноватое излучение
GLfloat white_light[] = {1.0, 1.0, 1.0, 1.0};// Цвет и интенсивность
// освещения, генерируемого источником
GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0};// Расположение источника
GLfloat lmodel_ambient[] = {0.5, 0.5, 0.5, 1.0};// Параметры фонового освещения
void SetLightMultiMaterial()
{
glClearColor(0.0, 0.0, 0.0, 1.0);
glShadeModel(GL_SMOOTH);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
}
//**********************************************************
void RenderSceneMultiMaterial()
{
// Сбрасываем буфер цвета и буфер глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Рисуем левую верхнюю сферу
glPushMatrix();// Сохранить матрицу преобразования модели
glTranslatef(-.75, 0.75, 0.0);
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
glRotatef(zRot, 0.0f, 0.0f, 1.0f);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient_color);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
glutSolidSphere(0.75, 16, 16); // Диаметр, число параллелей и меридиан
glPopMatrix();// Восстановить матрицу преобразования модели
// Рисуем правую верхнюю сферу
glPushMatrix();// Сохранить матрицу преобразования модели
glTranslatef(0.75, 0.75, 0.0);
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
glRotatef(zRot, 0.0f, 0.0f, 1.0f);
glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
glutSolidSphere(0.75, 16, 16); // Диаметр, число параллелей и меридиан
glPopMatrix();// Восстановить матрицу преобразования модели
// Рисуем левую нижнюю сферу
glPushMatrix();// Сохранить матрицу преобразования модели
glTranslatef(-0.75, -0.75, 0.0);
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
glRotatef(zRot, 0.0f, 0.0f, 1.0f);
glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
glutSolidSphere(0.75, 16, 16); // Диаметр, число параллелей и меридиан
glPopMatrix();// Восстановить матрицу преобразования модели
// Рисуем правую нижнюю сферу
glPushMatrix();// Сохранить матрицу преобразования модели
glTranslatef(0.75, -0.75, 0.0);
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
glRotatef(zRot, 0.0f, 0.0f, 1.0f);
glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
glutSolidSphere(0.75, 16, 16); // Диаметр, число параллелей и меридиан
glPopMatrix();// Восстановить матрицу преобразования модели
// Для анимации
xRot += 1.0f;
yRot += 1.0f;
zRot += 1.0f;
xRot = (GLfloat)((const int)xRot % 360);
yRot = (GLfloat)((const int)yRot % 360);
zRot = (GLfloat)((const int)zRot % 360);
// Переключить буфер
glutSwapBuffers();
// Прокачка сообщений
glFlush();
}
//**********************************************************
void ChangeSizeMultiMaterial(int width, int height)
{
// Индивидуальные настройки освещения
SetLightMultiMaterial();
// Предотвращаем деление на нуль
if(height == 0)
height = 1;
// Устанавливаем поле просмотра с размерами окна
glViewport(0, 0, width, height);
// Устанавливает матрицу преобразования в режим проецирования
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Устанавливаем размеры ортогонального отсекающего объема
GLfloat aspectRatio = (GLfloat)width / (GLfloat)height;// Для соблюдения пропорций
if (width <= height)
glOrtho (-1.5, 1.5, -1.5 / aspectRatio, 1.5 / aspectRatio, -10.0, 10.0);
else
glOrtho (-1.5 * aspectRatio, 1.5 * aspectRatio, -1.5, 1.5, -10, 10);
// Восстановливает матрицу преобразования в исходный режим вида
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
Здесь мы поочередно настраиваем свойства материала перед рисованием
очередной сферы, повторно вызывая функцию glMaterial*().
Обратите внимание, что для экономии временных затрат эту функцию
необходимо вызывать только для тех параметров, которые изменяются.
Измените
код файла LightAndMaterial.cpp, чтобы он поддерживал новое
упражнение// LightAndMaterial.cpp : Defines the entry point for the console application.
//
//**********************************************************
// Подключение стандартного файла с библиотекой OpenGL
#include "stdafx.h"
//**********************************************************
// Прототипы функций
void ExecuteMenu(int); // Контекстное меню первого уровня
void TimerFunc(int); // Обработчик события таймера
void SpecialKeys(int, int, int); // Обработка нажатия клавиш
void RenderScene(void); // Функция рендеринга
void ChangeSize(int, int); // Функция установки отсекающего объема
// Глобальная переменная выбранного варианта основного меню
int choice = 1;
// Глобальные переменные для создания вращения
// в градусах
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
GLfloat zRot = 0.0f;
GLint w, h; // Ширина и высота экрана
//**********************************************************
// Подключение файлов с упражнениями
#include "ColorCube.h" // Упражнение 1: "1) Куб цвета"
#include "Jet.h" // Упражнение 2: "2) Самолет без освещения"
#include "LightSphere.h" // Упражнение 3: "3) Рисование освещенной сферы"
#include "MultiMaterial.h" // Упражнение 4: "4) Рисование с разными материалами"
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
// Сбрасываем углы вращения прежнего варианта
xRot = yRot = zRot = 0;
// Выключаем освещение
glDisable(GL_LIGHTING);
// Запоминаем выбор в глобальную переменную
::choice = choice;
switch(::choice)
{
case 1:
ChangeSizeColorCube(w, h);
break;
case 2:
ChangeSizeJet(w, h);
break;
case 3:
ChangeSizeLightSphere(w, h);
break;
case 4:
ChangeSizeMultiMaterial(w, h);
break;
}
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
// Сохранить прежние настройки OpenGL в стеке атрибутов
glPushAttrib(GL_LIGHTING_BIT);
switch(::choice)
{
case 1:
RenderSceneColorCube();
break;
case 2:
RenderSceneJet();
break;
case 3:
RenderSceneLightSphere();
break;
case 4:
RenderSceneMultiMaterial();
break;
}
// Восстановить прежние настройки OpenGL из стека атрибутов
glPopAttrib();
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
void ChangeSize(int width, int height)
{
w = width;
h = height;
switch(::choice)
{
case 1:
ChangeSizeColorCube(width, height);
break;
case 2:
ChangeSizeJet(width, height);
break;
case 3:
ChangeSizeLightSphere(width, height);
break;
case 4:
ChangeSizeMultiMaterial(width, height);
break;
}
}
//**********************************************************
// Обработчик события таймера
void TimerFunc(int value)
{
glutPostRedisplay(); // Перерисовка сцены
glutTimerFunc(30, TimerFunc, 1); // Заряжаем новый таймер
}
//**********************************************************
// Управление с клавиатуры стрелками
// для задания новых значений матрицы поворота
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP) // Стрелка вверх
xRot -= 5.0f;
if(key == GLUT_KEY_DOWN)// Стрелка вниз
xRot += 5.0f;
if(key == GLUT_KEY_LEFT)// Стрелка влево
yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT)// Стрелка вправо
yRot += 5.0f;
xRot = (GLfloat)((const int)xRot % 360);
yRot = (GLfloat)((const int)yRot % 360);
// Вызвать принудительно визуализацию с помощью RenderScene()
glutPostRedisplay();
}
//**********************************************************
void main(int argc, char* argv[])
{
glutInit(argc, argv);
// Двойная буферизация, цветовая модель RGB, буфер глубины
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(300, 300); // Начальные размеры окна
glutCreateWindow("Свет и материалы"); // Заголовок окна
glutDisplayFunc(RenderScene); // Обновление сцены при разрушении окна
glutReshapeFunc(ChangeSize); // При изменении размера окна
glutTimerFunc(100, TimerFunc, 1); // Создали таймер
glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("1) Куб цвета", 1);
glutAddMenuEntry("2) Самолет без освещения", 2);
glutAddMenuEntry("3) Рисование освещенной сферы", 3);
glutAddMenuEntry("4) Рисование с разными материалами", 4);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutMainLoop(); // Цикл сообщений графического окна
}
Запустите
приложение и получите такой результат
Попытайтесь
разобраться с кодом!!!Обратите внимание, что в функции RenderScene() мы применили
механизм сохранения настроек в стеке атрибутов с последующим
их восстановлением, чтобы изменения настроек в одном упражнении
не сбивали настройки для выполнения другого упражнения нашего
приложения. Для сохранения атрибутов используются функции
void glPushAttrib(GLbitfield mask); void glPopAttrib();
Атрибуты
сохраняются группами, определяемыми значением аргумента .
Эти значения приведены ниже в таблице. Минимальная глубина
стека равна 16. Группы атрибутов можно
объединять поразрядной логической операцией OR (|), что позволяет
сохранять в стеке практически любую комбинацию атрибутов.
Функция glPopAttrib() восстанавливает
значения параметров состояния, сохраненных последним вызовом
функции glPushAttrib().
Каждый бит значения маски соответствует определенному параметру
состояния
Например, используемый нами параметр GL_LIGHTING_BIT объединяет
все параметры состояния glPopAttrib() все
эти настройки восстанавливаются.
Маска |
Группа атрибутов |
|---|---|
GL_ACCUM_BUFFER_BIT |
Буфер накопления |
GL_ALL_ATTRIB_BITS |
Предназначена для сохранения всех параметров состояния
во всех наборах атрибутах (все настройки |
GL_COLOR_BUFFER_BIT |
Буфер цвета |
GL_CURRENT_BIT |
Текущий |
GL_DEPTH_BUFFER_BIT |
Буфер глубины |
GL_ENABLE_BIT |
Активация |
GL_EVAL_BIT |
Оценка |
GL_FOG_BIT |
Туман |
GL_HINT_BIT |
Подсказки |
GL_LIGHTING_BIT |
Освещение |
GL_LINE_BIT |
Линия |
GL_LIST_BIT |
Список |
GL_MULTISAMPLE_BIT |
Множественная выборка |
GL_PIXEL_MODE_BIT |
Пиксел |
GL_POINT_BIT |
Точка |
GL_POLYGON_BIT |
Многоугольник |
GL_POLYGON_STIPPLE_BIT |
Фактурный многоугольник |
GL_SCISSOR_BIT |
|
GL_STENCIL_BUFFER_BIT |
Буфер трафарета |
GL_TEXTURE_BIT |
|
GL_TRANSFORM_BIT |
Деформация |
GL_VIEWPORT_BIT |
Точка наблюдения |
Другой, упрощенный, но более быстрый способ задания свойств
многих материалов, называется согласованием
цветов. Он заключается в использовании
для изменения свойств материала функции glColorMaterial(),
которая имеет следующий синтаксис:
void glColorMaterial(GLenum face, GLenum mode);
Эта функция заставляет использовать для рисования освещенных
объектов текущий цвет рисования, установленный функцией glColor*() для
рисования неосвещенных объектов. При изменении текущего цвета
(командой glColor*() )
указанные свойства материала также незамедлительно меняются.
Аргумент определяет,
к какой стороне объекта применять задаваемое свойство материала,
и может принимать значения: GL_FRONT, GL_BACK или GL_FRONT_AND_BACK (по
умолчанию). Аргумент mode выбирается
из списка перечислений: GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, GL_AMBIENT_AND_DIFFUSE (по
умолчанию) или GL_EMISSION.
В каждый момент времени активным является только один из режимов.
Функция glColorMaterial() не
работает при использовании освещения в режиме индексирования
цветов, а только в режиме
Для того, чтобы включить в glEnable(GL_COLOR_MATERIAL).
Это позволяет изменять текущий цвет рисования объекта командой glColor*(),
но не отменяет возможности одновременного использования команды glMaterial*() с
нужными параметрами для настройки
нужных свойств материала иным способом. После того, как потребность
в использовании режима согласования цветов отпадет, нужно обязательно
отключить его командой glDisable(GL_COLOR_MATERIAL).
Это позволит избежать ошибочных изменений свойств материала,
а также снизить временные затраты на выполнение программы.
Функцию glColorMaterial() выгодно
использовать в том случае, когда для большинства вершин на
сцене требуется подстраивать значение лишь одного параметра
материала. Если изменяемых параметров несколько, то нужно применять
функцию glMaterial*().
Проиллюстрируем сказанное на примере моделирования сферы с изменяющимся цветом материала. Для этого воспользуемся кодом файла LightSphere.h рисования освещенной сферы из ранее выполненного упражнения.
Добавьте
к приложению новый
Скопируйте
в новый файл содержимое из файла LightSphere.h,
модифицируйте его, чтобы код окончательно выглядел так// ColorSphere.h
// Упражнение 5: "5) Способ согласования цветов"
//**********************************************************
// Прототипы
void SetLightColorSphere();
void RenderSceneColorSphere();
void ChangeSizeColorSphere(int, int);
// Глобальный массив свойств материала
GLfloat diffuseMaterial[] = {0.5, 0.5, 0.5, 1.0};
GLfloat mat_shininess[] = {25.0}; // Размер блика
void SetLightColorSphere()
{
GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};// Цвет блика
GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0};// Расположение источника
glClearColor(0.0, 0.0, 0.0, 1.0);
glShadeModel(GL_SMOOTH);
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuseMaterial);// Начальная установка диффузного материала
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
// Режим согласования цветов назначаем для рассеянного
glColorMaterial(GL_FRONT, GL_DIFFUSE);
// Включаем поддержку согласования цветов
glEnable(GL_COLOR_MATERIAL);
}
//**********************************************************
void RenderSceneColorSphere()
{
// Сбрасываем буфер цвета и буфер глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();// Сохранить матрицу преобразования модели
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
glRotatef(zRot, 0.0f, 0.0f, 1.0f);
// Для анимации
xRot += 1.0f;
yRot += 1.0f;
zRot += 1.0f;
xRot = (GLfloat)((const int)xRot % 360);
yRot = (GLfloat)((const int)yRot % 360);
zRot = (GLfloat)((const int)zRot % 360);
// Рисуем сферу
glutSolidSphere(1.0, 20, 16);
glPopMatrix();// Восстановить матрицу преобразования модели
// Переключить буфер
glutSwapBuffers();
// Прокачка сообщений
glFlush();
}
//**********************************************************
void ChangeSizeColorSphere(int width, int height)
{
// Индивидуальные настройки освещения
SetLightColorSphere();
// Предотвращаем деление на нуль
if(height == 0)
height = 1;
// Устанавливаем поле просмотра с размерами окна
glViewport(0, 0, width, height);
// Устанавливает матрицу преобразования в режим проецирования
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Устанавливаем размеры ортогонального отсекающего объема
GLfloat aspectRatio = (GLfloat)width / (GLfloat)height;// Для соблюдения пропорций
if (width <= height)
glOrtho (-1.5, 1.5, -1.5 / aspectRatio, 1.5 / aspectRatio, -10.0, 10.0);
else
glOrtho (-1.5 * aspectRatio, 1.5 * aspectRatio, -1.5, 1.5, -10, 10);
// Восстановливает матрицу преобразования в исходный режим вида
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
Добавьте
к файлу LightAndMaterial.cpp возможность запуска пятого упражнения ColorSphere// LightAndMaterial.cpp : Defines the entry point for the console application.
//
//**********************************************************
// Подключение стандартного файла с библиотекой OpenGL
#include "stdafx.h"
//**********************************************************
// Прототипы функций
void ExecuteMenu(int); // Контекстное меню первого уровня
void TimerFunc(int); // Обработчик события таймера
void SpecialKeys(int, int, int); // Обработка нажатия клавиш
void RenderScene(void); // Функция рендеринга
void ChangeSize(int, int); // Функция установки отсекающего объема
// Глобальная переменная выбранного варианта основного меню
int choice = 1;
// Глобальные переменные для создания вращения
// в градусах
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
GLfloat zRot = 0.0f;
GLint w, h; // Ширина и высота экрана
//**********************************************************
// Подключение файлов с упражнениями
#include "ColorCube.h" // Упражнение 1: "1) Куб цвета"
#include "Jet.h" // Упражнение 2: "2) Самолет без освещения"
#include "LightSphere.h" // Упражнение 3: "3) Рисование освещенной сферы"
#include "MultiMaterial.h" // Упражнение 4: "4) Рисование с разными материалами"
#include "ColorSphere.h" // Упражнение 5: "5) Способ согласования цветов"
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
// Сбрасываем углы вращения прежнего варианта
xRot = yRot = zRot = 0;
// Выключаем освещение
glDisable(GL_LIGHTING);
// Выключаем режим согласования цветов
glDisable(GL_COLOR_MATERIAL);
// Запоминаем выбор в глобальную переменную
::choice = choice;
switch(::choice)
{
case 1:
ChangeSizeColorCube(w, h);
break;
case 2:
ChangeSizeJet(w, h);
break;
case 3:
ChangeSizeLightSphere(w, h);
break;
case 4:
ChangeSizeMultiMaterial(w, h);
break;
case 5:
ChangeSizeColorSphere(w, h);
break;
}
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
// Сохранить прежние настройки OpenGL в стеке атрибутов
glPushAttrib(GL_LIGHTING_BIT);
switch(::choice)
{
case 1:
RenderSceneColorCube();
break;
case 2:
RenderSceneJet();
break;
case 3:
RenderSceneLightSphere();
break;
case 4:
RenderSceneMultiMaterial();
break;
case 5:
RenderSceneColorSphere();
break;
}
// Восстановить прежние настройки OpenGL из стека атрибутов
glPopAttrib();
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
void ChangeSize(int width, int height)
{
w = width;
h = height;
switch(::choice)
{
case 1:
ChangeSizeColorCube(width, height);
break;
case 2:
ChangeSizeJet(width, height);
break;
case 3:
ChangeSizeLightSphere(width, height);
break;
case 4:
ChangeSizeMultiMaterial(width, height);
break;
case 5:
ChangeSizeColorSphere(width, height);
break;
}
}
//**********************************************************
// Обработчик события таймера
void TimerFunc(int value)
{
glutPostRedisplay(); // Перерисовка сцены
glutTimerFunc(30, TimerFunc, 1); // Заряжаем новый таймер
}
//**********************************************************
// Управление с клавиатуры стрелками
// для задания новых значений матрицы поворота
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP) // Стрелка вверх
xRot -= 5.0f;
if(key == GLUT_KEY_DOWN)// Стрелка вниз
xRot += 5.0f;
if(key == GLUT_KEY_LEFT)// Стрелка влево
yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT)// Стрелка вправо
yRot += 5.0f;
xRot = (GLfloat)((const int)xRot % 360);
yRot = (GLfloat)((const int)yRot % 360);
// Для упражнения 5 смены материала
if(key == GLUT_KEY_F1) // Меняем красный
{
diffuseMaterial[0] += 0.1;
*diffuseMaterial = *diffuseMaterial > 1.0 ? 0.0 : *diffuseMaterial;
glColor4fv(diffuseMaterial);
}
if(key == GLUT_KEY_F2) // Меняем зеленый
{
diffuseMaterial[1] += 0.1;
*(diffuseMaterial + 1) = diffuseMaterial[1] > 1.0 ? 0.0 : diffuseMaterial[1];
glColor4fv(diffuseMaterial);
}
if(key == GLUT_KEY_F3) // Меняем синий
{
diffuseMaterial[2] += 0.1;
*(diffuseMaterial + 2) = diffuseMaterial[2] > 1.0 ? 0.0 : diffuseMaterial[2];
glColor4fv(diffuseMaterial);
}
// Вызвать принудительно визуализацию с помощью RenderScene()
glutPostRedisplay();
}
//**********************************************************
void main(int argc, char* argv[])
{
glutInit(argc, argv);
// Двойная буферизация, цветовая модель RGB, буфер глубины
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(300, 300); // Начальные размеры окна
glutCreateWindow("Свет и материалы"); // Заголовок окна
glutDisplayFunc(RenderScene); // Обновление сцены при разрушении окна
glutReshapeFunc(ChangeSize); // При изменении размера окна
glutTimerFunc(100, TimerFunc, 1); // Создали таймер
glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("1) Куб цвета", 1);
glutAddMenuEntry("2) Самолет без освещения", 2);
glutAddMenuEntry("3) Рисование освещенной сферы", 3);
glutAddMenuEntry("4) Рисование с разными материалами", 4);
glutAddMenuEntry("5) Способ согласования цветов (F1, F2, F3)", 5);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutMainLoop(); // Цикл сообщений графического окна
}
Обратите внимание, что мы ввели возможность изменения рассеянного цвета материала прямо по ходу выполнения приложения с помощью функциональных клавиш: F1 - Red, F2 - Green, F3 - Blue.
Запустите
приложение и выберите пятое упражнение. Если менять цвета
закрепленными функциональными клавишами, то будет меняться
цвет рассеянного отражения. Картина будет примерно такой
Попытайтесь
разобраться с кодомЗдесь требуется внесение небольших изменений в третье упражнение в связи с вновь открывшимися обстоятельствами. Если вы после упражнения 5 сразу запустите упражнение 3, то диффузный цвет сферы в упражнении 3 будет таким же, как для упражнения 5. Это происходит потому, что мы в упражнении 3 не задаем явно цвет диффузной составляющей материала, поэтому этот цвет имеет текущие настройки, измененные в упражнении 5.
Эх!, подпреснуть бы вас, дорогие граждане студенты, пусть бы сами искали исправление, да ладно! - главное, чтобы внимательно разобрались с кодом и все бы кругом по-обсмыслили (конечно, - по желанию).
Добавьте
в функцию SetLight() файла LightSphere.h только
одну строку кода, явно определяющую свойство диффузной
составляющей отражения.void SetLight()
{
GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};// Цвет блика
GLfloat mat_shininess[] = {50.0}; // Размер блика (обратная пропорция)
GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0};// Расположение источника
GLfloat white_light[] = {1.0, 1.0, 1.0, 1.0};// Цвет и интенсивность
// освещения, генерируемого источником
GLfloat lmodel_ambient[] = {0.5, 0.5, 0.5, 1.0};// Параметры фонового освещения
glClearColor(0.0, 0.0, 0.0, 1.0);
glShadeModel(GL_SMOOTH);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glMaterialfv(GL_FRONT, GL_DIFFUSE, lmodel_ambient);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
}
Попереключайтесь
с третьего упражнения на пятое и наоборот. Все восстанавливается
индивидуально для каждого упражнения.И еще одно замечание. Попробуйте, для сравнения, выключить
в любом из упражнений, начиная со второго, в функции SetLight*() освещение
сцены, для чего закомментрируйте строку кода
glEnable(GL_LIGHTING);
Мы сразу же получим плоский (постный) цвет без всяких эффектов освещения, что делает сцену совершенно убогой. Например, сравните два рисунка - наверное есть за что бороться!
![]() |
![]() |
Дошла очередь и до рассмотрения источников света. Источники
света характеризуются набором свойств, включая цвет, яркость,
положение, ориентацию и т.д., которые могут очень сильно влиять
на вид сцены. Свойства источников света, в нашем примере, устанавливаются
функцией glLightfv(),
которая вызывается несколько раз для настройки
свойств источника, но каждый раз с разными аргументами настройки.
В общем случае создания и настройки источников света имеет
вид
void glLight{if}(GLenum light, GLenum pname, TYPE param);
void glLight{if}v(GLenum light, GLenum pname, TYPE *param);
Параметр имеет
значения GL_LIGHT0,
..., GL_LIGHT7,
которые определяют номерное имя создаваемого источника. Настраиваемое
свойство указывается одним из значений перечисления pname,
приведенных в таблице ниже. Аргумент -
это собственно значение настраиваемого свойства pname или
указатель на набор значений (в векторной версии).
Значение параметра pname |
Значение по умолчанию | Описание |
|---|---|---|
GL_AMBIENT |
(0.0, 0.0, 0.0, 1.0) - отключено по умолчанию для всех источников | Цвет фонового освещения |
GL_DIFFUSE |
(1.0, 1.0, 1.0, 1.0) для LIGHT0 белый или (0.0, 0.0, 0.0, 1.0) - нет света для остальных источников |
Цвет рассеянного освещения |
GL_SPECULAR |
(1.0, 1.0, 1.0, 1.0) для LIGHT0 белый или (0.0, 0.0, 0.0, 1.0) - нет света для остальных источников |
Цвет отраженного освещения |
GL_POSITION |
(0.0, 0.0, 1.0, 0.0) | Координаты (x, y, z, w) источника света |
GL_SPOT_DIRECTION |
(0.0, 0.0, -1.0) | Направление (x, y, z, w) распространения света |
GL_SPOT_EXPONENT |
0.0 | Распределение интенсивности светового пучка
по |
GL_SPOT_CUTOFF |
180.0 | Угол разброса световых лучей |
GL_CONSTANT_ATTENUATION |
1.0 | Ослабление интенсивности света вне зависимости от направления и расстояния |
GL_LINEAR_ATTENUATION |
0.0 | Линейное ослабление интенсивности света |
GL_QUADRATIC_ATTENUATION |
0.0 | Квадратичное затухание света |
Но всегда следует помнить, что добавление каждого
нового источника света и наделение его всякими (зачастую,
не нужными) свойствами требует значительных вычислений при
рисовании, т.е. существенно сказывается на производительности
программы. Как видно из таблицы, GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR.
Для того, чтобы использовать источники света, системе необходима
дополнительная информация о нормалях к вершинам поверхностей.
До сих пор мы эти
glutSolidSphere(1.0, 20, 16);
Под нормалью к вершине поверхности подразумевается
После создания и настройки источников света их необходимо
включить с помощью команды glEnable(GL_LIGHTING),
тем самым сообщив
Приведем пример, когда
В панели
Solution Explorer добавьте к проекту новый 
Скопируйте
в новый файл JetNight.h содержимое
файла Jet.h
Добавьте
в коде файла JetNight.h к именам функций
и их прототипам окончание Night,
чтобы не было конфликта имен в проекте приложения, например,
для прототипов
|
->
|
void JetNight() ; |
|
->
|
|
|
->
|
void ChangeSizeJetNight(int, int) ; |
Поместите
в самое начало функции ChangeSizeJetNight() код
включения освещения// Индивидуальные настройки освещения glEnable(GL_LIGHTING);
Добавьте
в файл LightAndMaterial.cpp код, подключающий
новое упражнение// LightAndMaterial.cpp : Defines the entry point for the console application.
//
//**********************************************************
// Подключение стандартного файла с библиотекой OpenGL
#include "stdafx.h"
//**********************************************************
// Прототипы функций
void ExecuteMenu(int); // Контекстное меню первого уровня
void TimerFunc(int); // Обработчик события таймера
void SpecialKeys(int, int, int); // Обработка нажатия клавиш
void RenderScene(void); // Функция рендеринга
void ChangeSize(int, int); // Функция установки отсекающего объема
// Глобальная переменная выбранного варианта основного меню
int choice = 1;
// Глобальные переменные для создания вращения
// в градусах
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
GLfloat zRot = 0.0f;
GLint w, h; // Ширина и высота экрана
//**********************************************************
// Подключение файлов с упражнениями
#include "ColorCube.h" // Упражнение 1: "1) Куб цвета"
#include "Jet.h" // Упражнение 2: "2) Самолет без освещения"
#include "LightSphere.h" // Упражнение 3: "3) Рисование освещенной сферы"
#include "MultiMaterial.h" // Упражнение 4: "4) Рисование с разными материалами"
#include "ColorSphere.h" // Упражнение 5: "5) Способ согласования цветов"
#include "JetNight.h" // Упражнение 6: "6) Самолет с нулевым освещением"
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
// Сбрасываем углы вращения прежнего варианта
xRot = yRot = zRot = 0;
// Выключаем освещение
glDisable(GL_LIGHTING);
// Выключаем режим согласования цветов
glDisable(GL_COLOR_MATERIAL);
// Запоминаем выбор в глобальную переменную
::choice = choice;
switch(::choice)
{
case 1:
ChangeSizeColorCube(w, h);
break;
case 2:
ChangeSizeJet(w, h);
break;
case 3:
ChangeSizeLightSphere(w, h);
break;
case 4:
ChangeSizeMultiMaterial(w, h);
break;
case 5:
ChangeSizeColorSphere(w, h);
break;
case 6:
ChangeSizeJetNight(w, h);
break;
}
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
// Сохранить прежние настройки OpenGL в стеке атрибутов
glPushAttrib(GL_LIGHTING_BIT);
switch(::choice)
{
case 1:
RenderSceneColorCube();
break;
case 2:
RenderSceneJet();
break;
case 3:
RenderSceneLightSphere();
break;
case 4:
RenderSceneMultiMaterial();
break;
case 5:
RenderSceneColorSphere();
break;
case 6:
RenderSceneJetNight();
break;
}
// Восстановить прежние настройки OpenGL из стека атрибутов
glPopAttrib();
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
void ChangeSize(int width, int height)
{
w = width;
h = height;
switch(::choice)
{
case 1:
ChangeSizeColorCube(width, height);
break;
case 2:
ChangeSizeJet(width, height);
break;
case 3:
ChangeSizeLightSphere(width, height);
break;
case 4:
ChangeSizeMultiMaterial(width, height);
break;
case 5:
ChangeSizeColorSphere(width, height);
break;
case 6:
ChangeSizeJetNight(width, height);
break;
}
}
//**********************************************************
// Обработчик события таймера
void TimerFunc(int value)
{
glutPostRedisplay(); // Перерисовка сцены
glutTimerFunc(30, TimerFunc, 1); // Заряжаем новый таймер
}
//**********************************************************
// Управление с клавиатуры стрелками
// для задания новых значений матрицы поворота
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP) // Стрелка вверх
xRot -= 5.0f;
if(key == GLUT_KEY_DOWN)// Стрелка вниз
xRot += 5.0f;
if(key == GLUT_KEY_LEFT)// Стрелка влево
yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT)// Стрелка вправо
yRot += 5.0f;
xRot = (GLfloat)((const int)xRot % 360);
yRot = (GLfloat)((const int)yRot % 360);
// Для упражнения 5 смены материала
if(key == GLUT_KEY_F1) // Меняем красный
{
diffuseMaterial[0] += 0.1;
*diffuseMaterial = *diffuseMaterial > 1.0 ? 0.0 : *diffuseMaterial;
glColor4fv(diffuseMaterial);
}
if(key == GLUT_KEY_F2) // Меняем зеленый
{
diffuseMaterial[1] += 0.1;
*(diffuseMaterial + 1) = diffuseMaterial[1] > 1.0 ? 0.0 : diffuseMaterial[1];
glColor4fv(diffuseMaterial);
}
if(key == GLUT_KEY_F3) // Меняем синий
{
diffuseMaterial[2] += 0.1;
*(diffuseMaterial + 2) = diffuseMaterial[2] > 1.0 ? 0.0 : diffuseMaterial[2];
glColor4fv(diffuseMaterial);
}
// Вызвать принудительно визуализацию с помощью RenderScene()
glutPostRedisplay();
}
//**********************************************************
void main(int argc, char* argv[])
{
glutInit(argc, argv);
// Двойная буферизация, цветовая модель RGB, буфер глубины
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(300, 300); // Начальные размеры окна
glutCreateWindow("Свет и материалы"); // Заголовок окна
glutDisplayFunc(RenderScene); // Обновление сцены при разрушении окна
glutReshapeFunc(ChangeSize); // При изменении размера окна
glutTimerFunc(100, TimerFunc, 1); // Создали таймер
glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("1) Куб цвета", 1);
glutAddMenuEntry("2) Самолет без освещения", 2);
glutAddMenuEntry("3) Рисование освещенной сферы", 3);
glutAddMenuEntry("4) Рисование с разными материалами", 4);
glutAddMenuEntry("5) Способ согласования цветов (F1, F2, F3)", 5);
glutAddMenuEntry("6) Самолет с нулевым освещением", 6);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutMainLoop(); // Цикл сообщений графического окна
}
Запустите
приложение и выполните упражнение 6, результат должен быть
примерно таким, как на первом рисунке
С нулевым освещением (упражнение 6) |
Без применения освещения (упражнение 2) |
Обратите внимание, что объект остается абсолютно неосвещенным
только тогда, когда мы вызываем упражнение 6 первым после запуска
приложения. После выполнения других упражнений, где задаются
источники света и материалы, мы уже не получим абсолютно неосвещенного
объекта в упражнении 6, поскольку
Вернемся к нормалям.
Для
упражнения 7 добавьте к проекту новый 
Заполните
файл следующим кодом// Упражнение 7: "7) Самолет с равномерным освещением"
#include <math.h>
//**********************************************************
// Прототипы
void JetLight();
void SetLightJetLight();
void RenderSceneJetLight(void);
void ChangeSizeJetLight(int, int); // При изменении размеров окна
//**********************************************************
// Вспомогательный код
typedef GLfloat GLTVector3[3]; // Новый тип - вектор
void gltGetNormalVector(const GLTVector3 vP1, const GLTVector3 vP2, const GLTVector3 vP3, GLTVector3 vNormal);
void gltSubtractVectors(const GLTVector3 vFirst, const GLTVector3 vSecond, GLTVector3 vResult);
void gltVectorCrossProduct(const GLTVector3 vU, const GLTVector3 vV, GLTVector3 vResult);
void gltNormalizeVector(GLTVector3 vNormal);
GLfloat gltGetVectorLength(const GLTVector3 vVector);
GLfloat gltGetVectorLengthSqrd(const GLTVector3 vVector);
void gltScaleVector(GLTVector3 vVector, const GLfloat fScale);
//**********************************************************
// Рисовать самолет
void JetLight()
{
GLTVector3 vNormal; // Для вектора нормали
// Очищаем буфер цвета и глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Сохранить матрицу преобразования и выполнить
// последовательность поворотов
glPushMatrix();
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
// Устанавливаем цвет материала серый для всех граней самолета
glColor3ub(128, 128, 128);
// Рисуем самолет
glBegin(GL_TRIANGLES);
// Конус носа /////////////////////////////
glNormal3f(0.0f, -1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 60.0f);
glVertex3f(-15.0f, 0.0f, 30.0f);
glVertex3f(15.0f,0.0f,30.0f);
{// Блок кода
GLTVector3 vPoints[3] = {{ 15.0f, 0.0f, 30.0f},
{ 0.0f, 15.0f, 30.0f},
{ 0.0f, 0.0f, 60.0f}};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{// Блок кода
GLTVector3 vPoints[3] = {{ 0.0f, 0.0f, 60.0f },
{ 0.0f, 15.0f, 30.0f },
{ -15.0f, 0.0f, 30.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
// Тело самолета //////////////////////////
{
GLTVector3 vPoints[3] = {{ -15.0f, 0.0f, 30.0f },
{ 0.0f, 15.0f, 30.0f },
{ 0.0f, 0.0f, -56.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{ 0.0f, 0.0f, -56.0f },
{ 0.0f, 15.0f, 30.0f },
{ 15.0f,0.0f,30.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
glNormal3f(0.0f, -1.0f, 0.0f);
glVertex3f(15.0f,0.0f,30.0f);
glVertex3f(-15.0f, 0.0f, 30.0f);
glVertex3f(0.0f, 0.0f, -56.0f);
///////////////////////////////////////////
// Левое крыло
// Большой треугольник для основания крыла
{
GLTVector3 vPoints[3] = {{ 0.0f,2.0f,27.0f },
{ -60.0f, 2.0f, -8.0f },
{ 60.0f, 2.0f, -8.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{ 60.0f, 2.0f, -8.0f},
{0.0f, 7.0f, -8.0f},
{0.0f,2.0f,27.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{60.0f, 2.0f, -8.0f},
{-60.0f, 2.0f, -8.0f},
{0.0f,7.0f,-8.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
// Другое крыло верхней секции
{
GLTVector3 vPoints[3] = {{0.0f,2.0f,27.0f},
{0.0f, 7.0f, -8.0f},
{-60.0f, 2.0f, -8.0f}};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
///////////////////////////////////////////
// Хвост
glNormal3f(0.0f, -1.0f, 0.0f);
glVertex3f(-30.0f, -0.50f, -57.0f);
glVertex3f(30.0f, -0.50f, -57.0f);
glVertex3f(0.0f,-0.50f,-40.0f);
// Верхняя левая сторона
{
GLTVector3 vPoints[3] = {{ 0.0f,-0.5f,-40.0f },
{30.0f, -0.5f, -57.0f},
{0.0f, 4.0f, -57.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
// Верхняя правая сторона
{
GLTVector3 vPoints[3] = {{ 0.0f, 4.0f, -57.0f },
{ -30.0f, -0.5f, -57.0f },
{ 0.0f,-0.5f,-40.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
// Оборотная нижняя часть
{
GLTVector3 vPoints[3] = {{ 30.0f,-0.5f,-57.0f },
{ -30.0f, -0.5f, -57.0f },
{ 0.0f, 4.0f, -57.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{ 0.0f,0.5f,-40.0f },
{ 3.0f, 0.5f, -57.0f },
{ 0.0f, 25.0f, -65.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{ 0.0f, 25.0f, -65.0f },
{ -3.0f, 0.5f, -57.0f},
{ 0.0f,0.5f,-40.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{ 3.0f,0.5f,-57.0f },
{ -3.0f, 0.5f, -57.0f },
{ 0.0f, 25.0f, -65.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
glEnd(); // О самолете
// Восстановить матрицу
glPopMatrix();
// Переключить буфер
glutSwapBuffers();
}
//**********************************************************
// Задать параметры освещения и согласования цветов материала
void SetLightJetLight()
{
GLfloat ambientLight[] = { 0.3f, 0.3f, 0.3f, 1.0f };// Фоновый слабобелый
GLfloat diffuseLight[] = { 0.7f, 0.7f, 0.7f, 1.0f };// Рассеяный среднебелый
GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0};// Расположение источника
glShadeModel(GL_SMOOTH);
GLfloat diffuseMaterial[] = {0.5, 0.5, 0.5, 1.0};
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuseMaterial);// Начальное значение диффузного материала
glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight);
glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
// Режим согласования цветов назначаем для фонового и рассеянного
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
// Включаем поддержку согласования цветов
glEnable(GL_COLOR_MATERIAL);
}
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderSceneJetLight(void)
{
// Сбрасываем буфер цвета и буфер глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Рисуем сцену
JetLight();
// Прокачка сообщений
glFlush();
}
//**********************************************************
void ChangeSizeJetLight(int width, int height)
{
// Устанавливаем индивидуальные настройки освещения
SetLightJetLight();
glEnable(GL_DEPTH_TEST); // Включить тест глубины
glEnable(GL_CULL_FACE); // Отображать только лицевую сторону
glFrontFace(GL_CCW); // Считать лицевым обход против часовой стрелки
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);// Цвет фона окна
// Предотвращаем деление на нуль
if(height == 0)
height = 1;
// Устанавливаем поле просмотра с размерами окна
glViewport(0, 0, width, height);
// Устанавливает матрицу преобразования в режим проецирования
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Устанавливаем размеры отсекающего объема перспективы
GLfloat aspectRatio = (GLfloat)width / (GLfloat)height; // Для соблюдения пропорций
gluPerspective(45.0f, aspectRatio, 1.0f, 250.0f); // Отсекающая перспектива
// Восстановливает матрицу преобразования в исходный режим вида
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Отодвинем сцену в отрицательную сторону оси 0z
glTranslatef(0.0f, 0.0f, -180.0f);
}
//**********************************************************
// Вспомогательный код
//**********************************************************
void gltGetNormalVector(const GLTVector3 vP1, const GLTVector3 vP2, const GLTVector3 vP3, GLTVector3 vNormal)
{
GLTVector3 vV1, vV2;
gltSubtractVectors(vP2, vP1, vV1);
gltSubtractVectors(vP3, vP1, vV2);
gltVectorCrossProduct(vV1, vV2, vNormal);
gltNormalizeVector(vNormal);
}
//**********************************************************
void gltSubtractVectors(const GLTVector3 vFirst, const GLTVector3 vSecond, GLTVector3 vResult)
{
vResult[0] = vFirst[0] - vSecond[0];
vResult[1] = vFirst[1] - vSecond[1];
vResult[2] = vFirst[2] - vSecond[2];
}
//**********************************************************
void gltVectorCrossProduct(const GLTVector3 vU, const GLTVector3 vV, GLTVector3 vResult)
{
vResult[0] = vU[1]*vV[2] - vV[1]*vU[2];
vResult[1] = -vU[0]*vV[2] + vV[0]*vU[2];
vResult[2] = vU[0]*vV[1] - vV[0]*vU[1];
}
//**********************************************************
void gltNormalizeVector(GLTVector3 vNormal)
{
GLfloat fLength = 1.0f / gltGetVectorLength(vNormal);
gltScaleVector(vNormal, fLength);
}
//**********************************************************
GLfloat gltGetVectorLength(const GLTVector3 vVector)
{
return (GLfloat)sqrt(gltGetVectorLengthSqrd(vVector));
}
//**********************************************************
GLfloat gltGetVectorLengthSqrd(const GLTVector3 vVector)
{
return (vVector[0]*vVector[0]) + (vVector[1]*vVector[1]) + (vVector[2]*vVector[2]);
}
//**********************************************************
void gltScaleVector(GLTVector3 vVector, const GLfloat fScale)
{
vVector[0] *= fScale; vVector[1] *= fScale; vVector[2] *= fScale;
}
Добавьте
в файл LightAndMaterial.cpp код, подключающий
новое упражнение// LightAndMaterial.cpp : Defines the entry point for the console application.
//
//**********************************************************
// Подключение стандартного файла с библиотекой OpenGL
#include "stdafx.h"
//**********************************************************
// Прототипы функций
void ExecuteMenu(int); // Контекстное меню первого уровня
void TimerFunc(int); // Обработчик события таймера
void SpecialKeys(int, int, int); // Обработка нажатия клавиш
void RenderScene(void); // Функция рендеринга
void ChangeSize(int, int); // Функция установки отсекающего объема
// Глобальная переменная выбранного варианта основного меню
int choice = 1;
// Глобальные переменные для создания вращения
// в градусах
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
GLfloat zRot = 0.0f;
GLint w, h; // Ширина и высота экрана
//**********************************************************
// Подключение файлов с упражнениями
#include "ColorCube.h" // Упражнение 1: "1) Куб цвета"
#include "Jet.h" // Упражнение 2: "2) Самолет без освещения"
#include "LightSphere.h" // Упражнение 3: "3) Рисование освещенной сферы"
#include "MultiMaterial.h" // Упражнение 4: "4) Рисование с разными материалами"
#include "ColorSphere.h" // Упражнение 5: "5) Способ согласования цветов"
#include "JetNight.h" // Упражнение 6: "6) Самолет с нулевым освещением"
#include "JetLight.h" // Упражнение 7: "7) Самолет с равномерным освещением"
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
// Сбрасываем углы вращения прежнего варианта
xRot = yRot = zRot = 0;
// Выключаем освещение
glDisable(GL_LIGHTING);
// Выключаем режим согласования цветов
glDisable(GL_COLOR_MATERIAL);
// Запоминаем выбор в глобальную переменную
::choice = choice;
switch(::choice)
{
case 1:
ChangeSizeColorCube(w, h);
break;
case 2:
ChangeSizeJet(w, h);
break;
case 3:
ChangeSizeLightSphere(w, h);
break;
case 4:
ChangeSizeMultiMaterial(w, h);
break;
case 5:
ChangeSizeColorSphere(w, h);
break;
case 6:
ChangeSizeJetNight(w, h);
break;
case 7:
ChangeSizeJetLight(w, h);
break;
}
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
// Сохранить прежние настройки OpenGL в стеке атрибутов
glPushAttrib(GL_LIGHTING_BIT);
switch(::choice)
{
case 1:
RenderSceneColorCube();
break;
case 2:
RenderSceneJet();
break;
case 3:
RenderSceneLightSphere();
break;
case 4:
RenderSceneMultiMaterial();
break;
case 5:
RenderSceneColorSphere();
break;
case 6:
RenderSceneJetNight();
break;
case 7:
RenderSceneJetLight();
break;
}
// Восстановить прежние настройки OpenGL из стека атрибутов
glPopAttrib();
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
void ChangeSize(int width, int height)
{
w = width;
h = height;
switch(::choice)
{
case 1:
ChangeSizeColorCube(width, height);
break;
case 2:
ChangeSizeJet(width, height);
break;
case 3:
ChangeSizeLightSphere(width, height);
break;
case 4:
ChangeSizeMultiMaterial(width, height);
break;
case 5:
ChangeSizeColorSphere(width, height);
break;
case 6:
ChangeSizeJetNight(width, height);
break;
case 7:
ChangeSizeJetLight(width, height);
break;
}
}
//**********************************************************
// Обработчик события таймера
void TimerFunc(int value)
{
glutPostRedisplay(); // Перерисовка сцены
glutTimerFunc(30, TimerFunc, 1); // Заряжаем новый таймер
}
//**********************************************************
// Управление с клавиатуры стрелками
// для задания новых значений матрицы поворота
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP) // Стрелка вверх
xRot -= 5.0f;
if(key == GLUT_KEY_DOWN)// Стрелка вниз
xRot += 5.0f;
if(key == GLUT_KEY_LEFT)// Стрелка влево
yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT)// Стрелка вправо
yRot += 5.0f;
xRot = (GLfloat)((const int)xRot % 360);
yRot = (GLfloat)((const int)yRot % 360);
// Для упражнения 5 смены материала
if(key == GLUT_KEY_F1) // Меняем красный
{
diffuseMaterial[0] += 0.1;
*diffuseMaterial = *diffuseMaterial > 1.0 ? 0.0 : *diffuseMaterial;
glColor4fv(diffuseMaterial);
}
if(key == GLUT_KEY_F2) // Меняем зеленый
{
diffuseMaterial[1] += 0.1;
*(diffuseMaterial + 1) = diffuseMaterial[1] > 1.0 ? 0.0 : diffuseMaterial[1];
glColor4fv(diffuseMaterial);
}
if(key == GLUT_KEY_F3) // Меняем синий
{
diffuseMaterial[2] += 0.1;
*(diffuseMaterial + 2) = diffuseMaterial[2] > 1.0 ? 0.0 : diffuseMaterial[2];
glColor4fv(diffuseMaterial);
}
// Вызвать принудительно визуализацию с помощью RenderScene()
glutPostRedisplay();
}
//**********************************************************
void main(int argc, char* argv[])
{
glutInit(argc, argv);
// Двойная буферизация, цветовая модель RGB, буфер глубины
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(300, 300); // Начальные размеры окна
glutCreateWindow("Свет и материалы"); // Заголовок окна
glutDisplayFunc(RenderScene); // Обновление сцены при разрушении окна
glutReshapeFunc(ChangeSize); // При изменении размера окна
glutTimerFunc(100, TimerFunc, 1); // Создали таймер
glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("1) Куб цвета", 1);
glutAddMenuEntry("2) Самолет без освещения", 2);
glutAddMenuEntry("3) Рисование освещенной сферы", 3);
glutAddMenuEntry("4) Рисование с разными материалами", 4);
glutAddMenuEntry("5) Способ согласования цветов (F1, F2, F3)", 5);
glutAddMenuEntry("6) Самолет с нулевым освещением", 6);
glutAddMenuEntry("7) Самолет с равномерным освещением", 7);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutMainLoop(); // Цикл сообщений графического окна
}
Запустите
приложение и выполните упражнение 7, результат должен быть
примерно таким
Если поуправлять поворотами модели с помощью клавиш-стрелок,
то будет меняться цвет граней самолета по мере того, как
их
Предполагается, что поверхность самолета имеет некоторые зеркальные отражательные свойства и способна генерировать блики. Пусть эти блики будут равномерно распределены по всей поверхности граней самолета. Способность зеркального отражения мы введем, задав соответствующее свойство материала.
Добавьте
к проекту новый файл с именем JetShiny.h
С
копируйте в новый файл код из файла JetLight.h и
змените окончания всех функций и их прототипов с JetLight на JetShiny
Удалите
весь код, помеченный как вспомогательный, вместе с прототипами
Добавьте
в конец функции SetLightJetShiny() код установки отражательных
свойств поверхности самолетаvoid SetLightJetShiny()
{
GLfloat ambientLight[] = { 0.3f, 0.3f, 0.3f, 1.0f };// Фоновый слабобелый
GLfloat diffuseLight[] = { 0.7f, 0.7f, 0.7f, 1.0f };// Рассеяный среднебелый
GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0};// Расположение источника
glShadeModel(GL_SMOOTH);
GLfloat diffuseMaterial[] = {0.5, 0.5, 0.5, 1.0};
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuseMaterial);// Начальное значение диффузного материала
glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight);
glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
// Режим согласования цветов назначаем для фонового и рассеянного
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
// Включаем поддержку согласования цветов
glEnable(GL_COLOR_MATERIAL);
// Задание свойства зеркального отражения
GLfloat specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glMateriali(GL_FRONT, GL_SHININESS, 50);
}
Добавьте
в файл LightAndMaterial.cpp код, подключающий
новое упражнение// LightAndMaterial.cpp : Defines the entry point for the console application.
//
//**********************************************************
// Подключение стандартного файла с библиотекой OpenGL
#include "stdafx.h"
//**********************************************************
// Прототипы функций
void ExecuteMenu(int); // Контекстное меню первого уровня
void TimerFunc(int); // Обработчик события таймера
void SpecialKeys(int, int, int); // Обработка нажатия клавиш
void RenderScene(void); // Функция рендеринга
void ChangeSize(int, int); // Функция установки отсекающего объема
// Глобальная переменная выбранного варианта основного меню
int choice = 1;
// Глобальные переменные для создания вращения
// в градусах
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
GLfloat zRot = 0.0f;
GLint w, h; // Ширина и высота экрана
//**********************************************************
// Подключение файлов с упражнениями
#include "ColorCube.h" // Упражнение 1: "1) Куб цвета"
#include "Jet.h" // Упражнение 2: "2) Самолет без освещения"
#include "LightSphere.h" // Упражнение 3: "3) Рисование освещенной сферы"
#include "MultiMaterial.h" // Упражнение 4: "4) Рисование с разными материалами"
#include "ColorSphere.h" // Упражнение 5: "5) Способ согласования цветов"
#include "JetNight.h" // Упражнение 6: "6) Самолет с нулевым освещением"
#include "JetLight.h" // Упражнение 7: "7) Самолет с равномерным освещением"
#include "JetShiny.h" // Упражнение 8: "8) Самолет с зеркальным отражением"
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
// Сбрасываем углы вращения прежнего варианта
xRot = yRot = zRot = 0;
// Выключаем освещение
glDisable(GL_LIGHTING);
// Выключаем режим согласования цветов
glDisable(GL_COLOR_MATERIAL);
// Запоминаем выбор в глобальную переменную
::choice = choice;
switch(::choice)
{
case 1:
ChangeSizeColorCube(w, h);
break;
case 2:
ChangeSizeJet(w, h);
break;
case 3:
ChangeSizeLightSphere(w, h);
break;
case 4:
ChangeSizeMultiMaterial(w, h);
break;
case 5:
ChangeSizeColorSphere(w, h);
break;
case 6:
ChangeSizeJetNight(w, h);
break;
case 7:
ChangeSizeJetLight(w, h);
break;
case 8:
ChangeSizeJetShiny(w, h);
break;
}
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
// Сохранить прежние настройки OpenGL в стеке атрибутов
glPushAttrib(GL_LIGHTING_BIT);
switch(::choice)
{
case 1:
RenderSceneColorCube();
break;
case 2:
RenderSceneJet();
break;
case 3:
RenderSceneLightSphere();
break;
case 4:
RenderSceneMultiMaterial();
break;
case 5:
RenderSceneColorSphere();
break;
case 6:
RenderSceneJetNight();
break;
case 7:
RenderSceneJetLight();
break;
case 8:
RenderSceneJetShiny();
break;
}
// Восстановить прежние настройки OpenGL из стека атрибутов
glPopAttrib();
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
void ChangeSize(int width, int height)
{
w = width;
h = height;
switch(::choice)
{
case 1:
ChangeSizeColorCube(width, height);
break;
case 2:
ChangeSizeJet(width, height);
break;
case 3:
ChangeSizeLightSphere(width, height);
break;
case 4:
ChangeSizeMultiMaterial(width, height);
break;
case 5:
ChangeSizeColorSphere(width, height);
break;
case 6:
ChangeSizeJetNight(width, height);
break;
case 7:
ChangeSizeJetLight(width, height);
break;
case 8:
ChangeSizeJetShiny(width, height);
break;
}
}
//**********************************************************
// Обработчик события таймера
void TimerFunc(int value)
{
glutPostRedisplay(); // Перерисовка сцены
glutTimerFunc(30, TimerFunc, 1); // Заряжаем новый таймер
}
//**********************************************************
// Управление с клавиатуры стрелками
// для задания новых значений матрицы поворота
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP) // Стрелка вверх
xRot -= 5.0f;
if(key == GLUT_KEY_DOWN)// Стрелка вниз
xRot += 5.0f;
if(key == GLUT_KEY_LEFT)// Стрелка влево
yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT)// Стрелка вправо
yRot += 5.0f;
xRot = (GLfloat)((const int)xRot % 360);
yRot = (GLfloat)((const int)yRot % 360);
// Для упражнения 5 смены материала
if(key == GLUT_KEY_F1) // Меняем красный
{
diffuseMaterial[0] += 0.1;
*diffuseMaterial = *diffuseMaterial > 1.0 ? 0.0 : *diffuseMaterial;
glColor4fv(diffuseMaterial);
}
if(key == GLUT_KEY_F2) // Меняем зеленый
{
diffuseMaterial[1] += 0.1;
*(diffuseMaterial + 1) = diffuseMaterial[1] > 1.0 ? 0.0 : diffuseMaterial[1];
glColor4fv(diffuseMaterial);
}
if(key == GLUT_KEY_F3) // Меняем синий
{
diffuseMaterial[2] += 0.1;
*(diffuseMaterial + 2) = diffuseMaterial[2] > 1.0 ? 0.0 : diffuseMaterial[2];
glColor4fv(diffuseMaterial);
}
// Вызвать принудительно визуализацию с помощью RenderScene()
glutPostRedisplay();
}
//**********************************************************
void main(int argc, char* argv[])
{
glutInit(argc, argv);
// Двойная буферизация, цветовая модель RGB, буфер глубины
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(300, 300); // Начальные размеры окна
glutCreateWindow("Свет и материалы"); // Заголовок окна
glutDisplayFunc(RenderScene); // Обновление сцены при разрушении окна
glutReshapeFunc(ChangeSize); // При изменении размера окна
glutTimerFunc(100, TimerFunc, 1); // Создали таймер
glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("1) Куб цвета", 1);
glutAddMenuEntry("2) Самолет без освещения", 2);
glutAddMenuEntry("3) Рисование освещенной сферы", 3);
glutAddMenuEntry("4) Рисование с разными материалами", 4);
glutAddMenuEntry("5) Способ согласования цветов (F1, F2, F3)", 5);
glutAddMenuEntry("6) Самолет с нулевым освещением", 6);
glutAddMenuEntry("7) Самолет с равномерным освещением", 7);
glutAddMenuEntry("8) Самолет с зеркальным отражением", 8);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutMainLoop(); // Цикл сообщений графического окна
}
Запустите
приложение и выполните упражнение 8, результат должен быть
примерно таким
Продолжим знакомство с источниками света и отражательными
свойствами материала. Будем рассматривать сферу с различной
степенью GL_SPOT_CUTOFF между осевой
линией и краем конуса.

Фактически при создании прожекторных эффектов допускаются углы от 0 до 90 градусов. Прожектор излучает конус света, а объекты вне этого конуса не освещаются.
В нашем следующем примере мы поместим источник света в определенную точку, нарисовав его в виде конуса, внутри которого будет находиться желтая сфера, имитирующая электрическую лампочку. Осевая линия источника будет все время направлена на освещаемый объект, а сам источник света будет перемещаться с помощью стрелок. В контекстном меню мы введем несколько режимов, которые устанавливают режим затенения модели-кристалла, а также количество примитивов, применяемых для рисования кристалла.
Добавьте
к проекту новый 
Заполните
этот файл следующим кодом для упражнения 9// CrystalAndSpot.h
// Упражнение 9: "9) Рисование кристалла и прожектора"
//**********************************************************
// Прототипы
void CrystalAndSpot();
void SetLightCrystalAndSpot();
void RenderSceneCrystalAndSpot();
void ChangeSizeCrystalAndSpot(int, int);
//**********************************************************
// Настройки источника света
GLfloat light_positionCrystalAndSpot[] = { 0.0f, 0.0f, 75.0f, 1.0f };
GLfloat specular[] = { 1.0f, 1.0f, 1.0f, 1.0f};
GLfloat ambientLight[] = { 0.5f, 0.5f, 0.5f, 1.0f};
GLfloat spotDir[] = { 0.0f, 0.0f, -1.0f };
//**********************************************************
// Флаги для спецификаций
#define MODE_FLAT 1
#define MODE_SMOOTH 2
#define MODE_VERYLOW 3
#define MODE_MEDIUM 4
#define MODE_VERYHIGH 5
int iShade = MODE_FLAT; // Модель затенения
int iTess = MODE_VERYLOW; // Тесселяция
//**********************************************************
// Рисуем сцену
void CrystalAndSpot()
{
if(iShade == MODE_FLAT)
glShadeModel(GL_FLAT);
else // iShade == MODE_SMOOTH;
glShadeModel(GL_SMOOTH);
// Сохраняем в стеке матрицу преобразований
glPushMatrix();
// Поворот системы координат
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
// Ориентируем прожектор
glLightfv(GL_LIGHT0, GL_POSITION, light_positionCrystalAndSpot);
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spotDir);
// Устанавливаем цвет рисования конуса прожектора красный
glColor3ub(255, 0, 0);
// Перемещаем точку рисования конуса в позицию источника света
glTranslatef(light_positionCrystalAndSpot[0], light_positionCrystalAndSpot[1], light_positionCrystalAndSpot[2]);
glutSolidCone(6.0f, 10.0f, 15, 15); // Рисуем конус
// Сохраняем атрибуты освещения в стеке атрибутов
glPushAttrib(GL_LIGHTING_BIT);
// Выключаем временно освещение, чтобы нарисовать желтый фонарик в конусе
glDisable(GL_LIGHTING);
glColor3ub(255, 255, 0);
glutSolidSphere(5.0f, 15, 15);
// Восстанавливаем атрибуты освещения из стека атрибутов
glPopAttrib();
// Восстанавливаем из стека матрицу преобразований
glPopMatrix();
// Усстанавливаем текущий цвет рисования сферы синий
glColor3ub(0, 0, 255);
if(iTess == MODE_VERYLOW) // Низкая тесселяция
glutSolidSphere(40.0f, 7, 7);
else
if(iTess == MODE_MEDIUM)// Средняя тесселяция
glutSolidSphere(40.0f, 15, 15);
else // iTess == MODE_VERYHIGH // Высокая тесселяция
glutSolidSphere(40.0f, 50, 50);
// Переключить буфер
glutSwapBuffers();
}
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderSceneCrystalAndSpot()
{
// Сбрасываем буфер цвета и буфер глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Рисуем сцену
CrystalAndSpot();
// Прокачка сообщений
glFlush();
}
//**********************************************************
// Настройка света и материала
void SetLightCrystalAndSpot()
{
// Модель освещения
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
// Настройка источника света
glLightfv(GL_LIGHT0, GL_DIFFUSE, ambientLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glLightfv(GL_LIGHT0, GL_POSITION, light_positionCrystalAndSpot);
// Спецификация источника как прожектора
glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 60.0f);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
// Включение режима согласования цветов
glEnable(GL_COLOR_MATERIAL);
// Установка свойств материала в зависимости от значения glColor()
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
// Настройка материала
glMaterialfv(GL_FRONT, GL_SPECULAR, specular); // Настройка отражения
glMateriali(GL_FRONT, GL_SHININESS, 128); // Яркий блик
}
//**********************************************************
void ChangeSizeCrystalAndSpot(int width, int height)
{
// Устанавливаем индивидуальные настройки освещения
SetLightCrystalAndSpot();
glEnable(GL_DEPTH_TEST); // Включить тест глубины
glEnable(GL_CULL_FACE); // Отображать только лицевую сторону
glFrontFace(GL_CCW); // Считать лицевым обход против часовой стрелки
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);// Цвет фона окна
// Предотвращаем деление на нуль
if(height == 0)
height = 1;
// Устанавливаем поле просмотра с размерами окна
glViewport(0, 0, width, height);
// Устанавливает матрицу преобразования в режим проецирования
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Устанавливаем размеры отсекающего объема перспективы
GLfloat aspectRatio = (GLfloat)width / (GLfloat)height; // Для соблюдения пропорций
gluPerspective(35.0f, aspectRatio, 1.0f, 500.0f); // Отсекающая перспектива
// Восстановливает матрицу преобразования в исходный режим вида
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Отодвинем сцену в отрицательную сторону оси 0z
glTranslatef(0.0f, 0.0f, -250.0f);
}
Добавьте
в файл LightAndMaterial.cpp код, подключающий
новое упражнение// LightAndMaterial.cpp : Defines the entry point for the console application.
//
//**********************************************************
// Подключение стандартного файла с библиотекой OpenGL
#include "stdafx.h"
//**********************************************************
// Прототипы функций
void ExecuteMenu(int); // Контекстное меню первого уровня
void TimerFunc(int); // Обработчик события таймера
void SpecialKeys(int, int, int); // Обработка нажатия клавиш
void RenderScene(void); // Функция рендеринга
void ChangeSize(int, int); // Функция установки отсекающего объема
void CrystalAndSpotExecuteMenu(int);// Подменю
// Глобальная переменная выбранного варианта основного меню
int choice = 1;
// Глобальные переменные для создания вращения
// в градусах
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
GLfloat zRot = 0.0f;
GLint w, h; // Ширина и высота экрана
//**********************************************************
// Подключение файлов с упражнениями
#include "ColorCube.h" // Упражнение 1: "1) Куб цвета"
#include "Jet.h" // Упражнение 2: "2) Самолет без освещения"
#include "LightSphere.h" // Упражнение 3: "3) Рисование освещенной сферы"
#include "MultiMaterial.h" // Упражнение 4: "4) Рисование с разными материалами"
#include "ColorSphere.h" // Упражнение 5: "5) Способ согласования цветов"
#include "JetNight.h" // Упражнение 6: "6) Самолет с нулевым освещением"
#include "JetLight.h" // Упражнение 7: "7) Самолет с равномерным освещением"
#include "JetShiny.h" // Упражнение 8: "8) Самолет с зеркальным отражением"
#include "CrystalAndSpot.h" // Упражнение 9: "9) Рисование кристалла и прожектора"
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
// Сбрасываем углы вращения прежнего варианта
xRot = yRot = zRot = 0;
// Выключаем освещение
glDisable(GL_LIGHTING);
// Выключаем режим согласования цветов
glDisable(GL_COLOR_MATERIAL);
// Запоминаем выбор в глобальную переменную
::choice = choice;
switch(::choice)
{
case 1:
ChangeSizeColorCube(w, h);
break;
case 2:
ChangeSizeJet(w, h);
break;
case 3:
ChangeSizeLightSphere(w, h);
break;
case 4:
ChangeSizeMultiMaterial(w, h);
break;
case 5:
ChangeSizeColorSphere(w, h);
break;
case 6:
ChangeSizeJetNight(w, h);
break;
case 7:
ChangeSizeJetLight(w, h);
break;
case 8:
ChangeSizeJetShiny(w, h);
break;
}
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//****************************************************
// Функция обратного вызова обработки выбора пользователя в подменю
void CrystalAndSpotExecuteMenu(int choice)
{
xRot = yRot = 0;
ChangeSizeCrystalAndSpot(w, h);
switch(choice){
case 0:
iShade = MODE_FLAT;
break;
case 1:
iShade = MODE_SMOOTH;
break;
case 2:
iTess = MODE_VERYLOW;
break;
case 3:
iTess = MODE_MEDIUM;
break;
case 4:
default:
iTess = MODE_VERYHIGH;
}
// Устанавливаем режим и вызываем принудительно
// функцию визуализации, где размещен
// вызов функции рисования кристалла и прожектора
::choice = 9;
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
// Сохранить прежние настройки OpenGL в стеке атрибутов
glPushAttrib(GL_LIGHTING_BIT);
switch(::choice)
{
case 1:
RenderSceneColorCube();
break;
case 2:
RenderSceneJet();
break;
case 3:
RenderSceneLightSphere();
break;
case 4:
RenderSceneMultiMaterial();
break;
case 5:
RenderSceneColorSphere();
break;
case 6:
RenderSceneJetNight();
break;
case 7:
RenderSceneJetLight();
break;
case 8:
RenderSceneJetShiny();
break;
case 9:
RenderSceneCrystalAndSpot();
break;
}
// Восстановить прежние настройки OpenGL из стека атрибутов
glPopAttrib();
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
void ChangeSize(int width, int height)
{
w = width;
h = height;
switch(::choice)
{
case 1:
ChangeSizeColorCube(width, height);
break;
case 2:
ChangeSizeJet(width, height);
break;
case 3:
ChangeSizeLightSphere(width, height);
break;
case 4:
ChangeSizeMultiMaterial(width, height);
break;
case 5:
ChangeSizeColorSphere(width, height);
break;
case 6:
ChangeSizeJetNight(width, height);
break;
case 7:
ChangeSizeJetLight(width, height);
break;
case 8:
ChangeSizeJetShiny(width, height);
break;
case 9:
ChangeSizeCrystalAndSpot(width, height);
break;
}
}
//**********************************************************
// Обработчик события таймера
void TimerFunc(int value)
{
glutPostRedisplay(); // Перерисовка сцены
glutTimerFunc(30, TimerFunc, 1); // Заряжаем новый таймер
}
//**********************************************************
// Управление с клавиатуры стрелками
// для задания новых значений матрицы поворота
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP) // Стрелка вверх
xRot -= 5.0f;
if(key == GLUT_KEY_DOWN)// Стрелка вниз
xRot += 5.0f;
if(key == GLUT_KEY_LEFT)// Стрелка влево
yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT)// Стрелка вправо
yRot += 5.0f;
xRot = (GLfloat)((const int)xRot % 360);
yRot = (GLfloat)((const int)yRot % 360);
// Для упражнения 5 смены материала
if(key == GLUT_KEY_F1) // Меняем красный
{
diffuseMaterial[0] += 0.1;
*diffuseMaterial = *diffuseMaterial > 1.0 ? 0.0 : *diffuseMaterial;
glColor4fv(diffuseMaterial);
}
if(key == GLUT_KEY_F2) // Меняем зеленый
{
diffuseMaterial[1] += 0.1;
*(diffuseMaterial + 1) = diffuseMaterial[1] > 1.0 ? 0.0 : diffuseMaterial[1];
glColor4fv(diffuseMaterial);
}
if(key == GLUT_KEY_F3) // Меняем синий
{
diffuseMaterial[2] += 0.1;
*(diffuseMaterial + 2) = diffuseMaterial[2] > 1.0 ? 0.0 : diffuseMaterial[2];
glColor4fv(diffuseMaterial);
}
// Вызвать принудительно визуализацию с помощью RenderScene()
glutPostRedisplay();
}
//**********************************************************
void main(int argc, char* argv[])
{
glutInit(argc, argv);
// Двойная буферизация, цветовая модель RGB, буфер глубины
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(300, 300); // Начальные размеры окна
glutCreateWindow("Свет и материалы"); // Заголовок окна
glutDisplayFunc(RenderScene); // Обновление сцены при разрушении окна
glutReshapeFunc(ChangeSize); // При изменении размера окна
glutTimerFunc(100, TimerFunc, 1); // Создали таймер
glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
// Создаем подменю для рисования кристалла и прожектора
int CrystalAndSpot_menu = glutCreateMenu(CrystalAndSpotExecuteMenu);
glutAddMenuEntry("Плоское затенение поверхностей", 0);
glutAddMenuEntry("Гладкое (градиентное) затенение", 1);
glutAddMenuEntry("Низкая тесселяция (крупные грани)", 2);
glutAddMenuEntry("Средняя тесселяция (больше граней)", 3);
glutAddMenuEntry("Высокая аппроксимация (много граней)", 4);
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("1) Куб цвета", 1);
glutAddMenuEntry("2) Самолет без освещения", 2);
glutAddMenuEntry("3) Рисование освещенной сферы", 3);
glutAddMenuEntry("4) Рисование с разными материалами", 4);
glutAddMenuEntry("5) Способ согласования цветов (F1, F2, F3)", 5);
glutAddMenuEntry("6) Самолет с нулевым освещением", 6);
glutAddMenuEntry("7) Самолет с равномерным освещением", 7);
glutAddMenuEntry("8) Самолет с зеркальным отражением", 8);
glutAddSubMenu("9) Рисование кристалла и прожектора", CrystalAndSpot_menu);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutMainLoop(); // Цикл сообщений графического окна
}
Запустите
приложение и выполните упражнение 9, результат должен быть
примерно таким![]() |
|
Попытайтесь
разобраться с кодом!!!Переменные iTess и iMode контролируют,
на сколько участков разбивается сфера и какое затенение
применяется - плоское или гладкое. Обратите внимание, что
осуществляется управление положением не объекта, а прожектора.
Если специфицировать прожектор с меньшим углом конуса света,
например, в функции SetLightCrystalAndSpot() таким кодом
// Спецификация источника как прожектора glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 20.0f);
то мы действительно увидим световое пятно на кристалле, сходное с лучем прожектора.
Эта работа по цвету и освещению была бы неполной без обсуждения
теней. Добавление тени к сцене может повысить ее
Простую тень, падающую на
Вне зависимости от ориентации объекта его образ должен
быть сплющен матрицей преобразования модели в двухмерный
вид на
typedef GLfloat GLTVector3[3];
typedef GLfloat GLTVector4[4];
typedef GLfloat GLTMatrix[16];
//**********************************************************
// Вычисляет матрицу преобразования тени.
// Входными параметрами являются координаты трех точек
// на плоскости (не лежащих на одной прямой)
// и четырехмерный вектор положения источника света
// Возвращаемое значение находится в destMat
void gltMakeShadowMatrix(GLTVector3 vPoints[3], GLTVector4 vLightPos, GLTMatrix destMat)
{
GLTVector4 vPlaneEquation;
GLfloat dot;
gltGetPlaneEquation(vPoints[0], vPoints[1], vPoints[2], vPlaneEquation);
// Вычисляет скалярное произведение направляющего вектора плоскости
// и вектора положения источника света
dot = vPlaneEquation[0]*vLightPos[0] +
vPlaneEquation[1]*vLightPos[1] +
vPlaneEquation[2]*vLightPos[2] +
vPlaneEquation[3]*vLightPos[3];
// Формируем матрицу проекции
// Первый столбец
destMat[0] = dot - vLightPos[0] * vPlaneEquation[0];
destMat[4] = 0.0f - vLightPos[0] * vPlaneEquation[1];
destMat[8] = 0.0f - vLightPos[0] * vPlaneEquation[2];
destMat[12] = 0.0f - vLightPos[0] * vPlaneEquation[3];
// Второй столбец
destMat[1] = 0.0f - vLightPos[1] * vPlaneEquation[0];
destMat[5] = dot - vLightPos[1] * vPlaneEquation[1];
destMat[9] = 0.0f - vLightPos[1] * vPlaneEquation[2];
destMat[13] = 0.0f - vLightPos[1] * vPlaneEquation[3];
// Третий столбец
destMat[2] = 0.0f - vLightPos[2] * vPlaneEquation[0];
destMat[6] = 0.0f - vLightPos[2] * vPlaneEquation[1];
destMat[10] = dot - vLightPos[2] * vPlaneEquation[2];
destMat[14] = 0.0f - vLightPos[2] * vPlaneEquation[3];
// Четвертый столбец
destMat[3] = 0.0f - vLightPos[3] * vPlaneEquation[0];
destMat[7] = 0.0f - vLightPos[3] * vPlaneEquation[1];
destMat[11] = 0.0f - vLightPos[3] * vPlaneEquation[2];
destMat[15] = dot - vLightPos[3] * vPlaneEquation[3];
}
//**********************************************************
// Далее идет код вспомогательных функций
//**********************************************************
// Возвращает коэффициенты уравнения плоскости по трем точкам
void gltGetPlaneEquation(GLTVector3 vPoint1, GLTVector3 vPoint2, GLTVector3 vPoint3, GLTVector3 vPlane)
{
// Вычислить вектор нормали
gltGetNormalVector(vPoint1, vPoint2, vPoint3, vPlane);
vPlane[3] = -(vPlane[0] * vPoint3[0] + vPlane[1] * vPoint3[1] + vPlane[2] * vPoint3[2]);
}
//**********************************************************
// Вычислить нормаль по трем точкам
void gltGetNormalVector(const GLTVector3 vP1, const GLTVector3 vP2, const GLTVector3 vP3, GLTVector3 vNormal)
{
GLTVector3 vV1, vV2;
gltSubtractVectors(vP2, vP1, vV1);
gltSubtractVectors(vP3, vP1, vV2);
gltVectorCrossProduct(vV1, vV2, vNormal);
gltNormalizeVector(vNormal);
}
//**********************************************************
// Вычесть один вектор из другого
void gltSubtractVectors(const GLTVector3 vFirst, const GLTVector3 vSecond, GLTVector3 vResult)
{
vResult[0] = vFirst[0] - vSecond[0];
vResult[1] = vFirst[1] - vSecond[1];
vResult[2] = vFirst[2] - vSecond[2];
}
//**********************************************************
// Вычислить векторное произведение двух векторов
void gltVectorCrossProduct(const GLTVector3 vU, const GLTVector3 vV, GLTVector3 vResult)
{
vResult[0] = vU[1]*vV[2] - vV[1]*vU[2];
vResult[1] = -vU[0]*vV[2] + vV[0]*vU[2];
vResult[2] = vU[0]*vV[1] - vV[0]*vU[1];
}
//**********************************************************
// Привести вектор к единичной длине (нормировать)
void gltNormalizeVector(GLTVector3 vNormal)
{
GLfloat fLength = 1.0f / gltGetVectorLength(vNormal);
gltScaleVector(vNormal, fLength);
}
//**********************************************************
// Умножить вектор на скаляр
void gltScaleVector(GLTVector3 vVector, const GLfloat fScale)
{
vVector[0] *= fScale; vVector[1] *= fScale; vVector[2] *= fScale;
}
Приведенная функция по трем точкам заданной
Чтобы продемонстрировать использование приведенной функции
преобразования объекта на
Добавьте
к проекту новый 
Заполните
этот файл следующим кодом для упражнения 10// JetShadow.h
// Упражнение 10: "10) Модель самолета и его тени"
#include <math.h>
//**********************************************************
// Прототипы
void JetShadow(int nShadow);
void RenderSceneJetShadow(void);
void SetLightJetShadow();
void ChangeSizeJetShadow(int width, int height);
//**********************************************************
// Вспомогательный код
typedef GLfloat GLTVector3[3];
typedef GLfloat GLTVector4[4];
typedef GLfloat GLTMatrix[16];
void gltMakeShadowMatrix(GLTVector3 vPoints[3], GLTVector4 vLightPos, GLTMatrix destMat);
void gltGetPlaneEquation(GLTVector3 vPoint1, GLTVector3 vPoint2, GLTVector3 vPoint3, GLTVector3 vPlane);
//**********************************************************
// Глобальные переменные
GLTMatrix shadowMat;
GLfloat lightPosJetShadow[] = { -75.0f, 150.0f, -50.0f, 0.0f };
//**********************************************************
// Рисовать самолет
void JetShadow(int nShadow)
{
GLTVector3 vNormal; // Для вектора нормали
// Каким цветом рисовать
if(nShadow == 0) // Рисовать белым цветом сам самолет
glColor3ub(128, 128, 128);
else // Рисовать черным цветом тень самолета
glColor3ub(0,0,0);
// Рисуем самолет
glBegin(GL_TRIANGLES);
// Конус носа /////////////////////////////
glNormal3f(0.0f, -1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 60.0f);
glVertex3f(-15.0f, 0.0f, 30.0f);
glVertex3f(15.0f,0.0f,30.0f);
{// Блок кода
GLTVector3 vPoints[3] = {{ 15.0f, 0.0f, 30.0f},
{ 0.0f, 15.0f, 30.0f},
{ 0.0f, 0.0f, 60.0f}};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{// Блок кода
GLTVector3 vPoints[3] = {{ 0.0f, 0.0f, 60.0f },
{ 0.0f, 15.0f, 30.0f },
{ -15.0f, 0.0f, 30.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
// Тело самолета //////////////////////////
{
GLTVector3 vPoints[3] = {{ -15.0f, 0.0f, 30.0f },
{ 0.0f, 15.0f, 30.0f },
{ 0.0f, 0.0f, -56.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{ 0.0f, 0.0f, -56.0f },
{ 0.0f, 15.0f, 30.0f },
{ 15.0f,0.0f,30.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
glNormal3f(0.0f, -1.0f, 0.0f);
glVertex3f(15.0f,0.0f,30.0f);
glVertex3f(-15.0f, 0.0f, 30.0f);
glVertex3f(0.0f, 0.0f, -56.0f);
///////////////////////////////////////////
// Левое крыло
// Большой треугольник для основания крыла
{
GLTVector3 vPoints[3] = {{ 0.0f,2.0f,27.0f },
{ -60.0f, 2.0f, -8.0f },
{ 60.0f, 2.0f, -8.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{ 60.0f, 2.0f, -8.0f},
{0.0f, 7.0f, -8.0f},
{0.0f,2.0f,27.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{60.0f, 2.0f, -8.0f},
{-60.0f, 2.0f, -8.0f},
{0.0f,7.0f,-8.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
// Другое крыло верхней секции
{
GLTVector3 vPoints[3] = {{0.0f,2.0f,27.0f},
{0.0f, 7.0f, -8.0f},
{-60.0f, 2.0f, -8.0f}};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
///////////////////////////////////////////
// Хвост
glNormal3f(0.0f, -1.0f, 0.0f);
glVertex3f(-30.0f, -0.50f, -57.0f);
glVertex3f(30.0f, -0.50f, -57.0f);
glVertex3f(0.0f,-0.50f,-40.0f);
// Верхняя левая сторона
{
GLTVector3 vPoints[3] = {{ 0.0f,-0.5f,-40.0f },
{30.0f, -0.5f, -57.0f},
{0.0f, 4.0f, -57.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
// Верхняя правая сторона
{
GLTVector3 vPoints[3] = {{ 0.0f, 4.0f, -57.0f },
{ -30.0f, -0.5f, -57.0f },
{ 0.0f,-0.5f,-40.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
// Оборотная нижняя часть
{
GLTVector3 vPoints[3] = {{ 30.0f,-0.5f,-57.0f },
{ -30.0f, -0.5f, -57.0f },
{ 0.0f, 4.0f, -57.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{ 0.0f,0.5f,-40.0f },
{ 3.0f, 0.5f, -57.0f },
{ 0.0f, 25.0f, -65.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{ 0.0f, 25.0f, -65.0f },
{ -3.0f, 0.5f, -57.0f},
{ 0.0f,0.5f,-40.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{ 3.0f,0.5f,-57.0f },
{ -3.0f, 0.5f, -57.0f },
{ 0.0f, 25.0f, -65.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
glEnd(); // О самолете
}
//**********************************************************
// Функция рендеринга
void RenderSceneJetShadow(void)
{
// Очищаем буфер цвета и глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Рисуем плоскость, имитирующую зеленую землю
glBegin(GL_QUADS);
glColor3ub(0,32,0);
glVertex3f(400.0f, -150.0f, -200.0f);
glVertex3f(-400.0f, -150.0f, -200.0f);
glColor3ub(0,255,0);
glVertex3f(-400.0f, -150.0f, 200.0f);
glVertex3f(400.0f, -150.0f, 200.0f);
glEnd();
// Сохраним матрицу преобразования модели
// перед добавлением поворотов освещенного самолета
glPushMatrix();
// Включаем освещение и ориентируем источник света
glEnable(GL_LIGHTING);
glLightfv(GL_LIGHT0, GL_POSITION, lightPosJetShadow);
// Для поворота самолета
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
// Нарисуем освещенный самолет
JetShadow(0);
// Восстанавливаем матрицу преобразования модели
glPopMatrix();
// Перед рисованием тени отключаем освещение и глубину
// Чтобы тень была чисто черной и плоской
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glPushMatrix();
// Умножение на матрицу преобразования тени
glMultMatrixf( (GLfloat *) shadowMat );
// Добавим повороты
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
// Нарисуем тень самолета
JetShadow(1);
// Восстановим матрицу преобразования модели
glPopMatrix();
// Рисуем источник света
glPushMatrix();
// Рисуем желтую сферу в месте нахождения источника света
glTranslatef(lightPosJetShadow[0], lightPosJetShadow[1], lightPosJetShadow[2]);
glColor3ub(255, 255, 0); // Задаем желтый цвет
glutSolidSphere(15.0f, 15, 15);// Рисуем сферу
glPopMatrix();
// Восстанавливаем отключенную проверку глубины
// Отключенное освещение включится при новом заходе в эту функцию
glEnable(GL_DEPTH_TEST);
// Переключаем буферы рисования
glutSwapBuffers();
// Прокачка сообщений
glFlush();
}
//**********************************************************
// Настройка света и материала
void SetLightJetShadow()
{
// Параметры освещения
GLfloat ambientLight[] = { 0.3f, 0.3f, 0.3f, 1.0f };
GLfloat diffuseLight[] = { 0.7f, 0.7f, 0.7f, 1.0f };
GLfloat specular[] = { 1.0f, 1.0f, 1.0f, 1.0f};
// Любые три точки на плоскости, имитирующей землю
GLTVector3 points[3] = {{ -30.0f, -149.0f, -20.0f },
{ -30.0f, -149.0f, 20.0f },
{ 40.0f, -149.0f, 20.0f }};
// Настроить и включить источник света
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glLightfv(GL_LIGHT0, GL_POSITION, lightPosJetShadow);
glEnable(GL_LIGHT0);
// Установить режим согласования цветов
glEnable(GL_COLOR_MATERIAL);
// Свойства материала следуют за значениями glColor()
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
// Добавляются отражающие свойства материала
// и размеры бликов
glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
glMateriali(GL_FRONT, GL_SHININESS, 128);
// Вычисление матрицы проекции тени на плоскость основания (землю)
gltMakeShadowMatrix(points, lightPosJetShadow, shadowMat);
}
//**********************************************************
void ChangeSizeJetShadow(int width, int height)
{
// Устанавливаем индивидуальные настройки освещения
// и материала
SetLightJetShadow();
glEnable(GL_DEPTH_TEST); // Включить тест глубины
glEnable(GL_CULL_FACE); // Отображать только лицевую сторону
glFrontFace(GL_CCW); // Считать лицевым обход против часовой стрелки
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);// Цвет фона окна
// Предотвращаем деление на нуль
if(height == 0)
height = 1;
// Устанавливаем поле просмотра с размерами окна
glViewport(0, 0, width, height);
// Устанавливает матрицу преобразования в режим проецирования
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Устанавливаем размеры отсекающего объема перспективы
GLfloat aspectRatio = (GLfloat)width / (GLfloat)height; // Для соблюдения пропорций
gluPerspective(60.0f, aspectRatio, 200.0f, 500.0f); // Отсекающая перспектива
// Восстановливает матрицу преобразования в исходный режим вида
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Отодвинем сцену в отрицательную сторону оси 0z
glTranslatef(0.0f, 0.0f, -400.0f);
}
//**********************************************************
//**********************************************************
// Вспомогательный код
//**********************************************************
//**********************************************************
// Вычисляет матрицу преобразования тени.
// Входными параметрами являются координаты трех точек
// на плоскости (не лежащих на одной прямой)
// и четырехмерный вектор положения источника света
// Возвращаемое значение находится в destMat
void gltMakeShadowMatrix(GLTVector3 vPoints[3], GLTVector4 vLightPos, GLTMatrix destMat)
{
GLTVector4 vPlaneEquation;
GLfloat dot;
gltGetPlaneEquation(vPoints[0], vPoints[1], vPoints[2], vPlaneEquation);
// Вычисляет скалярное произведение направляющего вектора плоскости
// и вектора положения источника света
dot = vPlaneEquation[0]*vLightPos[0] +
vPlaneEquation[1]*vLightPos[1] +
vPlaneEquation[2]*vLightPos[2] +
vPlaneEquation[3]*vLightPos[3];
// Формируем матрицу проекции
// Первый столбец
destMat[0] = dot - vLightPos[0] * vPlaneEquation[0];
destMat[4] = 0.0f - vLightPos[0] * vPlaneEquation[1];
destMat[8] = 0.0f - vLightPos[0] * vPlaneEquation[2];
destMat[12] = 0.0f - vLightPos[0] * vPlaneEquation[3];
// Второй столбец
destMat[1] = 0.0f - vLightPos[1] * vPlaneEquation[0];
destMat[5] = dot - vLightPos[1] * vPlaneEquation[1];
destMat[9] = 0.0f - vLightPos[1] * vPlaneEquation[2];
destMat[13] = 0.0f - vLightPos[1] * vPlaneEquation[3];
// Третий столбец
destMat[2] = 0.0f - vLightPos[2] * vPlaneEquation[0];
destMat[6] = 0.0f - vLightPos[2] * vPlaneEquation[1];
destMat[10] = dot - vLightPos[2] * vPlaneEquation[2];
destMat[14] = 0.0f - vLightPos[2] * vPlaneEquation[3];
// Четвертый столбец
destMat[3] = 0.0f - vLightPos[3] * vPlaneEquation[0];
destMat[7] = 0.0f - vLightPos[3] * vPlaneEquation[1];
destMat[11] = 0.0f - vLightPos[3] * vPlaneEquation[2];
destMat[15] = dot - vLightPos[3] * vPlaneEquation[3];
}
//**********************************************************
// Возвращает коэффициенты уравнения плоскости по трем точкам
void gltGetPlaneEquation(GLTVector3 vPoint1, GLTVector3 vPoint2, GLTVector3 vPoint3, GLTVector3 vPlane)
{
// Вычислить вектор нормали
gltGetNormalVector(vPoint1, vPoint2, vPoint3, vPlane);
vPlane[3] = -(vPlane[0] * vPoint3[0] + vPlane[1] * vPoint3[1] + vPlane[2] * vPoint3[2]);
}
Добавьте
в файл LightAndMaterial.cpp код, подключающий
новое упражнение// LightAndMaterial.cpp : Defines the entry point for the console application.
//
//**********************************************************
// Подключение стандартного файла с библиотекой OpenGL
#include "stdafx.h"
//**********************************************************
// Прототипы функций
void ExecuteMenu(int); // Контекстное меню первого уровня
void TimerFunc(int); // Обработчик события таймера
void SpecialKeys(int, int, int); // Обработка нажатия клавиш
void RenderScene(void); // Функция рендеринга
void ChangeSize(int, int); // Функция установки отсекающего объема
void CrystalAndSpotExecuteMenu(int);// Подменю
// Глобальная переменная выбранного варианта основного меню
int choice = 1;
// Глобальные переменные для создания вращения
// в градусах
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
GLfloat zRot = 0.0f;
GLint w, h; // Ширина и высота экрана
//**********************************************************
// Подключение файлов с упражнениями
#include "ColorCube.h" // Упражнение 1: "1) Куб цвета"
#include "Jet.h" // Упражнение 2: "2) Самолет без освещения"
#include "LightSphere.h" // Упражнение 3: "3) Рисование освещенной сферы"
#include "MultiMaterial.h" // Упражнение 4: "4) Рисование с разными материалами"
#include "ColorSphere.h" // Упражнение 5: "5) Способ согласования цветов"
#include "JetNight.h" // Упражнение 6: "6) Самолет с нулевым освещением"
#include "JetLight.h" // Упражнение 7: "7) Самолет с равномерным освещением"
#include "JetShiny.h" // Упражнение 8: "8) Самолет с зеркальным отражением"
#include "CrystalAndSpot.h" // Упражнение 9: "9) Рисование кристалла и прожектора"
#include "JetShadow.h" // Упражнение 10: "10) Модель самолета и его тени"
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
// Сбрасываем углы вращения прежнего варианта
xRot = yRot = zRot = 0;
// Выключаем освещение
glDisable(GL_LIGHTING);
// Выключаем режим согласования цветов
glDisable(GL_COLOR_MATERIAL);
// Запоминаем выбор в глобальную переменную
::choice = choice;
switch(::choice)
{
case 1:
ChangeSizeColorCube(w, h);
break;
case 2:
ChangeSizeJet(w, h);
break;
case 3:
ChangeSizeLightSphere(w, h);
break;
case 4:
ChangeSizeMultiMaterial(w, h);
break;
case 5:
ChangeSizeColorSphere(w, h);
break;
case 6:
ChangeSizeJetNight(w, h);
break;
case 7:
ChangeSizeJetLight(w, h);
break;
case 8:
ChangeSizeJetShiny(w, h);
break;
case 10:
ChangeSizeJetShadow(w, h);
break;
}
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//****************************************************
// Функция обратного вызова обработки выбора пользователя в подменю
void CrystalAndSpotExecuteMenu(int choice)
{
xRot = yRot = 0;
ChangeSizeCrystalAndSpot(w, h);
switch(choice){
case 0:
iShade = MODE_FLAT;
break;
case 1:
iShade = MODE_SMOOTH;
break;
case 2:
iTess = MODE_VERYLOW;
break;
case 3:
iTess = MODE_MEDIUM;
break;
case 4:
default:
iTess = MODE_VERYHIGH;
}
// Устанавливаем режим и вызываем принудительно
// функцию визуализации, где размещен
// вызов функции рисования кристалла и прожектора
::choice = 9;
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
// Сохранить прежние настройки OpenGL в стеке атрибутов
glPushAttrib(GL_LIGHTING_BIT);
switch(::choice)
{
case 1:
RenderSceneColorCube();
break;
case 2:
RenderSceneJet();
break;
case 3:
RenderSceneLightSphere();
break;
case 4:
RenderSceneMultiMaterial();
break;
case 5:
RenderSceneColorSphere();
break;
case 6:
RenderSceneJetNight();
break;
case 7:
RenderSceneJetLight();
break;
case 8:
RenderSceneJetShiny();
break;
case 9:
RenderSceneCrystalAndSpot();
break;
case 10:
RenderSceneJetShadow();
break;
}
// Восстановить прежние настройки OpenGL из стека атрибутов
glPopAttrib();
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
void ChangeSize(int width, int height)
{
w = width;
h = height;
switch(::choice)
{
case 1:
ChangeSizeColorCube(width, height);
break;
case 2:
ChangeSizeJet(width, height);
break;
case 3:
ChangeSizeLightSphere(width, height);
break;
case 4:
ChangeSizeMultiMaterial(width, height);
break;
case 5:
ChangeSizeColorSphere(width, height);
break;
case 6:
ChangeSizeJetNight(width, height);
break;
case 7:
ChangeSizeJetLight(width, height);
break;
case 8:
ChangeSizeJetShiny(width, height);
break;
case 9:
ChangeSizeCrystalAndSpot(width, height);
break;
case 10:
ChangeSizeJetShadow(width, height);
break;
}
}
//**********************************************************
// Обработчик события таймера
void TimerFunc(int value)
{
glutPostRedisplay(); // Перерисовка сцены
glutTimerFunc(30, TimerFunc, 1); // Заряжаем новый таймер
}
//**********************************************************
// Управление с клавиатуры стрелками
// для задания новых значений матрицы поворота
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP) // Стрелка вверх
xRot -= 5.0f;
if(key == GLUT_KEY_DOWN)// Стрелка вниз
xRot += 5.0f;
if(key == GLUT_KEY_LEFT)// Стрелка влево
yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT)// Стрелка вправо
yRot += 5.0f;
xRot = (GLfloat)((const int)xRot % 360);
yRot = (GLfloat)((const int)yRot % 360);
// Для упражнения 5 смены материала
if(key == GLUT_KEY_F1) // Меняем красный
{
diffuseMaterial[0] += 0.1;
*diffuseMaterial = *diffuseMaterial > 1.0 ? 0.0 : *diffuseMaterial;
glColor4fv(diffuseMaterial);
}
if(key == GLUT_KEY_F2) // Меняем зеленый
{
diffuseMaterial[1] += 0.1;
*(diffuseMaterial + 1) = diffuseMaterial[1] > 1.0 ? 0.0 : diffuseMaterial[1];
glColor4fv(diffuseMaterial);
}
if(key == GLUT_KEY_F3) // Меняем синий
{
diffuseMaterial[2] += 0.1;
*(diffuseMaterial + 2) = diffuseMaterial[2] > 1.0 ? 0.0 : diffuseMaterial[2];
glColor4fv(diffuseMaterial);
}
// Вызвать принудительно визуализацию с помощью RenderScene()
glutPostRedisplay();
}
//**********************************************************
void main(int argc, char* argv[])
{
glutInit(argc, argv);
// Двойная буферизация, цветовая модель RGB, буфер глубины
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(300, 300); // Начальные размеры окна
glutCreateWindow("Свет и материалы"); // Заголовок окна
glutDisplayFunc(RenderScene); // Обновление сцены при разрушении окна
glutReshapeFunc(ChangeSize); // При изменении размера окна
glutTimerFunc(100, TimerFunc, 1); // Создали таймер
glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
// Создаем подменю для рисования кристалла и прожектора
int CrystalAndSpot_menu = glutCreateMenu(CrystalAndSpotExecuteMenu);
glutAddMenuEntry("Плоское затенение поверхностей", 0);
glutAddMenuEntry("Гладкое (градиентное) затенение", 1);
glutAddMenuEntry("Низкая тесселяция (крупные грани)", 2);
glutAddMenuEntry("Средняя тесселяция (больше граней)", 3);
glutAddMenuEntry("Высокая аппроксимация (много граней)", 4);
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("1) Куб цвета", 1);
glutAddMenuEntry("2) Самолет без освещения", 2);
glutAddMenuEntry("3) Рисование освещенной сферы", 3);
glutAddMenuEntry("4) Рисование с разными материалами", 4);
glutAddMenuEntry("5) Способ согласования цветов (F1, F2, F3)", 5);
glutAddMenuEntry("6) Самолет с нулевым освещением", 6);
glutAddMenuEntry("7) Самолет с равномерным освещением", 7);
glutAddMenuEntry("8) Самолет с зеркальным отражением", 8);
glutAddSubMenu("9) Рисование кристалла и прожектора", CrystalAndSpot_menu);
glutAddMenuEntry("10) Модель самолета и его тени", 10);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutMainLoop(); // Цикл сообщений графического окна
}
Запустите
приложение и выполните упражнение 10, результат должен
быть примерно таким![]() |
|
Поуправляйте
самолетом клавишами-стрелками и попытайтесь разобраться
с кодом!!!Заметьте, что проверку глубины перед рисованием тени мы
отключаем для того, чтобы тень гарантированно оказалась
выше
В заключение выполним еще одно упражнение. В предыдущей лабораторной работе мы рассматривали пример, где рисовался мир сфер в каркасном режиме. Здесь в качестве поучительной иллюстрации приведем тот же пример, но с использованием в сцене источников света и материалов.
Добавьте
к проекту новый 
Наполните этот файл следующим кодом// SphereWorld.h
// Упражнение 11: "11) Освещенный мир сфер"
//**********************************************************
// Прототипы
void SetLightSphereWorld();
void ChangeSizeSphereWorld(int width, int height);
void SpecialKeysSphereWorld(int key);
void RenderSceneSphereWorld(void);
void DrawInhabitants(GLint nShadow);
void DrawGround(void);
//**********************************************************
// Глобальные переменные
typedef struct{
GLTVector3 vLocation;
GLTVector3 vUp;
GLTVector3 vForward;
} GLTFrame;
#define NUM_SPHERES 30
GLTFrame spheres[NUM_SPHERES];
GLTFrame frameCamera;
// Данные для источников света и материалов
GLfloat fLightPos[4] = { -100.0f, 100.0f, 50.0f, 1.0f };
GLfloat fNoLight[] = { 0.0f, 0.0f, 0.0f, 0.0f };
GLfloat fLowLight[] = { 0.25f, 0.25f, 0.25f, 1.0f };
GLfloat fBrightLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };
// Матрица проектировария тени
GLTMatrix mShadowMatrix;
//**********************************************************
// Вспомогательный код
void gltApplyActorTransform(GLTFrame *pFrame);
void gltApplyCameraTransform(GLTFrame *pCamera);
void gltDrawTorus(GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
void gltInitFrame(GLTFrame *pFrame);
void gltMoveFrameForward(GLTFrame *pFrame, GLfloat fStep);
void gltRotateFrameLocalY(GLTFrame *pFrame, GLfloat fAngle);
#define GLT_PI_DIV_180 0.017453292519943296
#define gltDegToRad(x) ((x)*GLT_PI_DIV_180)
void gltGetMatrixFromFrame(GLTFrame *pFrame, GLTMatrix mMatrix);
void gltRotateVector(const GLTVector3 vSrcVector, const GLTMatrix mMatrix, GLTVector3 vOut);
void gltRotationMatrix(float angle, float x, float y, float z, GLTMatrix mMatrix);
void gltLoadIdentityMatrix(GLTMatrix m);
//**********************************************************
// Установка света и материалов
void SetLightSphereWorld()
{
int iSphere;
// Точки плоскости размещения тени
GLTVector3 vPoints[3] = {{ 0.0f, -0.4f, 0.0f },
{ 10.0f, -0.4f, 0.0f },
{ 5.0f, -0.4f, -5.0f }};
// Установка параметров света
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fNoLight);
glLightfv(GL_LIGHT0, GL_AMBIENT, fLowLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, fBrightLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, fBrightLight);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
// Вычисляем матрицу тени
gltMakeShadowMatrix(vPoints, fLightPos, mShadowMatrix);
// Задаем согласование цветов материала
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glMateriali(GL_FRONT, GL_SHININESS, 128);
gltInitFrame(frameCamera); // Инициализируем камеру
// Вычисляем случайно места для размещения сфер
for(iSphere = 0; iSphere < NUM_SPHERES; iSphere++)
{
// Инициализация фрейма для очередной сферы
gltInitFrame(spheres[iSphere]);
// Корректируем координаты сфер
spheres[iSphere].vLocation[0] = (float)((rand() % 400) - 200) * 0.1f;
spheres[iSphere].vLocation[1] = 0.0f;
spheres[iSphere].vLocation[2] = (float)((rand() % 400) - 200) * 0.1f;
}
}
//**********************************************************
// Рисуем основание как ленты треугольников
void DrawGround(void)
{
GLfloat fExtent = 20.0f;
GLfloat fStep = 1.0f;
GLfloat y = -0.4f;
GLint iStrip, iRun;
for(iStrip = -fExtent; iStrip <= fExtent; iStrip += fStep)
{
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0.0f, 1.0f, 0.0f); // Нормаль для всех вершин
for(iRun = fExtent; iRun >= -fExtent; iRun -= fStep)
{
glVertex3f(iStrip, y, iRun);
glVertex3f(iStrip + fStep, y, iRun);
}
glEnd();
}
}
//**********************************************************
// Рисуем случайным образом сферы и тор (или их тени)
void DrawInhabitants(GLint nShadow)
{
static GLfloat yRot = 0.0f;
GLint i;
if(nShadow == 0)
yRot += 0.5f;
else // Черный цвет
glColor3f(0.0f, 0.0f, 0.0f);
// Рисуем зелеными случайно расположенные сферы
if(nShadow == 0)
glColor3f(0.0f, 1.0f, 0.0f);
for(i = 0; i < NUM_SPHERES; i++)
{
glPushMatrix();
gltApplyActorTransform(spheres[i]);
glutSolidSphere(0.3f, 17, 9);
glPopMatrix();
}
glPushMatrix();
glTranslatef(0.0f, 0.1f, -2.5f);
if(nShadow == 0)
glColor3f(0.0f, 0.0f, 1.0f);
glPushMatrix();
glRotatef(-yRot * 2.0f, 0.0f, 1.0f, 0.0f);
glTranslatef(1.0f, 0.0f, 0.0f);
glutSolidSphere(0.1f, 17, 9);
glPopMatrix();
if(nShadow == 0)
{
// Устанавливаем для тора зеркальные свойства
glColor3f(1.0f, 0.0f, 0.0f);// Красный цвет тора
glMaterialfv(GL_FRONT, GL_SPECULAR, fBrightLight);
}
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
gltDrawTorus(0.35, 0.15, 61, 37);
glMaterialfv(GL_FRONT, GL_SPECULAR, fNoLight);
glPopMatrix();
}
//**********************************************************
// Рендеринг сцены
void RenderSceneSphereWorld(void)
{
// Очистка буфера цвета и глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
gltApplyCameraTransform(frameCamera);
// Позиция источника света до преобразований
glLightfv(GL_LIGHT0, GL_POSITION, fLightPos);
// Рисуем основание коричневым цветом
glColor3f(0.60f, .40f, .10f);
DrawGround();
// Вначале рисуем тени объектов
// при выключенных глубине и освещении
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glPushMatrix();
glMultMatrixf(mShadowMatrix);
DrawInhabitants(1);
glPopMatrix();
// Включаем проверку глубины и освещение
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
// Теперь рисуем сами объекты (сферы и тор)
DrawInhabitants(0);
glPopMatrix();
// Переключаем буфер и обновляем экран
glutSwapBuffers();
glutPostRedisplay();
}
//**********************************************************
// Управление клавишами
void SpecialKeysSphereWorld(int key)
{
if(key == GLUT_KEY_UP)
gltMoveFrameForward(frameCamera, 0.1f);
if(key == GLUT_KEY_DOWN)
gltMoveFrameForward(frameCamera, -0.1f);
if(key == GLUT_KEY_LEFT)
gltRotateFrameLocalY(frameCamera, 0.1);
if(key == GLUT_KEY_RIGHT)
gltRotateFrameLocalY(frameCamera, -0.1);
}
//**********************************************************
void ChangeSizeSphereWorld(int width, int height)
{
// Устанавливаем индивидуальные настройки освещения
// и материала
SetLightSphereWorld();
glEnable(GL_DEPTH_TEST); // Включить тест глубины
glEnable(GL_CULL_FACE); // Отображать только лицевую сторону
glFrontFace(GL_CCW); // Считать лицевым обход против часовой стрелки
glCullFace(GL_BACK);
// Цвет фона окна угрюмо - серый
glClearColor(fLowLight[0], fLowLight[1], fLowLight[2], fLowLight[3]);
// Предотвращаем деление на нуль
if(height == 0)
height = 1;
// Устанавливаем поле просмотра с размерами окна
glViewport(0, 0, width, height);
// Устанавливает матрицу преобразования в режим проецирования
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Устанавливаем размеры отсекающего объема перспективы
GLfloat aspectRatio = (GLfloat)width / (GLfloat)height; // Для соблюдения пропорций
gluPerspective(35.0f, aspectRatio, 1.0f, 50.0f); // Отсекающая перспектива
// Восстановливает матрицу преобразования в исходный режим вида
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
//**********************************************************
//**********************************************************
// Вспомогательный код
//**********************************************************
//**********************************************************
////////////////////////////////////////////////////////////////////
// Apply an actors transform given it's frame of reference
void gltApplyActorTransform(GLTFrame *pFrame)
{
GLTMatrix mTransform;
gltGetMatrixFromFrame(pFrame, mTransform);
glMultMatrixf(mTransform);
}
//////////////////////////////////////////////////////////////////
// Apply a camera transform given a frame of reference. This is
// pretty much just an alternate implementation of gluLookAt using
// floats instead of doubles and having the forward vector specified
// instead of a point out in front of me.
void gltApplyCameraTransform(GLTFrame *pCamera)
{
GLTMatrix mMatrix;
GLTVector3 vAxisX;
GLTVector3 zFlipped;
zFlipped[0] = -pCamera->vForward[0];
zFlipped[1] = -pCamera->vForward[1];
zFlipped[2] = -pCamera->vForward[2];
// Derive X vector
gltVectorCrossProduct(pCamera->vUp, zFlipped, vAxisX);
// Populate matrix, note this is just the rotation and is transposed
mMatrix[0] = vAxisX[0];
mMatrix[4] = vAxisX[1];
mMatrix[8] = vAxisX[2];
mMatrix[12] = 0.0f;
mMatrix[1] = pCamera->vUp[0];
mMatrix[5] = pCamera->vUp[1];
mMatrix[9] = pCamera->vUp[2];
mMatrix[13] = 0.0f;
mMatrix[2] = zFlipped[0];
mMatrix[6] = zFlipped[1];
mMatrix[10] = zFlipped[2];
mMatrix[14] = 0.0f;
mMatrix[3] = 0.0f;
mMatrix[7] = 0.0f;
mMatrix[11] = 0.0f;
mMatrix[15] = 1.0f;
// Do the rotation first
glMultMatrixf(mMatrix);
// Now, translate backwards
glTranslatef(-pCamera->vLocation[0], -pCamera->vLocation[1], -pCamera->vLocation[2]);
}
#define GLT_PI 3.14159265358979323846
// For best results, put this in a display list
// Draw a torus (doughnut) at z = fZVal... torus is in xy plane
void gltDrawTorus(GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor)
{
GLTVector3 vNormal;
double majorStep = 2.0f*GLT_PI / numMajor;
double minorStep = 2.0f*GLT_PI / numMinor;
int i, j;
for (i=0; i < numMajor; ++i)
{
double a0 = i * majorStep;
double a1 = a0 + majorStep;
GLfloat x0 = (GLfloat) cos(a0);
GLfloat y0 = (GLfloat) sin(a0);
GLfloat x1 = (GLfloat) cos(a1);
GLfloat y1 = (GLfloat) sin(a1);
glBegin(GL_TRIANGLE_STRIP);
for (j=0; j<=numMinor; ++j)
{
double b = j * minorStep;
GLfloat c = (GLfloat) cos(b);
GLfloat r = minorRadius * c + majorRadius;
GLfloat z = minorRadius * (GLfloat) sin(b);
// First point
glTexCoord2f((float)(i)/(float)(numMajor), (float)(j)/(float)(numMinor));
vNormal[0] = x0*c;
vNormal[1] = y0*c;
vNormal[2] = z/minorRadius;
gltNormalizeVector(vNormal);
glNormal3fv(vNormal);
glVertex3f(x0*r, y0*r, z);
glTexCoord2f((float)(i+1)/(float)(numMajor), (float)(j)/(float)(numMinor));
vNormal[0] = x1*c;
vNormal[1] = y1*c;
vNormal[2] = z/minorRadius;
glNormal3fv(vNormal);
glVertex3f(x1*r, y1*r, z);
}
glEnd();
}
}
// Initialize a frame of reference.
// Uses default OpenGL viewing position and orientation
void gltInitFrame(GLTFrame *pFrame)
{
pFrame->vLocation[0] = 0.0f;
pFrame->vLocation[1] = 0.0f;
pFrame->vLocation[2] = 0.0f;
pFrame->vUp[0] = 0.0f;
pFrame->vUp[1] = 1.0f;
pFrame->vUp[2] = 0.0f;
pFrame->vForward[0] = 0.0f;
pFrame->vForward[1] = 0.0f;
pFrame->vForward[2] = -1.0f;
}
/////////////////////////////////////////////////////////
// March a frame of reference forward. This simply moves
// the location forward along the forward vector.
void gltMoveFrameForward(GLTFrame *pFrame, GLfloat fStep)
{
pFrame->vLocation[0] += pFrame->vForward[0] * fStep;
pFrame->vLocation[1] += pFrame->vForward[1] * fStep;
pFrame->vLocation[2] += pFrame->vForward[2] * fStep;
}
/////////////////////////////////////////////////////////
// Rotate a frame around it's local Y axis
void gltRotateFrameLocalY(GLTFrame *pFrame, GLfloat fAngle)
{
GLTMatrix mRotation;
GLTVector3 vNewForward;
gltRotationMatrix((float)gltDegToRad(fAngle), 0.0f, 1.0f, 0.0f, mRotation);
gltRotationMatrix(fAngle, pFrame->vUp[0], pFrame->vUp[1], pFrame->vUp[2], mRotation);
gltRotateVector(pFrame->vForward, mRotation, vNewForward);
memcpy(pFrame->vForward, vNewForward, sizeof(GLTVector3));
}
///////////////////////////////////////////////////////////////////
// Derives a 4x4 transformation matrix from a frame of reference
void gltGetMatrixFromFrame(GLTFrame *pFrame, GLTMatrix mMatrix)
{
GLTVector3 vXAxis; // Derived X Axis
// Calculate X Axis
gltVectorCrossProduct(pFrame->vUp, pFrame->vForward, vXAxis);
// Just populate the matrix
// X column vector
memcpy(mMatrix, vXAxis, sizeof(GLTVector3));
mMatrix[3] = 0.0f;
// y column vector
memcpy(mMatrix+4, pFrame->vUp, sizeof(GLTVector3));
mMatrix[7] = 0.0f;
// z column vector
memcpy(mMatrix+8, pFrame->vForward, sizeof(GLTVector3));
mMatrix[11] = 0.0f;
// Translation/Location vector
memcpy(mMatrix+12, pFrame->vLocation, sizeof(GLTVector3));
mMatrix[15] = 1.0f;
}
// Rotates a vector using a 4x4 matrix. Translation column is ignored
void gltRotateVector(const GLTVector3 vSrcVector, const GLTMatrix mMatrix, GLTVector3 vOut)
{
vOut[0] = mMatrix[0] * vSrcVector[0] + mMatrix[4] * vSrcVector[1] + mMatrix[8] * vSrcVector[2];
vOut[1] = mMatrix[1] * vSrcVector[0] + mMatrix[5] * vSrcVector[1] + mMatrix[9] * vSrcVector[2];
vOut[2] = mMatrix[2] * vSrcVector[0] + mMatrix[6] * vSrcVector[1] + mMatrix[10] * vSrcVector[2];
}
///////////////////////////////////////////////////////////////////////////////
// Creates a 4x4 rotation matrix, takes radians NOT degrees
void gltRotationMatrix(float angle, float x, float y, float z, GLTMatrix mMatrix)
{
float vecLength, sinSave, cosSave, oneMinusCos;
float xx, yy, zz, xy, yz, zx, xs, ys, zs;
// If NULL vector passed in, this will blow up...
if(x == 0.0f y == 0.0f z == 0.0f)
{
gltLoadIdentityMatrix(mMatrix);
return;
}
// Scale vector
vecLength = (float)sqrt( x*x + y*y + z*z );
// Rotation matrix is normalized
x /= vecLength;
y /= vecLength;
z /= vecLength;
sinSave = (float)sin(angle);
cosSave = (float)cos(angle);
oneMinusCos = 1.0f - cosSave;
xx = x * x;
yy = y * y;
zz = z * z;
xy = x * y;
yz = y * z;
zx = z * x;
xs = x * sinSave;
ys = y * sinSave;
zs = z * sinSave;
mMatrix[0] = (oneMinusCos * xx) + cosSave;
mMatrix[4] = (oneMinusCos * xy) - zs;
mMatrix[8] = (oneMinusCos * zx) + ys;
mMatrix[12] = 0.0f;
mMatrix[1] = (oneMinusCos * xy) + zs;
mMatrix[5] = (oneMinusCos * yy) + cosSave;
mMatrix[9] = (oneMinusCos * yz) - xs;
mMatrix[13] = 0.0f;
mMatrix[2] = (oneMinusCos * zx) - ys;
mMatrix[6] = (oneMinusCos * yz) + xs;
mMatrix[10] = (oneMinusCos * zz) + cosSave;
mMatrix[14] = 0.0f;
mMatrix[3] = 0.0f;
mMatrix[7] = 0.0f;
mMatrix[11] = 0.0f;
mMatrix[15] = 1.0f;
}
///////////////////////////////////////////////////////////////////////////////
// Load a matrix with the Idenity matrix
void gltLoadIdentityMatrix(GLTMatrix m)
{
static GLTMatrix identity = { 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };
memcpy(m, identity, sizeof(GLTMatrix));
}
Добавьте
в файл LightAndMaterial.cpp код, подключающий
новое упражнение// LightAndMaterial.cpp : Defines the entry point for the console application.
//
//**********************************************************
// Подключение стандартного файла с библиотекой OpenGL
#include "stdafx.h"
//**********************************************************
// Прототипы функций
void ExecuteMenu(int); // Контекстное меню первого уровня
void TimerFunc(int); // Обработчик события таймера
void SpecialKeys(int, int, int); // Обработка нажатия клавиш
void RenderScene(void); // Функция рендеринга
void ChangeSize(int, int); // Функция установки отсекающего объема
void CrystalAndSpotExecuteMenu(int);// Подменю
// Глобальная переменная выбранного варианта основного меню
int choice = 1;
// Глобальные переменные для создания вращения
// в градусах
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
GLfloat zRot = 0.0f;
GLint w, h; // Ширина и высота экрана
//**********************************************************
// Подключение файлов с упражнениями
#include "ColorCube.h" // Упражнение 1: "1) Куб цвета"
#include "Jet.h" // Упражнение 2: "2) Самолет без освещения"
#include "LightSphere.h" // Упражнение 3: "3) Рисование освещенной сферы"
#include "MultiMaterial.h" // Упражнение 4: "4) Рисование с разными материалами"
#include "ColorSphere.h" // Упражнение 5: "5) Способ согласования цветов"
#include "JetNight.h" // Упражнение 6: "6) Самолет с нулевым освещением"
#include "JetLight.h" // Упражнение 7: "7) Самолет с равномерным освещением"
#include "JetShiny.h" // Упражнение 8: "8) Самолет с зеркальным отражением"
#include "CrystalAndSpot.h" // Упражнение 9: "9) Рисование кристалла и прожектора"
#include "JetShadow.h" // Упражнение 10: "10) Модель самолета и его тени"
#include "SphereWorld.h" // Упражнение 11: "11) Освещенный мир сфер"
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
// Сбрасываем углы вращения прежнего варианта
xRot = yRot = zRot = 0;
// Выключаем освещение
glDisable(GL_LIGHTING);
// Выключаем режим согласования цветов
glDisable(GL_COLOR_MATERIAL);
// Запоминаем выбор в глобальную переменную
::choice = choice;
switch(::choice)
{
case 1:
ChangeSizeColorCube(w, h);
break;
case 2:
ChangeSizeJet(w, h);
break;
case 3:
ChangeSizeLightSphere(w, h);
break;
case 4:
ChangeSizeMultiMaterial(w, h);
break;
case 5:
ChangeSizeColorSphere(w, h);
break;
case 6:
ChangeSizeJetNight(w, h);
break;
case 7:
ChangeSizeJetLight(w, h);
break;
case 8:
ChangeSizeJetShiny(w, h);
break;
case 10:
ChangeSizeJetShadow(w, h);
break;
case 11:
ChangeSizeSphereWorld(w, h);
break;
}
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//****************************************************
// Функция обратного вызова обработки выбора пользователя в подменю
void CrystalAndSpotExecuteMenu(int choice)
{
xRot = yRot = 0;
ChangeSizeCrystalAndSpot(w, h);
switch(choice){
case 0:
iShade = MODE_FLAT;
break;
case 1:
iShade = MODE_SMOOTH;
break;
case 2:
iTess = MODE_VERYLOW;
break;
case 3:
iTess = MODE_MEDIUM;
break;
case 4:
default:
iTess = MODE_VERYHIGH;
}
// Устанавливаем режим и вызываем принудительно
// функцию визуализации, где размещен
// вызов функции рисования кристалла и прожектора
::choice = 9;
// Вызвать принудительно визуализацию
glutPostRedisplay();
}
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
// Сохранить прежние настройки OpenGL в стеке атрибутов
glPushAttrib(GL_LIGHTING_BIT);
switch(::choice)
{
case 1:
RenderSceneColorCube();
break;
case 2:
RenderSceneJet();
break;
case 3:
RenderSceneLightSphere();
break;
case 4:
RenderSceneMultiMaterial();
break;
case 5:
RenderSceneColorSphere();
break;
case 6:
RenderSceneJetNight();
break;
case 7:
RenderSceneJetLight();
break;
case 8:
RenderSceneJetShiny();
break;
case 9:
RenderSceneCrystalAndSpot();
break;
case 10:
RenderSceneJetShadow();
break;
case 11:
RenderSceneSphereWorld();
break;
}
// Восстановить прежние настройки OpenGL из стека атрибутов
glPopAttrib();
}
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
void ChangeSize(int width, int height)
{
w = width;
h = height;
switch(::choice)
{
case 1:
ChangeSizeColorCube(width, height);
break;
case 2:
ChangeSizeJet(width, height);
break;
case 3:
ChangeSizeLightSphere(width, height);
break;
case 4:
ChangeSizeMultiMaterial(width, height);
break;
case 5:
ChangeSizeColorSphere(width, height);
break;
case 6:
ChangeSizeJetNight(width, height);
break;
case 7:
ChangeSizeJetLight(width, height);
break;
case 8:
ChangeSizeJetShiny(width, height);
break;
case 9:
ChangeSizeCrystalAndSpot(width, height);
break;
case 10:
ChangeSizeJetShadow(width, height);
break;
case 11:
ChangeSizeSphereWorld(width, height);
break;
}
}
//**********************************************************
// Обработчик события таймера
void TimerFunc(int value)
{
glutPostRedisplay(); // Перерисовка сцены
switch(::choice)
{
case 11:
glutTimerFunc(3, TimerFunc, 1);
break;
default:
glutTimerFunc(30, TimerFunc, 1);
}
}
//**********************************************************
// Управление с клавиатуры стрелками
// для задания новых значений матрицы поворота
void SpecialKeys(int key, int x, int y)
{
switch(::choice)
{
default:
if(key == GLUT_KEY_UP) // Стрелка вверх
xRot -= 5.0f;
if(key == GLUT_KEY_DOWN)// Стрелка вниз
xRot += 5.0f;
if(key == GLUT_KEY_LEFT)// Стрелка влево
yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT)// Стрелка вправо
yRot += 5.0f;
xRot = (GLfloat)((const int)xRot % 360);
yRot = (GLfloat)((const int)yRot % 360);
// Для упражнения 5 смены материала
if(key == GLUT_KEY_F1) // Меняем красный
{
diffuseMaterial[0] += 0.1;
*diffuseMaterial = *diffuseMaterial > 1.0 ? 0.0 : *diffuseMaterial;
glColor4fv(diffuseMaterial);
}
if(key == GLUT_KEY_F2) // Меняем зеленый
{
diffuseMaterial[1] += 0.1;
*(diffuseMaterial + 1) = diffuseMaterial[1] > 1.0 ? 0.0 : diffuseMaterial[1];
glColor4fv(diffuseMaterial);
}
if(key == GLUT_KEY_F3) // Меняем синий
{
diffuseMaterial[2] += 0.1;
*(diffuseMaterial + 2) = diffuseMaterial[2] > 1.0 ? 0.0 : diffuseMaterial[2];
glColor4fv(diffuseMaterial);
}
break;
case 11:
SpecialKeysSphereWorld(key);
}
// Вызвать принудительно визуализацию с помощью RenderScene()
glutPostRedisplay();
}
//**********************************************************
void main(int argc, char* argv[])
{
glutInit(argc, argv);
// Двойная буферизация, цветовая модель RGB, буфер глубины
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(300, 300); // Начальные размеры окна
glutCreateWindow("Свет и материалы"); // Заголовок окна
glutDisplayFunc(RenderScene); // Обновление сцены при разрушении окна
glutReshapeFunc(ChangeSize); // При изменении размера окна
glutTimerFunc(100, TimerFunc, 1); // Создали таймер
glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
// Создаем подменю для рисования кристалла и прожектора
int CrystalAndSpot_menu = glutCreateMenu(CrystalAndSpotExecuteMenu);
glutAddMenuEntry("Плоское затенение поверхностей", 0);
glutAddMenuEntry("Гладкое (градиентное) затенение", 1);
glutAddMenuEntry("Низкая тесселяция (крупные грани)", 2);
glutAddMenuEntry("Средняя тесселяция (больше граней)", 3);
glutAddMenuEntry("Высокая аппроксимация (много граней)", 4);
// Создание меню и добавление опций выбора
glutCreateMenu(ExecuteMenu);
glutAddMenuEntry("1) Куб цвета", 1);
glutAddMenuEntry("2) Самолет без освещения", 2);
glutAddMenuEntry("3) Рисование освещенной сферы", 3);
glutAddMenuEntry("4) Рисование с разными материалами", 4);
glutAddMenuEntry("5) Способ согласования цветов (F1, F2, F3)", 5);
glutAddMenuEntry("6) Самолет с нулевым освещением", 6);
glutAddMenuEntry("7) Самолет с равномерным освещением", 7);
glutAddMenuEntry("8) Самолет с зеркальным отражением", 8);
glutAddSubMenu("9) Рисование кристалла и прожектора", CrystalAndSpot_menu);
glutAddMenuEntry("10) Модель самолета и его тени", 10);
glutAddMenuEntry("11) Освещенный мир сфер", 11);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutMainLoop(); // Цикл сообщений графического окна
}
Запустите
приложение и выполните упражнение 11, результат должен
быть примерно таким
Поуправляйте
сценой с помощью клавиш-стрелок и попытайтесь разобраться
с кодом
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.