Опубликован: 13.07.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 22:

Свет и материалы в 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 или указатель на набор значений (в векторной версии).

Перечень свойств источника света, подлежащих настройке функцией glLight*()
Значение параметра 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 Квадратичное затухание света

OpenGL поддерживает по меньшей мере 8 независимый источников света, расположенных в любых точках сцены или даже вне отображаемого (отсекающего) объема. Мы можем поместить источник света в бесконечности, сделав его лучи параллельными, или заставить близлежащий источник света излучать во все стороны. Можно установить источник света, излучающий в пределах заданного конуса, и приписать ему любые характеристики.

Но всегда следует помнить, что добавление каждого нового источника света и наделение его всякими (зачастую, не нужными) свойствами требует значительных вычислений при рисовании, т.е. существенно сказывается на производительности программы. Как видно из таблицы, OpenGL позволяет связать с каждым источником света три различных параметра цвета: GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR.

Для того, чтобы использовать источники света, системе необходима дополнительная информация о нормалях к вершинам поверхностей. До сих пор мы эти нормали не вычисляли, хотя и строили освещенные сферы. Но нормали вычислялись неявно самой используемой нами сервисной функцией

glutSolidSphere(1.0, 20, 16);

Под нормалью к вершине поверхности подразумевается вектор, исходящий прочь от поверхности и перпендикулярный к семейству касательных, проведенных через вершину. Обычно используются единичные векторы нормали.

После создания и настройки источников света их необходимо включить с помощью команды glEnable(GL_LIGHTING), тем самым сообщив OpenGL о необходимости принимать во внимание при рисовании сцены условия освещенности.

Если освещение включено, но материал, источник освещения или нормали не заданы, то объект останется темным и неосвещенным (будет виден черный силуэт, если фон имеет цвет).

Модель самолета с нулевым освещением (JetNight)

Приведем пример, когда OpenGl получила инструкцию учитывать освещение, но само освещение осталось не заданным.

  • В панели Solution Explorer добавьте к проекту новый заголовочный файл JetNight.h


  • Скопируйте в новый файл 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)


Без применения освещения (упражнение 2)

Обратите внимание, что объект остается абсолютно неосвещенным только тогда, когда мы вызываем упражнение 6 первым после запуска приложения. После выполнения других упражнений, где задаются источники света и материалы, мы уже не получим абсолютно неосвещенного объекта в упражнении 6, поскольку OpenGL приобретет от других упражнений некоторые настройки на использование освещения.

Иван Циферблат
Иван Циферблат
Россия, Таганрог, 36, 2000