Свет и материалы в OpenGL
Модель освещения
В OpenGL различают четыре типа создаваемого освещения (соответственно, четыре типа источников света):
- Фоновое (ambient) освещение (рассредоточенное освещение, свет окружающей среды) - свет, отраженный окружающими объектами столько раз, что его направление невозможно установить. Он выглядит приходящим со всех направлений. Объекты, освещенные рассеянным светом, равномерно окрашиваются со всех сторон и во всех направлениях. Такой свет не дает тени и отражается от поверхности во всех направлениях.
- Диффузный (или рассеянное, diffuse) свет - свет, поступающий из определенного направления, но равномерно отражается от поверхности во всех направлениях. При прямом попадании на поверхность он более яркий, чем при легком прикосновении к ней вскользь.
- Отраженный (или зеркальный, specular) свет - приходит из конкретного направления и отражается от поверхности также в определенном направлении. Отражающую способность поверхности можно представить как свойство с названием блеск ( shininess ). Сильно отраженный свет обычно дает яркое пятно на поверхности, именуемое "зайчиком" или "бликом".
- Излучаемый (или эмиссионный, emission) свет - свет, который исходит от самого объекта. В модели освещения OpenGL цвет самосвечения добавляет яркости самому объекту и не зависит от любых других источников света. Этот свет не вносит дополнительного освещения в сцену, т.е. не освещает соседние объекты.
Можно считать, что фоновое освещение создается бесконечным множеством источников света, заполняющих весь объем сцены. Удобно считать, что направленный свет (диффузный и отраженный) создается прожектором ( spotlights ), бесконечно удаленным от поверхности освещения и генерирующим параллельные лучи. Примером параллельного источника света является Солнце.
В OpenGL понятие модели освещения слагается из следующих компонентов
- Интенсивность общего фонового освещения.
- Положение точки обзора - локального по отношению к сцене или бесконечно удаленного.
- Расчет освещенности для лицевых и оборотных граней объектов.
- Требования, должен или нет отраженный цвет быть отделен от фонового и рассеянного и наложен на объект после операций текстурирования.
Для задания всех параметров модели освещения используется команда glLightModel*(), которая имеет два аргумента: имя параметра модели освещения и значение для этого параметра. Синтаксис команды следующий:
void glLightModel{if}(GLenum pname, TYPE param); void glLightModel{if}v(GLenum pname, TYPE *param);
Команда устанавливает свойство модели освещения, идентифицируемое аргументом pname (описание приведено в таблице). Аргумент param - это собственно значение свойства, а если используется векторная версия команды - указатель на набор значений. Невекторная версия команды используется только для установки одиночных параметров и неприменима для режима GL_LIGHT_MODEL_AMBIENT.
Модель самолета без использования освещения (Jet)
В качестве иллюстрации выполним несколько примеров по моделированию поверхности реактивного самолета, в которых последовательно, сначала без использования освещения, затем применяя его в разных вариантах, рассмотрим особенности света и материалов.
Вначале мы просто нарисуем ряд цветных треугольников, составляющих поверхность модели реактивного самолета.
- Заполните файл 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(); }Листинг 23.6. Код файла Jet.h без использования освещения
- Модифицируйте файл 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(); // Цикл сообщений графического окна }Листинг 23.7. Дополненный файл LightAndMaterial.cpp
- Разберитесь с кодом, запустите упражнение, поуправляйте изображением с помощью клавиш-стрелок, полюбуйтесь самолетом.
Снимки экрана могут быть такими
Обратите внимание, что в данном коде мы никакого освещения сцены не применяли. Самолет разукрашен автономными цветными поверхностями составляющих треугольников. Начнем реализацию освещения на самом простом примере - рисовании освещенной сферы.