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

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

Модель освещения

В OpenGL различают четыре типа создаваемого освещения (соответственно, четыре типа источников света):

  1. Фоновое (ambient) освещение (рассредоточенное освещение, свет окружающей среды) - свет, отраженный окружающими объектами столько раз, что его направление невозможно установить. Он выглядит приходящим со всех направлений. Объекты, освещенные рассеянным светом, равномерно окрашиваются со всех сторон и во всех направлениях. Такой свет не дает тени и отражается от поверхности во всех направлениях.
  2. Диффузный (или рассеянное, diffuse) свет - свет, поступающий из определенного направления, но равномерно отражается от поверхности во всех направлениях. При прямом попадании на поверхность он более яркий, чем при легком прикосновении к ней вскользь.
  3. Отраженный (или зеркальный, specular) свет - приходит из конкретного направления и отражается от поверхности также в определенном направлении. Отражающую способность поверхности можно представить как свойство с названием блеск ( shininess ). Сильно отраженный свет обычно дает яркое пятно на поверхности, именуемое "зайчиком" или "бликом".
  4. Излучаемый (или эмиссионный, emission) свет - свет, который исходит от самого объекта. В модели освещения OpenGL цвет самосвечения добавляет яркости самому объекту и не зависит от любых других источников света. Этот свет не вносит дополнительного освещения в сцену, т.е. не освещает соседние объекты.

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

В OpenGL понятие модели освещения слагается из следующих компонентов

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

Для задания всех параметров модели освещения используется команда glLightModel*(), которая имеет два аргумента: имя параметра модели освещения и значение для этого параметра. Синтаксис команды следующий:

void glLightModel{if}(GLenum pname, TYPE param);
void glLightModel{if}v(GLenum pname, TYPE *param);

Команда устанавливает свойство модели освещения, идентифицируемое аргументом pname (описание приведено в таблице). Аргумент param - это собственно значение свойства, а если используется векторная версия команды - указатель на набор значений. Невекторная версия команды используется только для установки одиночных параметров и неприменима для режима GL_LIGHT_MODEL_AMBIENT.

Компоненты модели освещения, подлежащие выбору командой glLightModel*()
Значение параметра pname Значение по умолчанию Описание
GL_LIGHT_MODEL_AMBIENT (0.2, 0.2, 0.2, 1.0) Фоновая RGBA - интенсивность всей сцены
GL_LIGHT_MODEL_LOCAL_VIEWER 0.0 или GL_FALSE Способ вычисления углов зеркального отражения (точка обзора локальная или в бесконечности)
GL_LIGHT_MODEL_TWO_SIDE 0.0 или GL_FALSE Выбор между односторонним и двухсторонним освещением
GL_LIGHT_MODEL_COLOR_CONTROL GL_SINGLE_COLOR Вычисляется ли отраженный цвет отдельно от фонового или рассеянного

Модель самолета без использования освещения (Jet)

В качестве иллюстрации выполним несколько примеров по моделированию поверхности реактивного самолета, в которых последовательно, сначала без использования освещения, затем применяя его в разных вариантах, рассмотрим особенности света и материалов.

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

  • Добавьте к проекту новый заголовочный файл (Header File) с именем Jet.h


  • Заполните файл Jet.h таким кодом
// Упражнение 2: "2) Самолет без освещения"
  
//**********************************************************
// Прототипы
void Jet();        
void RenderSceneJet(void);
void ChangeSizeJet(int, int);  // При изменении размеров окна
  
//**********************************************************
// Рисовать самолет
void Jet() 
{
  // Сохранить матрицу в стеке и выполнить повороты
  glPushMatrix();
  glRotatef(xRot, 1.0f, 0.0f, 0.0f);
  glRotatef(yRot, 0.0f, 1.0f, 0.0f);
  
  glBegin(GL_TRIANGLES);
  // Конус носа /////////////////////////////
    // Белый
    glColor3ub(255, 255, 255);
    glVertex3f(0.0f, 0.0f, 60.0f);
    glVertex3f(-15.0f, 0.0f, 30.0f);
    glVertex3f(15.0f,0.0f,30.0f);
  
    // Черный
    glColor3ub(0,0,0);
    glVertex3f(15.0f,0.0f,30.0f);
    glVertex3f(0.0f, 15.0f, 30.0f);
    glVertex3f(0.0f, 0.0f, 60.0f);
  
    // Красный
    glColor3ub(255,0,0);
    glVertex3f(0.0f, 0.0f, 60.0f);
    glVertex3f(0.0f, 15.0f, 30.0f);
    glVertex3f(-15.0f,0.0f,30.0f);
  
  // Тело самолета //////////////////////////
    // Зеленый
    glColor3ub(0,255,0);
    glVertex3f(-15.0f,0.0f,30.0f);
    glVertex3f(0.0f, 15.0f, 30.0f);
    glVertex3f(0.0f, 0.0f, -56.0f);
  
    // Желтый
    glColor3ub(255,255,0);
    glVertex3f(0.0f, 0.0f, -56.0f);
    glVertex3f(0.0f, 15.0f, 30.0f);
    glVertex3f(15.0f,0.0f,30.0f);  
  
    // Голубой
    glColor3ub(0, 255, 255);
    glVertex3f(15.0f,0.0f,30.0f);
    glVertex3f(-15.0f, 0.0f, 30.0f);
    glVertex3f(0.0f, 0.0f, -56.0f);
  
  ///////////////////////////////////////////
  // Левое крыло
  // Большой треугольник для основания крыла
    // Серый
    glColor3ub(128,128,128);
    glVertex3f(0.0f,2.0f,27.0f);
    glVertex3f(-60.0f, 2.0f, -8.0f);
    glVertex3f(60.0f, 2.0f, -8.0f);
  
    // Темно - серый
    glColor3ub(64,64,64);
    glVertex3f(60.0f, 2.0f, -8.0f);
    glVertex3f(0.0f, 7.0f, -8.0f);
    glVertex3f(0.0f,2.0f,27.0f);
  
    // Светло - серый
    glColor3ub(192,192,192);
    glVertex3f(60.0f, 2.0f, -8.0f);
    glVertex3f(-60.0f, 2.0f, -8.0f);
    glVertex3f(0.0f,7.0f,-8.0f);
  
  // Другое крыло верхней секции
    // Темно - серый
    glColor3ub(64,64,64);
    glVertex3f(0.0f,2.0f,27.0f);
    glVertex3f(0.0f, 7.0f, -8.0f);
    glVertex3f(-60.0f, 2.0f, -8.0f);
  
  ///////////////////////////////////////////
  // Хвост 
    // Розовый
    glColor3ub(255,128,255);
    glVertex3f(-30.0f, -0.50f, -57.0f);
    glVertex3f(30.0f, -0.50f, -57.0f);
    glVertex3f(0.0f,-0.50f,-40.0f);
  
    // Верхняя левая сторона
    // Коричневый
    glColor3ub(255,128,0);
    glVertex3f(0.0f,-0.5f,-40.0f);
    glVertex3f(30.0f, -0.5f, -57.0f);
    glVertex3f(0.0f, 4.0f, -57.0f);
  
    // Верхняя правая сторона
    // Коричневый
    glColor3ub(255,128,0);
    glVertex3f(0.0f, 4.0f, -57.0f);
    glVertex3f(-30.0f, -0.5f, -57.0f);
    glVertex3f(0.0f,-0.5f,-40.0f);
  
    // Оборотная нижняя часть
    // Белый
    glColor3ub(255,255,255);
    glVertex3f(30.0f,-0.5f,-57.0f);
    glVertex3f(-30.0f, -0.5f, -57.0f);
    glVertex3f(0.0f, 4.0f, -57.0f);
  
    // Красный
    glColor3ub(255,0,0);
    glVertex3f(0.0f,0.5f,-40.0f);
    glVertex3f(3.0f, 0.5f, -57.0f);
    glVertex3f(0.0f, 25.0f, -65.0f);
  
    // Красный
    glColor3ub(255,0,0);
    glVertex3f(0.0f, 25.0f, -65.0f);
    glVertex3f(-3.0f, 0.5f, -57.0f);
    glVertex3f(0.0f,0.5f,-40.0f);
  
    // Серый
    glColor3ub(128,128,128);
    glVertex3f(3.0f,0.5f,-57.0f);
    glVertex3f(-3.0f, 0.5f, -57.0f);
    glVertex3f(0.0f, 25.0f, -65.0f);
  
  glEnd(); // О самолете
  
  glPopMatrix();
  
  // Переключить буфер
  glutSwapBuffers();
}
  
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderSceneJet(void)
{
  // Сбрасываем буфер цвета и буфер глубины
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  // Рисуем сцену
  Jet();
  
  // Прокачка сообщений
  glFlush();
}
  
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
// для второго упражнения Jet
void ChangeSizeJet(int width, int height)
{  
  // Индивидуальные настройки для упражнения
    glEnable(GL_DEPTH_TEST);  // Включить тест глубины
    glEnable(GL_CULL_FACE);    // Отображать только лицевую сторону
    glFrontFace(GL_CCW);    // Считать лицевым обход против часовой стрелки
    glClearColor(0.0f, 0.0f, 1.0f, 1.0f);// Цвет фона окна
  
  // Предотвращаем деление на нуль
  if(height == 0)
    height = 1;
  
  // Устанавливаем поле просмотра с размерами окна
  glViewport(0, 0, width, height);
  
  // Устанавливает матрицу преобразования в режим проецирования
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  
  // Устанавливаем размеры ортогонального отсекающего объема
  GLfloat aspectRatio = (GLfloat)width / (GLfloat)height;// Для соблюдения пропорций
    GLfloat nRange = 80.0f;
    if (width <= height) 
        glOrtho (-nRange, nRange, -nRange / aspectRatio, nRange / aspectRatio, -nRange, nRange);
    else 
        glOrtho (-nRange * aspectRatio, nRange * aspectRatio, -nRange, nRange, -nRange, nRange);
  
  // Восстановливает матрицу преобразования в исходный режим вида
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}
Листинг 23.6. Код файла Jet.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) Самолет без освещения"
  
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
  // Сбрасываем углы вращения прежнего варианта
  xRot = yRot = zRot = 0;
  
  // Запоминаем выбор в глобальную переменную
  ::choice = choice; 
  
  switch(::choice)
  {
    case 1:
      ChangeSizeColorCube(w, h);
      break;
    case 2:
      ChangeSizeJet(w, h);
      break;
  }
    
  // Вызвать принудительно визуализацию
  glutPostRedisplay();
}
  
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
  switch(::choice)
  {
    case 1:
      RenderSceneColorCube();
      break;
    case 2:
      RenderSceneJet();
      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;
  }
}
  
//**********************************************************
// Обработчик события таймера
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);
  
  glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем 
  // Конец создания меню
  
  glutMainLoop();  // Цикл сообщений графического окна
}
Листинг 23.7. Дополненный файл LightAndMaterial.cpp
  • Разберитесь с кодом, запустите упражнение, поуправляйте изображением с помощью клавиш-стрелок, полюбуйтесь самолетом.

Снимки экрана могут быть такими



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

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