Опубликован: 13.07.2010 | Доступ: свободный | Студентов: 889 / 20 | Оценка: 4.40 / 4.20 | Длительность: 77:34:00
Самостоятельная работа 22:

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

Куб цвета (ColorCube)

Поскольку цвет задается тремя положительными кодами, то все доступные цвета можно смоделировать объемом, который можно назвать кубом цвета. Давайте смоделируем куб цвета средствами OpenGL.

  • Заполните файл 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) Куб цвета"
  
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
  // Сбрасываем углы вращения прежнего варианта
  xRot = yRot = zRot = 0;
  
  // Запоминаем выбор в глобальную переменную
  ::choice = choice; 
  
  switch(::choice)
  {
    case 1:
      ChangeSizeColorCube(w, h);
      break;
  }
    
  // Вызвать принудительно визуализацию
  glutPostRedisplay();
}
  
//**********************************************************
// Функция обратного вызова для рисования сцены
// для первого упражнения ColorCube
void RenderScene(void)
{
  switch(::choice)
  {
    case 1:
      RenderSceneColorCube();
      break;
  }
}
  
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
// для первого упражнения ColorCube
void ChangeSize(int width, int height)
{  
  w = width;
  h = height;
  
  switch(::choice)
  {
    case 1:
      ChangeSizeColorCube(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);
  
  glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем 
  // Конец создания меню
  
  glutMainLoop();  // Цикл сообщений графического окна
}
Листинг 23.3. Код файла LightAndMaterial.cpp для первого упражнения
  • Добавьте к проекту новый заголовочный файл (Header File) с именем ColorCube.h


  • Заполните файл ColorCube.h таким кодом
// Упражнение 1: "1) Куб цвета"
  
//**********************************************************
// Прототипы
void ColorCube();        
void RenderSceneColorCube(void);
void ChangeSizeColorCube(int, int);  // При изменении размеров окна
  
//**********************************************************
void ColorCube() 
{
  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);
  
  // Рисуем шесть граней
  glBegin(GL_QUADS);
  // 1. Передняя грань
    // Белый
    glColor3ub((GLubyte) 255, (GLubyte)255, (GLubyte)255);
    glVertex3f(50.0f,50.0f,50.0f);
  
    // Желтый
    glColor3ub((GLubyte) 255, (GLubyte)255, (GLubyte)0);
    glVertex3f(50.0f,-50.0f,50.0f);
  
    // Красный
    glColor3ub((GLubyte) 255, (GLubyte)0, (GLubyte)0);
    glVertex3f(-50.0f,-50.0f,50.0f);
  
    // Сиреневый
    glColor3ub((GLubyte) 255, (GLubyte)0, (GLubyte)255);
    glVertex3f(-50.0f,50.0f,50.0f);
  
  // 2. Задняя грань
    // Голубой
    glColor3f(0.0f, 1.0f, 1.0f);
    glVertex3f(50.0f,50.0f,-50.0f);
  
    // Зеленый
    glColor3f(0.0f, 1.0f, 0.0f);
    glVertex3f(50.0f,-50.0f,-50.0f);
  
    // Черный
    glColor3f(0.0f, 0.0f, 0.0f);
    glVertex3f(-50.0f,-50.0f,-50.0f);
  
    // Синий
    glColor3f(0.0f, 0.0f, 1.0f);
    glVertex3f(-50.0f,50.0f,-50.0f);
  
  // 3. Верхняя грань
    // Голубой
    glColor3f(0.0f, 1.0f, 1.0f);
    glVertex3f(50.0f,50.0f,-50.0f);
  
    // Белый
    glColor3f(1.0f, 1.0f, 1.0f);
    glVertex3f(50.0f,50.0f,50.0f);
  
    // Сиреневый
    glColor3f(1.0f, 0.0f, 1.0f);
    glVertex3f(-50.0f,50.0f,50.0f);
  
    // Синий
    glColor3f(0.0f, 0.0f, 1.0f);
    glVertex3f(-50.0f,50.0f,-50.0f);
  
  // 4. Нижняя грань
    // Зеленый
    glColor3f(0.0f, 1.0f, 0.0f);
    glVertex3f(50.0f,-50.0f,-50.0f);
  
    // Желтый
    glColor3f(1.0f, 1.0f, 0.0f);
    glVertex3f(50.0f,-50.0f,50.0f);
  
    // Красный
    glColor3f(1.0f, 0.0f, 0.0f);
    glVertex3f(-50.0f,-50.0f,50.0f);
  
    // Черный
    glColor3f(0.0f, 0.0f, 0.0f);
    glVertex3f(-50.0f,-50.0f,-50.0f);
  
  // 5. Левая грань
    // Белый
    glColor3f(1.0f, 1.0f, 1.0f);
    glVertex3f(50.0f,50.0f,50.0f);
  
    // Голубой
    glColor3f(0.0f, 1.0f, 1.0f);
    glVertex3f(50.0f,50.0f,-50.0f);
  
    // Зеленый
    glColor3f(0.0f, 1.0f, 0.0f);
    glVertex3f(50.0f,-50.0f,-50.0f);
  
    // Желтый
    glColor3f(1.0f, 1.0f, 0.0f);
    glVertex3f(50.0f,-50.0f,50.0f);
  
  // 6. Правая грань
    // Сиреневый
    glColor3f(1.0f, 0.0f, 1.0f);
    glVertex3f(-50.0f,50.0f,50.0f);
  
    // Синий
    glColor3f(0.0f, 0.0f, 1.0f);
    glVertex3f(-50.0f,50.0f,-50.0f);
  
    // Черный
    glColor3f(0.0f, 0.0f, 0.0f);
    glVertex3f(-50.0f,-50.0f,-50.0f);
  
    // Красный
    glColor3f(1.0f, 0.0f, 0.0f);
    glVertex3f(-50.0f,-50.0f,50.0f);
  glEnd();
  
  glPopMatrix();// Восстановить матрицу преобразования модели
  
  // Переключаем буферы рисования
  glutSwapBuffers();
}
  
//**********************************************************
// Функция обратного вызова для рисования сцены
// для первого упражнения ColorCube
void RenderSceneColorCube(void)
{
  // Сбрасываем буфер цвета и буфер глубины
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  // Рисуем сцену
  ColorCube();
  
  // Прокачка сообщений
  glFlush();
}
  
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
// для первого упражнения ColorCube
void ChangeSizeColorCube(int width, int height)
{  
  // Индивидуальные настройки для упражнения
  glClearColor(0.0F, 0.0F, 0.0F, 1.0F);// Фон черный непрозрачный
  glEnable(GL_DEPTH_TEST);  // Включили проверку глубины
  glEnable(GL_DITHER);    // Включить режим генерации полутонов
  glShadeModel(GL_SMOOTH);  // Режим градиентной закраски
  glDisable(GL_CULL_FACE);  // Выключить режим отсечения сторон
  
  // Предотвращаем деление на нуль
  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, 1000.0f);
  
  // Восстановливает матрицу преобразования в исходный режим вида
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  
  // Отодвигаем куб от начала координат вглубь оси 0z
  glTranslatef(0.0f, 0.0f, -400.0f);
}
Листинг 23.4. Код файла ColorCube.h реализации модели атома
  • Разберитесь с кодом, запустите упражнение, поуправляйте изображением с помощью клавиш-стрелок, полюбуйтесь вращающимся кубом цвета.

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



Свет и материя

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

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

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

  1. на модели самого источника света
  2. на световой модели материала объекта, взаимодействующего с прямыми лучами источника света.

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


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

Различают три основных типа взаимодействия света с поверхностью освещения в зависимости от материала

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


  2. Диффузное отражение. При диффузном отражении падающий свет рассеивается в разных направлениях. Такой тип взаимодействия характерен для матовой или шероховатой поверхности.


  3. Преломление. Луч света, падающий на поверхность, преломляется и проникает в среду под другим углом.