| Китай |
Свет и материалы в 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);
то мы действительно увидим световое пятно на кристалле, сходное с лучем прожектора.




