Китай |
Свет и материалы в OpenGL
Источники света и их свойства
Дошла очередь и до рассмотрения источников света. Источники света характеризуются набором свойств, включая цвет, яркость, положение, ориентацию и т.д., которые могут очень сильно влиять на вид сцены. Свойства источников света, в нашем примере, устанавливаются функцией glLightfv(), которая вызывается несколько раз для настройки свойств источника, но каждый раз с разными аргументами настройки. В общем случае создания и настройки источников света имеет вид
void glLight{if}(GLenum light, GLenum pname, TYPE param); void glLight{if}v(GLenum light, GLenum pname, TYPE *param);
Параметр light имеет значения GL_LIGHT0, ..., GL_LIGHT7, которые определяют номерное имя создаваемого источника. Настраиваемое свойство указывается одним из значений перечисления pname, приведенных в таблице ниже. Аргумент param - это собственно значение настраиваемого свойства pname или указатель на набор значений (в векторной версии).
OpenGL поддерживает по меньшей мере 8 независимый источников света, расположенных в любых точках сцены или даже вне отображаемого (отсекающего) объема. Мы можем поместить источник света в бесконечности, сделав его лучи параллельными, или заставить близлежащий источник света излучать во все стороны. Можно установить источник света, излучающий в пределах заданного конуса, и приписать ему любые характеристики.
Но всегда следует помнить, что добавление каждого нового источника света и наделение его всякими (зачастую, не нужными) свойствами требует значительных вычислений при рисовании, т.е. существенно сказывается на производительности программы. Как видно из таблицы, OpenGL позволяет связать с каждым источником света три различных параметра цвета: GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR.
Для того, чтобы использовать источники света, системе необходима дополнительная информация о нормалях к вершинам поверхностей. До сих пор мы эти нормали не вычисляли, хотя и строили освещенные сферы. Но нормали вычислялись неявно самой используемой нами сервисной функцией
glutSolidSphere(1.0, 20, 16);
Под нормалью к вершине поверхности подразумевается вектор, исходящий прочь от поверхности и перпендикулярный к семейству касательных, проведенных через вершину. Обычно используются единичные векторы нормали.
После создания и настройки источников света их необходимо включить с помощью команды glEnable(GL_LIGHTING), тем самым сообщив OpenGL о необходимости принимать во внимание при рисовании сцены условия освещенности.
Модель самолета с нулевым освещением (JetNight)
Приведем пример, когда OpenGl получила инструкцию учитывать освещение, но само освещение осталось не заданным.
- Скопируйте в новый файл JetNight.h содержимое файла Jet.h
- Добавьте в коде файла JetNight.h к именам функций и их прототипам окончание Night, чтобы не было конфликта имен в проекте приложения, например, для прототипов
void Jet() ; |
-> | void JetNight() ; |
void RenderSceneJet(void) ; |
-> |
void RenderSceneJetNight(void) ; |
void ChangeSizeJet(int, int) ; |
-> | 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(); // Цикл сообщений графического окна }Листинг 23.23. Код подключения модели самолета с нулевым освещением в файле LightAndMaterial.cpp
- Запустите приложение и выполните упражнение 6, результат должен быть примерно таким, как на первом рисунке
Обратите внимание, что объект остается абсолютно неосвещенным только тогда, когда мы вызываем упражнение 6 первым после запуска приложения. После выполнения других упражнений, где задаются источники света и материалы, мы уже не получим абсолютно неосвещенного объекта в упражнении 6, поскольку OpenGL приобретет от других упражнений некоторые настройки на использование освещения.