Китай |
Свет и материалы в OpenGL
Кристалл и прожектор (CrystalAndSpot)
Продолжим знакомство с источниками света и отражательными свойствами материала. Будем рассматривать сферу с различной степенью аппроксимации плоскими гранями, которую будем освещать источником света со свойствами прожектора. Прожектор представляет собой конус света с заданным углом 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); }Листинг 23.28. Код файла CrystalAndSpot.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); // Функция установки отсекающего объема 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(); // Цикл сообщений графического окна }Листинг 23.29. Код подключения модели самолета с зеркальным отражением в файле LightAndMaterial.cpp
- Запустите приложение и выполните упражнение 9, результат должен быть примерно таким
- Попытайтесь разобраться с кодом!!!
Переменные iTess и iMode контролируют, на сколько участков разбивается сфера и какое затенение применяется - плоское или гладкое. Обратите внимание, что осуществляется управление положением не объекта, а прожектора. Если специфицировать прожектор с меньшим углом конуса света, например, в функции SetLightCrystalAndSpot() таким кодом
// Спецификация источника как прожектора glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 20.0f);
то мы действительно увидим световое пятно на кристалле, сходное с лучем прожектора.