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

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

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

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

  • Для упражнения 7 добавьте к проекту новый заголовочный файл с именем JetLight.h


  • Заполните файл следующим кодом
// Упражнение 7: "7) Самолет с равномерным освещением"
  
#include <math.h>
  
//**********************************************************
// Прототипы
void JetLight();        
void SetLightJetLight();
void RenderSceneJetLight(void);
void ChangeSizeJetLight(int, int);  // При изменении размеров окна
  
//**********************************************************
// Вспомогательный код
typedef GLfloat GLTVector3[3];      // Новый тип - вектор
void gltGetNormalVector(const GLTVector3 vP1, const GLTVector3 vP2, const GLTVector3 vP3, GLTVector3 vNormal);
void gltSubtractVectors(const GLTVector3 vFirst, const GLTVector3 vSecond, GLTVector3 vResult); 
void gltVectorCrossProduct(const GLTVector3 vU, const GLTVector3 vV, GLTVector3 vResult);
void gltNormalizeVector(GLTVector3 vNormal);
GLfloat gltGetVectorLength(const GLTVector3 vVector);
GLfloat gltGetVectorLengthSqrd(const GLTVector3 vVector);
void gltScaleVector(GLTVector3 vVector, const GLfloat fScale);
  
//**********************************************************
// Рисовать самолет
void JetLight() 
{
  GLTVector3 vNormal;  // Для вектора нормали
  
  // Очищаем буфер цвета и глубины
  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);
  
  // Устанавливаем цвет материала серый для всех граней самолета
  glColor3ub(128, 128, 128);
  // Рисуем самолет
  glBegin(GL_TRIANGLES);
  // Конус носа /////////////////////////////
    glNormal3f(0.0f, -1.0f, 0.0f);
    glVertex3f(0.0f, 0.0f, 60.0f);
    glVertex3f(-15.0f, 0.0f, 30.0f);
    glVertex3f(15.0f,0.0f,30.0f);
  
    {// Блок кода
      GLTVector3 vPoints[3] = {{ 15.0f, 0.0f,  30.0f},
                  { 0.0f,  15.0f, 30.0f},
                  { 0.0f,  0.0f,  60.0f}};
      gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
      glNormal3fv(vNormal);
      glVertex3fv(vPoints[0]);
      glVertex3fv(vPoints[1]);
      glVertex3fv(vPoints[2]);
    }
  
    {// Блок кода
      GLTVector3 vPoints[3] = {{ 0.0f, 0.0f, 60.0f },
                  { 0.0f, 15.0f, 30.0f },
                  { -15.0f, 0.0f, 30.0f }};
      gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
      glNormal3fv(vNormal);
      glVertex3fv(vPoints[0]);
      glVertex3fv(vPoints[1]);
      glVertex3fv(vPoints[2]);
    }
  
  // Тело самолета //////////////////////////
    {
      GLTVector3 vPoints[3] = {{ -15.0f, 0.0f, 30.0f },
                  { 0.0f, 15.0f, 30.0f },
                  { 0.0f, 0.0f, -56.0f }};
      gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
      glNormal3fv(vNormal);
      glVertex3fv(vPoints[0]);
      glVertex3fv(vPoints[1]);
      glVertex3fv(vPoints[2]);
    }
  
    {
      GLTVector3 vPoints[3] = {{ 0.0f, 0.0f, -56.0f },
                  { 0.0f, 15.0f, 30.0f },
                  { 15.0f,0.0f,30.0f }};
      gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
      glNormal3fv(vNormal);
      glVertex3fv(vPoints[0]);
      glVertex3fv(vPoints[1]);
      glVertex3fv(vPoints[2]);
    }
  
    glNormal3f(0.0f, -1.0f, 0.0f);
    glVertex3f(15.0f,0.0f,30.0f);
    glVertex3f(-15.0f, 0.0f, 30.0f);
    glVertex3f(0.0f, 0.0f, -56.0f);
  
  ///////////////////////////////////////////
  // Левое крыло
  // Большой треугольник для основания крыла
    {
      GLTVector3 vPoints[3] = {{ 0.0f,2.0f,27.0f },
                  { -60.0f, 2.0f, -8.0f },
                  { 60.0f, 2.0f, -8.0f }};
      gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
      glNormal3fv(vNormal);
      glVertex3fv(vPoints[0]);
      glVertex3fv(vPoints[1]);
      glVertex3fv(vPoints[2]);
    }
  
    {
      GLTVector3 vPoints[3] = {{ 60.0f, 2.0f, -8.0f},
                  {0.0f, 7.0f, -8.0f},
                  {0.0f,2.0f,27.0f }};
      gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
      glNormal3fv(vNormal);
      glVertex3fv(vPoints[0]);
      glVertex3fv(vPoints[1]);
      glVertex3fv(vPoints[2]);
    }
  
    {
      GLTVector3 vPoints[3] = {{60.0f, 2.0f, -8.0f},
                  {-60.0f, 2.0f, -8.0f},
                  {0.0f,7.0f,-8.0f }};
      gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
      glNormal3fv(vNormal);
      glVertex3fv(vPoints[0]);
      glVertex3fv(vPoints[1]);
      glVertex3fv(vPoints[2]);
    }
  
  // Другое крыло верхней секции
    {
      GLTVector3 vPoints[3] = {{0.0f,2.0f,27.0f},
                  {0.0f, 7.0f, -8.0f},
                  {-60.0f, 2.0f, -8.0f}};
      gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
      glNormal3fv(vNormal);
      glVertex3fv(vPoints[0]);
      glVertex3fv(vPoints[1]);
      glVertex3fv(vPoints[2]);
    }
                        
  ///////////////////////////////////////////
  // Хвост 
    glNormal3f(0.0f, -1.0f, 0.0f);
    glVertex3f(-30.0f, -0.50f, -57.0f);
    glVertex3f(30.0f, -0.50f, -57.0f);
    glVertex3f(0.0f,-0.50f,-40.0f);
  
    // Верхняя левая сторона
    {
      GLTVector3 vPoints[3] = {{ 0.0f,-0.5f,-40.0f },
                  {30.0f, -0.5f, -57.0f},
                  {0.0f, 4.0f, -57.0f }};
      gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
      glNormal3fv(vNormal);
      glVertex3fv(vPoints[0]);
      glVertex3fv(vPoints[1]);
      glVertex3fv(vPoints[2]);
    }
                        
    // Верхняя правая сторона
    {
      GLTVector3 vPoints[3] = {{ 0.0f, 4.0f, -57.0f },
                  { -30.0f, -0.5f, -57.0f },
                  { 0.0f,-0.5f,-40.0f }};
      gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
      glNormal3fv(vNormal);
      glVertex3fv(vPoints[0]);
      glVertex3fv(vPoints[1]);
      glVertex3fv(vPoints[2]);
    }
  
    // Оборотная нижняя часть
    {
      GLTVector3 vPoints[3] = {{ 30.0f,-0.5f,-57.0f },
                  { -30.0f, -0.5f, -57.0f },
                  { 0.0f, 4.0f, -57.0f }};
      gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
      glNormal3fv(vNormal);
      glVertex3fv(vPoints[0]);
      glVertex3fv(vPoints[1]);
      glVertex3fv(vPoints[2]);
    }
  
    {
      GLTVector3 vPoints[3] = {{ 0.0f,0.5f,-40.0f },
                  { 3.0f, 0.5f, -57.0f },
                  { 0.0f, 25.0f, -65.0f }};
      gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
      glNormal3fv(vNormal);
      glVertex3fv(vPoints[0]);
      glVertex3fv(vPoints[1]);
      glVertex3fv(vPoints[2]);
    }
  
    {
      GLTVector3 vPoints[3] = {{ 0.0f, 25.0f, -65.0f },
                  { -3.0f, 0.5f, -57.0f},
                  { 0.0f,0.5f,-40.0f }};
      gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
      glNormal3fv(vNormal);
      glVertex3fv(vPoints[0]);
      glVertex3fv(vPoints[1]);
      glVertex3fv(vPoints[2]);
    }
  
    {
      GLTVector3 vPoints[3] = {{ 3.0f,0.5f,-57.0f },
                  { -3.0f, 0.5f, -57.0f },
                  { 0.0f, 25.0f, -65.0f }};
      gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
      glNormal3fv(vNormal);
      glVertex3fv(vPoints[0]);
      glVertex3fv(vPoints[1]);
      glVertex3fv(vPoints[2]);
    }
  glEnd(); // О самолете
  
  // Восстановить матрицу
  glPopMatrix();
  
  // Переключить буфер
  glutSwapBuffers();
}
  
//**********************************************************
// Задать параметры освещения и согласования цветов материала
void SetLightJetLight()
{
  GLfloat ambientLight[] = { 0.3f, 0.3f, 0.3f, 1.0f };// Фоновый слабобелый
  GLfloat diffuseLight[] = { 0.7f, 0.7f, 0.7f, 1.0f };// Рассеяный среднебелый
  GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0};// Расположение источника
  glShadeModel(GL_SMOOTH);
  
  GLfloat diffuseMaterial[] = {0.5, 0.5, 0.5, 1.0};
  glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuseMaterial);// Начальное значение диффузного материала
  
  glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight);
  glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight);
  glLightfv(GL_LIGHT0, GL_POSITION, light_position);
  
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  
  // Режим согласования цветов назначаем для фонового и рассеянного
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
  // Включаем поддержку согласования цветов
  glEnable(GL_COLOR_MATERIAL);
}
  
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderSceneJetLight(void)
{
  // Сбрасываем буфер цвета и буфер глубины
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  // Рисуем сцену
  JetLight();
  
  // Прокачка сообщений
  glFlush();
}
  
//**********************************************************
void ChangeSizeJetLight(int width, int height)
{  
  // Устанавливаем индивидуальные настройки освещения
  SetLightJetLight();
  
    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;  // Для соблюдения пропорций
    gluPerspective(45.0f, aspectRatio, 1.0f, 250.0f);    // Отсекающая перспектива
  
  // Восстановливает матрицу преобразования в исходный режим вида
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  
  // Отодвинем сцену в отрицательную сторону оси 0z
  glTranslatef(0.0f, 0.0f, -180.0f);
}
  
//**********************************************************
// Вспомогательный код
//**********************************************************
void gltGetNormalVector(const GLTVector3 vP1, const GLTVector3 vP2, const GLTVector3 vP3, GLTVector3 vNormal)
{
  GLTVector3 vV1, vV2;
  
  gltSubtractVectors(vP2, vP1, vV1);
  gltSubtractVectors(vP3, vP1, vV2);
  
  gltVectorCrossProduct(vV1, vV2, vNormal);
  gltNormalizeVector(vNormal);
}
  
//**********************************************************
void gltSubtractVectors(const GLTVector3 vFirst, const GLTVector3 vSecond, GLTVector3 vResult) 
{
  vResult[0] = vFirst[0] - vSecond[0];
  vResult[1] = vFirst[1] - vSecond[1];
  vResult[2] = vFirst[2] - vSecond[2];
}
  
//**********************************************************
void gltVectorCrossProduct(const GLTVector3 vU, const GLTVector3 vV, GLTVector3 vResult)
{
  vResult[0] = vU[1]*vV[2] - vV[1]*vU[2];
  vResult[1] = -vU[0]*vV[2] + vV[0]*vU[2];
  vResult[2] = vU[0]*vV[1] - vV[0]*vU[1];
}
  
//**********************************************************
void gltNormalizeVector(GLTVector3 vNormal)
{ 
  GLfloat fLength = 1.0f / gltGetVectorLength(vNormal);
  gltScaleVector(vNormal, fLength); 
}
  
//**********************************************************
GLfloat gltGetVectorLength(const GLTVector3 vVector)
{ 
  return (GLfloat)sqrt(gltGetVectorLengthSqrd(vVector)); 
}
  
//**********************************************************
GLfloat gltGetVectorLengthSqrd(const GLTVector3 vVector)
{ 
  return (vVector[0]*vVector[0]) + (vVector[1]*vVector[1]) + (vVector[2]*vVector[2]); 
}
  
//**********************************************************
void gltScaleVector(GLTVector3 vVector, const GLfloat fScale)
{ 
  vVector[0] *= fScale; vVector[1] *= fScale; vVector[2] *= fScale; 
}
Листинг 23.24. Код файла JetLight.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) Рисование освещенной сферы"
#include "MultiMaterial.h"  // Упражнение 4: "4) Рисование с разными материалами"
#include "ColorSphere.h"  // Упражнение 5: "5) Способ согласования цветов"
#include "JetNight.h"    // Упражнение 6: "6) Самолет с нулевым освещением"
#include "JetLight.h"    // Упражнение 7: "7) Самолет с равномерным освещением"
  
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
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;
  }
    
  // Вызвать принудительно визуализацию
  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;
  }
  
  // Восстановить прежние настройки 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;
  }
}
  
//**********************************************************
// Обработчик события таймера
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);
  glutAddMenuEntry("7) Самолет с равномерным освещением", 7);
  
  glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем 
  // Конец создания меню
  
  glutMainLoop();  // Цикл сообщений графического окна
}
Листинг 23.25. Код подключения модели самолета с равномерным освещением в файле LightAndMaterial.cpp
  • Запустите приложение и выполните упражнение 7, результат должен быть примерно таким


Если поуправлять поворотами модели с помощью клавиш-стрелок, то будет меняться цвет граней самолета по мере того, как их векторы нормалей будут менять свой угол по отношению к источнику света и точке наблюдения. OpenGL "на лету" будет эти цвета пересчитывать.