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

Свет и материалы в OpenGL

Рисование освещенной сферы (LightSphere)

  • Добавьте к приложению новый заголовочный файл с именем LightSphere.h и заполните его следующим кодом
// LightSphere.h
// Упражнение 3: "3) Рисование освещенной сферы"
  
//**********************************************************
// Прототипы
void SetLight();        
void RenderSceneLightSphere();
void ChangeSizeLightSphere(int, int);
  
void SetLight()
{
  GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};// Цвет блика
  GLfloat mat_shininess[] = {50.0}; // Размер блика (обратная пропорция)
  GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0};// Расположение источника
  GLfloat white_light[] = {1.0, 1.0, 1.0, 1.0};// Цвет и интенсивность 
                  // освещения, генерируемого источником
  GLfloat lmodel_ambient[] = {0.5, 0.5, 0.5, 1.0};// Параметры фонового освещения
  
  glClearColor(0.0, 0.0, 0.0, 1.0);
  glShadeModel(GL_SMOOTH);
  
  glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
  glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
  
  glLightfv(GL_LIGHT0, GL_POSITION, light_position);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
  glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
  
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glEnable(GL_DEPTH_TEST);
}
  
//**********************************************************
void RenderSceneLightSphere()
{
  // Сбрасываем буфер цвета и буфер глубины
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  glPushMatrix();// Сохранить матрицу преобразования модели
  
  glRotatef(xRot, 1.0f, 0.0f, 0.0f);
  glRotatef(yRot, 0.0f, 1.0f, 0.0f);
  glRotatef(zRot, 0.0f, 0.0f, 1.0f);
  
  // Для анимации
  xRot += 1.0f;
  yRot += 1.0f;
  zRot += 1.0f;
  xRot = (GLfloat)((const int)xRot % 360);
  yRot = (GLfloat)((const int)yRot % 360);
  zRot = (GLfloat)((const int)zRot % 360);
  
  // Рисуем сферу
  glutSolidSphere(1.0, 20, 16);
  
  glPopMatrix();// Восстановить матрицу преобразования модели
  
  // Переключить буфер
  glutSwapBuffers();
  
  // Прокачка сообщений
  glFlush();
}
  
//**********************************************************
void ChangeSizeLightSphere(int width, int height)
{  
  // Индивидуальные настройки освещения
  SetLight();
  
  // Предотвращаем деление на нуль
  if(height == 0)
    height = 1;
  
  // Устанавливаем поле просмотра с размерами окна
  glViewport(0, 0, width, height);
  
  // Устанавливает матрицу преобразования в режим проецирования
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  
  // Устанавливаем размеры ортогонального отсекающего объема
  GLfloat aspectRatio = (GLfloat)width / (GLfloat)height;// Для соблюдения пропорций
  
  if (width <= height) 
        glOrtho (-1.5, 1.5, -1.5 / aspectRatio, 1.5 / aspectRatio, -10.0, 10.0);
    else 
        glOrtho (-1.5 * aspectRatio, 1.5 * aspectRatio, -1.5, 1.5, -10, 10);
  
  // Восстановливает матрицу преобразования в исходный режим вида
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}
Листинг 23.8. Код файла LightSphere.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) Самолет без освещения"
#include "LightSphere.h"  // Упражнение 3: "3) Рисование освещенной сферы"
  
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
  // Сбрасываем углы вращения прежнего варианта
  xRot = yRot = zRot = 0;
  
  // Выключаем освещение
  glDisable(GL_LIGHTING);
  
  // Запоминаем выбор в глобальную переменную
  ::choice = choice; 
  
  switch(::choice)
  {
    case 1:
      ChangeSizeColorCube(w, h);
      break;
    case 2:
      ChangeSizeJet(w, h);
      break;
    case 3:
      ChangeSizeLightSphere(w, h);
      break;
  }
    
  // Вызвать принудительно визуализацию
  glutPostRedisplay();
}
  
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
  switch(::choice)
  {
    case 1:
      RenderSceneColorCube();
      break;
    case 2:
      RenderSceneJet();
      break;
    case 3:
      RenderSceneLightSphere();
      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;
    case 3:
      ChangeSizeLightSphere(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);
  glutAddMenuEntry("3) Рисование освещенной сферы", 3);
  
  glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем 
  // Конец создания меню
  
  glutMainLoop();  // Цикл сообщений графического окна
}
Листинг 23.9. Дополненный файл LightAndMaterial.cpp
  • Разберитесь с кодом, поэкспериментируйте с массивами функции SetLight(), которая настраивает освещение сцены. Один из снимков результата получился такой


Материал и его свойства

Свет - это лишь один компонент общей картины при моделировании освещенной сцены. Вторым компонентом является собственный цвет поверхности объектов, которые определяется его отражательными свойствами. Синий объект отражает преимущественно синие фотоны и поглащает остальные. В реальном мире естественный свет является белым (состоящий в равных долях из базовых RGB - цветов), поэтому все предметы освещаются в их естественном цвете.

В компьютерном мире мы можем осветить объект, который в естественном свете способен быть синим (отражать синие фотоны), другим светом, в котором нет синей составляющей для отражения, и объект будет выглядеть черным. Поэтому для моделирования освещения объектов сцены нужно не только правильно задавать положение источников света и их свойства, но и определять правильные свойства самих освещаемых поверхностей объектов.

Свойства объектов, определяющие взаимодействие их поверхностей со светом, называются материалами. Материал определяет цвет и способ его отражения от освещенного объекта. Вместо того, чтобы сказать, что многоугольник красный, мы говорим, что многоугольник сделан из материала, отражающего преимущественно красный цвет. Тем самым мы подчеркиваем, что подразумевается добавление освещения в сцену и иная последовательность действий:

  1. Определить векторы нормалей для каждой вершины каждого объекта. Они устанавливают ориентацию объекта относительно источников света.
  2. Создать, настроить и расположить один или несколько источников света.
  3. Создать и настроить модель освещения, которая определяет уровень фонового света и правильное местоположение точки обзора, подходящее для вычисления освещенности.
  4. Определить свойства материала для объектов в сцене.

Согласно модели освещения OpenGL, цвет материала задается в зависимости от того, какой процент падающего красного, зеленого или синего света поверхность должна отразить. Например, абсолютно красный мяч отбрасывает весь приходящий красный свет и полностью поглощает зеленый и синий свет. Соответственно, если посмотреть на этот мяч при белом освещении, которое состоит из одинакового количества красного, зеленого и синего света, то весь красный свет отразится и будет виден красный мяч. Освещенный беспримесным красным светом мяч также выглядит красным. Но если на мяч посмотреть при абсолютно зеленом свете, то мяч будет черным, поскольку зеленый свет полностью поглощается, а красной составляющей нет - следовательно нечего и отражать.

Как и источники света, материалы имеют различные фоновые ( ambient ), рассеянные ( diffuse ) или отраженные ( specular ) цвета, которые определяют способность материала отражать ту или иную RGB-составляющую тем или иным образом. На приведенном результате моделирования сферы материал освещенной части имеет свойство diffuse, блик задан свойством specular, а оборотная сторона сферы отражает фоновый свет ambient.


При расчетах отдельные компоненты объединяются: фоновый свет источника и фоновый цвет материала, диффузный свет источника и рассеивающий цвет материала, коэффициент отражения света с бликовым пятном материала. "Фоновое" и "рассеивающее" свойства материала определяют цвет объекта и обычно если не одинаковы, то похожи. Отражающая способность обычно соответствует белому или серому свету, но так, чтобы наиболее яркие места на поверхности объекта по цвету совпадали с зеркальной составляющей источника. Например, красная полированная сфера в ярких солнечных лучах будет красной, но самые освещенные точки (блики) будут белыми. (Короче, Склифасовский!: фабрики рабочим - земля крестьянам; кесарю кесарево - пекарю пекарево; свет источникам - цвет материалам ; ...).

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