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

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

Освещенный мир сфер (SphereWorld)

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

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


  • Наполните этот файл следующим кодом
// SphereWorld.h
// Упражнение 11: "11) Освещенный мир сфер"
  
//**********************************************************
// Прототипы
void SetLightSphereWorld();
void ChangeSizeSphereWorld(int width, int height);
void SpecialKeysSphereWorld(int key);
void RenderSceneSphereWorld(void);
void DrawInhabitants(GLint nShadow);
void DrawGround(void);
  
//**********************************************************
// Глобальные переменные
typedef struct{
  GLTVector3 vLocation;
  GLTVector3 vUp;
  GLTVector3 vForward;
  } GLTFrame;
#define NUM_SPHERES 30
GLTFrame spheres[NUM_SPHERES];
GLTFrame frameCamera;
  
// Данные для источников света и материалов
GLfloat fLightPos[4]   = { -100.0f, 100.0f, 50.0f, 1.0f }; 
GLfloat fNoLight[] = { 0.0f, 0.0f, 0.0f, 0.0f };
GLfloat fLowLight[] = { 0.25f, 0.25f, 0.25f, 1.0f };
GLfloat fBrightLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };
  
// Матрица проектировария тени
GLTMatrix mShadowMatrix;
  
//**********************************************************
// Вспомогательный код
void gltApplyActorTransform(GLTFrame *pFrame);
void gltApplyCameraTransform(GLTFrame *pCamera);
void gltDrawTorus(GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
void gltInitFrame(GLTFrame *pFrame);
void gltMoveFrameForward(GLTFrame *pFrame, GLfloat fStep);
void gltRotateFrameLocalY(GLTFrame *pFrame, GLfloat fAngle);
#define GLT_PI_DIV_180 0.017453292519943296
#define gltDegToRad(x)  ((x)*GLT_PI_DIV_180)
void gltGetMatrixFromFrame(GLTFrame *pFrame, GLTMatrix mMatrix);
void gltRotateVector(const GLTVector3 vSrcVector, const GLTMatrix mMatrix, GLTVector3 vOut);
void gltRotationMatrix(float angle, float x, float y, float z, GLTMatrix mMatrix);
void gltLoadIdentityMatrix(GLTMatrix m);
  
//**********************************************************
// Установка света и материалов
void SetLightSphereWorld()
{
  int iSphere;
  
  // Точки плоскости размещения тени
  GLTVector3 vPoints[3] = {{ 0.0f, -0.4f, 0.0f },
              { 10.0f, -0.4f, 0.0f },
              { 5.0f, -0.4f, -5.0f }};
  
  // Установка параметров света
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fNoLight);
  glLightfv(GL_LIGHT0, GL_AMBIENT, fLowLight);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, fBrightLight);
  glLightfv(GL_LIGHT0, GL_SPECULAR, fBrightLight);
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  
  // Вычисляем матрицу тени
  gltMakeShadowMatrix(vPoints, fLightPos, mShadowMatrix);
  
  // Задаем согласование цветов материала
  glEnable(GL_COLOR_MATERIAL);
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
  glMateriali(GL_FRONT, GL_SHININESS, 128);
  
  gltInitFrame(&frameCamera);  // Инициализируем камеру
  
    // Вычисляем случайно места для размещения сфер
  for(iSphere = 0; iSphere < NUM_SPHERES; iSphere++)
  {
    // Инициализация фрейма для очередной сферы
    gltInitFrame(&spheres[iSphere]);    
  
    // Корректируем координаты сфер
    spheres[iSphere].vLocation[0] = (float)((rand() % 400) - 200) * 0.1f;
    spheres[iSphere].vLocation[1] = 0.0f; 
    spheres[iSphere].vLocation[2] = (float)((rand() % 400) - 200) * 0.1f;
  }
}
  
//**********************************************************
// Рисуем основание как ленты треугольников
void DrawGround(void)
{
  GLfloat fExtent = 20.0f;
  GLfloat fStep = 1.0f;
  GLfloat y = -0.4f;
  GLint iStrip, iRun;
  
  for(iStrip = -fExtent; iStrip <= fExtent; iStrip += fStep)
  {
    glBegin(GL_TRIANGLE_STRIP);
      glNormal3f(0.0f, 1.0f, 0.0f);   // Нормаль для всех вершин
      for(iRun = fExtent; iRun >= -fExtent; iRun -= fStep)
      {
        glVertex3f(iStrip, y, iRun);
        glVertex3f(iStrip + fStep, y, iRun);
      }
    glEnd();
  }
}
  
//**********************************************************
// Рисуем случайным образом сферы и тор (или их тени)
void DrawInhabitants(GLint nShadow)
{
  static GLfloat yRot = 0.0f; 
  GLint i;
  
  if(nShadow == 0)
    yRot += 0.5f;
  else  // Черный цвет
    glColor3f(0.0f, 0.0f, 0.0f);
  
  // Рисуем зелеными случайно расположенные сферы
  if(nShadow == 0)
    glColor3f(0.0f, 1.0f, 0.0f);
      
  for(i = 0; i < NUM_SPHERES; i++)
  {
    glPushMatrix();
    gltApplyActorTransform(&spheres[i]);
    glutSolidSphere(0.3f, 17, 9);
    glPopMatrix();
  }
  
  glPushMatrix();
    glTranslatef(0.0f, 0.1f, -2.5f);
  
    if(nShadow == 0)
      glColor3f(0.0f, 0.0f, 1.0f);
  
    glPushMatrix();
      glRotatef(-yRot * 2.0f, 0.0f, 1.0f, 0.0f);
      glTranslatef(1.0f, 0.0f, 0.0f);
      glutSolidSphere(0.1f, 17, 9);
    glPopMatrix();
  
    if(nShadow == 0)
    {
      // Устанавливаем для тора зеркальные свойства
      glColor3f(1.0f, 0.0f, 0.0f);// Красный цвет тора
      glMaterialfv(GL_FRONT, GL_SPECULAR, fBrightLight);
    }
  
    glRotatef(yRot, 0.0f, 1.0f, 0.0f);
    gltDrawTorus(0.35, 0.15, 61, 37);
    glMaterialfv(GL_FRONT, GL_SPECULAR, fNoLight);
  glPopMatrix();
}
  
//**********************************************************
// Рендеринг сцены
void RenderSceneSphereWorld(void)
{
  // Очистка буфера цвета и глубины
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  glPushMatrix();
    gltApplyCameraTransform(&frameCamera);
  
    // Позиция источника света до преобразований
    glLightfv(GL_LIGHT0, GL_POSITION, fLightPos);
  
    // Рисуем основание коричневым цветом
    glColor3f(0.60f, .40f, .10f);
    DrawGround();
  
    // Вначале рисуем тени объектов 
    // при выключенных глубине и освещении
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_LIGHTING);
    glPushMatrix();
      glMultMatrixf(mShadowMatrix);
      DrawInhabitants(1);
    glPopMatrix();
    // Включаем проверку глубины и освещение
    glEnable(GL_LIGHTING);
    glEnable(GL_DEPTH_TEST);
  
    // Теперь рисуем сами объекты (сферы и тор)
    DrawInhabitants(0);
  
  glPopMatrix();
  
  // Переключаем буфер и обновляем экран
  glutSwapBuffers();
  glutPostRedisplay();
}
  
//**********************************************************
// Управление клавишами
void SpecialKeysSphereWorld(int key)
{
  if(key == GLUT_KEY_UP)
    gltMoveFrameForward(&frameCamera, 0.1f);
  
  if(key == GLUT_KEY_DOWN)
    gltMoveFrameForward(&frameCamera, -0.1f);
  
  if(key == GLUT_KEY_LEFT)
    gltRotateFrameLocalY(&frameCamera, 0.1);
  
  if(key == GLUT_KEY_RIGHT)
    gltRotateFrameLocalY(&frameCamera, -0.1);
}
  
//**********************************************************
void ChangeSizeSphereWorld(int width, int height)
{
  // Устанавливаем индивидуальные настройки освещения
  // и материала
  SetLightSphereWorld();
  
    glEnable(GL_DEPTH_TEST);  // Включить тест глубины
    glEnable(GL_CULL_FACE);    // Отображать только лицевую сторону
    glFrontFace(GL_CCW);    // Считать лицевым обход против часовой стрелки
    glCullFace(GL_BACK);
    // Цвет фона окна угрюмо - серый
    glClearColor(fLowLight[0], fLowLight[1], fLowLight[2], fLowLight[3]);
  
  // Предотвращаем деление на нуль
  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, 50.0f);    // Отсекающая перспектива
  
  // Восстановливает матрицу преобразования в исходный режим вида
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}
  
//**********************************************************
//**********************************************************
// Вспомогательный код
//**********************************************************
//**********************************************************
////////////////////////////////////////////////////////////////////
// Apply an actors transform given it's frame of reference
void gltApplyActorTransform(GLTFrame *pFrame)
{
    GLTMatrix mTransform;
    gltGetMatrixFromFrame(pFrame, mTransform);
    glMultMatrixf(mTransform);
}
  
//////////////////////////////////////////////////////////////////
// Apply a camera transform given a frame of reference. This is
// pretty much just an alternate implementation of gluLookAt using
// floats instead of doubles and having the forward vector specified
// instead of a point out in front of me. 
void gltApplyCameraTransform(GLTFrame *pCamera)
{
    GLTMatrix mMatrix;
    GLTVector3 vAxisX;
    GLTVector3 zFlipped;
    
    zFlipped[0] = -pCamera->vForward[0];
    zFlipped[1] = -pCamera->vForward[1];
    zFlipped[2] = -pCamera->vForward[2];
    
    // Derive X vector
    gltVectorCrossProduct(pCamera->vUp, zFlipped, vAxisX);
    
    // Populate matrix, note this is just the rotation and is transposed
    mMatrix[0] = vAxisX[0];
    mMatrix[4] = vAxisX[1];
    mMatrix[8] = vAxisX[2];
    mMatrix[12] = 0.0f;
    
    mMatrix[1] = pCamera->vUp[0];
    mMatrix[5] = pCamera->vUp[1];
    mMatrix[9] = pCamera->vUp[2];
    mMatrix[13] = 0.0f;
    
    mMatrix[2] = zFlipped[0];
    mMatrix[6] = zFlipped[1];
    mMatrix[10] = zFlipped[2];
    mMatrix[14] = 0.0f;
    
    mMatrix[3] = 0.0f;
    mMatrix[7] = 0.0f;
    mMatrix[11] = 0.0f;
    mMatrix[15] = 1.0f;
    
    // Do the rotation first
    glMultMatrixf(mMatrix);
    
    // Now, translate backwards
    glTranslatef(-pCamera->vLocation[0], -pCamera->vLocation[1], -pCamera->vLocation[2]);
}
  
#define GLT_PI  3.14159265358979323846
// For best results, put this in a display list
// Draw a torus (doughnut)  at z = fZVal... torus is in xy plane
void gltDrawTorus(GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor)
{
    GLTVector3 vNormal;
    double majorStep = 2.0f*GLT_PI / numMajor;
    double minorStep = 2.0f*GLT_PI / numMinor;
    int i, j;
  
    for (i=0; i < numMajor; ++i) 
    {
    double a0 = i * majorStep;
    double a1 = a0 + majorStep;
    GLfloat x0 = (GLfloat) cos(a0);
    GLfloat y0 = (GLfloat) sin(a0);
    GLfloat x1 = (GLfloat) cos(a1);
    GLfloat y1 = (GLfloat) sin(a1);
  
    glBegin(GL_TRIANGLE_STRIP);
      for (j=0; j<=numMinor; ++j) 
      {
        double b = j * minorStep;
        GLfloat c = (GLfloat) cos(b);
        GLfloat r = minorRadius * c + majorRadius;
        GLfloat z = minorRadius * (GLfloat) sin(b);
  
        // First point
        glTexCoord2f((float)(i)/(float)(numMajor), (float)(j)/(float)(numMinor));
        vNormal[0] = x0*c;
        vNormal[1] = y0*c;
        vNormal[2] = z/minorRadius;
        gltNormalizeVector(vNormal);
        glNormal3fv(vNormal);
        glVertex3f(x0*r, y0*r, z);
  
        glTexCoord2f((float)(i+1)/(float)(numMajor), (float)(j)/(float)(numMinor));
        vNormal[0] = x1*c;
        vNormal[1] = y1*c;
        vNormal[2] = z/minorRadius;
        glNormal3fv(vNormal);
        glVertex3f(x1*r, y1*r, z);
      }
    glEnd();
  }
}
  
// Initialize a frame of reference. 
// Uses default OpenGL viewing position and orientation
void gltInitFrame(GLTFrame *pFrame)
{
  pFrame->vLocation[0] = 0.0f;
  pFrame->vLocation[1] = 0.0f;
  pFrame->vLocation[2] = 0.0f;
  
  pFrame->vUp[0] = 0.0f;
  pFrame->vUp[1] = 1.0f;
  pFrame->vUp[2] = 0.0f;
  
  pFrame->vForward[0] = 0.0f;
  pFrame->vForward[1] = 0.0f;
  pFrame->vForward[2] = -1.0f;
}
  
/////////////////////////////////////////////////////////
// March a frame of reference forward. This simply moves
// the location forward along the forward vector.
void gltMoveFrameForward(GLTFrame *pFrame, GLfloat fStep)
{
  pFrame->vLocation[0] += pFrame->vForward[0] * fStep;
  pFrame->vLocation[1] += pFrame->vForward[1] * fStep;
  pFrame->vLocation[2] += pFrame->vForward[2] * fStep;
}
  
/////////////////////////////////////////////////////////
// Rotate a frame around it's local Y axis
void gltRotateFrameLocalY(GLTFrame *pFrame, GLfloat fAngle)
{
  GLTMatrix mRotation;
  GLTVector3 vNewForward;
  
  gltRotationMatrix((float)gltDegToRad(fAngle), 0.0f, 1.0f, 0.0f, mRotation);
  gltRotationMatrix(fAngle, pFrame->vUp[0], pFrame->vUp[1], pFrame->vUp[2], mRotation);
  
  gltRotateVector(pFrame->vForward, mRotation, vNewForward);
  memcpy(pFrame->vForward, vNewForward, sizeof(GLTVector3));
}
  
///////////////////////////////////////////////////////////////////
// Derives a 4x4 transformation matrix from a frame of reference
void gltGetMatrixFromFrame(GLTFrame *pFrame, GLTMatrix mMatrix)
{
  GLTVector3 vXAxis;       // Derived X Axis
  
  // Calculate X Axis
  gltVectorCrossProduct(pFrame->vUp, pFrame->vForward, vXAxis);
  
  // Just populate the matrix
  // X column vector
  memcpy(mMatrix, vXAxis, sizeof(GLTVector3));
  mMatrix[3] = 0.0f;
  
  // y column vector
  memcpy(mMatrix+4, pFrame->vUp, sizeof(GLTVector3));
  mMatrix[7] = 0.0f;
  
  // z column vector
  memcpy(mMatrix+8, pFrame->vForward, sizeof(GLTVector3));
  mMatrix[11] = 0.0f;
  
  // Translation/Location vector
  memcpy(mMatrix+12, pFrame->vLocation, sizeof(GLTVector3));
  mMatrix[15] = 1.0f;
}
  
// Rotates a vector using a 4x4 matrix. Translation column is ignored
void gltRotateVector(const GLTVector3 vSrcVector, const GLTMatrix mMatrix, GLTVector3 vOut)
{
  vOut[0] = mMatrix[0] * vSrcVector[0] + mMatrix[4] * vSrcVector[1] + mMatrix[8] *  vSrcVector[2];
  vOut[1] = mMatrix[1] * vSrcVector[0] + mMatrix[5] * vSrcVector[1] + mMatrix[9] *  vSrcVector[2];
  vOut[2] = mMatrix[2] * vSrcVector[0] + mMatrix[6] * vSrcVector[1] + mMatrix[10] * vSrcVector[2];      
}
  
///////////////////////////////////////////////////////////////////////////////
// Creates a 4x4 rotation matrix, takes radians NOT degrees
void gltRotationMatrix(float angle, float x, float y, float z, GLTMatrix mMatrix)
{
  float vecLength, sinSave, cosSave, oneMinusCos;
  float xx, yy, zz, xy, yz, zx, xs, ys, zs;
  
  // If NULL vector passed in, this will blow up...
  if(x == 0.0f && y == 0.0f && z == 0.0f)
  {
    gltLoadIdentityMatrix(mMatrix);
    return;
  }
  
  // Scale vector
  vecLength = (float)sqrt( x*x + y*y + z*z );
  
  // Rotation matrix is normalized
  x /= vecLength;
  y /= vecLength;
  z /= vecLength;
  
  sinSave = (float)sin(angle);
  cosSave = (float)cos(angle);
  oneMinusCos = 1.0f - cosSave;
  
  xx = x * x;
  yy = y * y;
  zz = z * z;
  xy = x * y;
  yz = y * z;
  zx = z * x;
  xs = x * sinSave;
  ys = y * sinSave;
  zs = z * sinSave;
  
  mMatrix[0] = (oneMinusCos * xx) + cosSave;
  mMatrix[4] = (oneMinusCos * xy) - zs;
  mMatrix[8] = (oneMinusCos * zx) + ys;
  mMatrix[12] = 0.0f;
  
  mMatrix[1] = (oneMinusCos * xy) + zs;
  mMatrix[5] = (oneMinusCos * yy) + cosSave;
  mMatrix[9] = (oneMinusCos * yz) - xs;
  mMatrix[13] = 0.0f;
  
  mMatrix[2] = (oneMinusCos * zx) - ys;
  mMatrix[6] = (oneMinusCos * yz) + xs;
  mMatrix[10] = (oneMinusCos * zz) + cosSave;
  mMatrix[14] = 0.0f;
  
  mMatrix[3] = 0.0f;
  mMatrix[7] = 0.0f;
  mMatrix[11] = 0.0f;
  mMatrix[15] = 1.0f;
}
  
///////////////////////////////////////////////////////////////////////////////
// Load a matrix with the Idenity matrix
void gltLoadIdentityMatrix(GLTMatrix m)
{
  static GLTMatrix identity = { 1.0f, 0.0f, 0.0f, 0.0f,
                  0.0f, 1.0f, 0.0f, 0.0f,
                  0.0f, 0.0f, 1.0f, 0.0f,
                  0.0f, 0.0f, 0.0f, 1.0f };
  
  memcpy(m, identity, sizeof(GLTMatrix));
}
Листинг 23.34. Код файла SphereWorld.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);  // Функция установки отсекающего объема
void CrystalAndSpotExecuteMenu(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) Самолет с равномерным освещением"
#include "JetShiny.h"    // Упражнение 8: "8) Самолет с зеркальным отражением"
#include "CrystalAndSpot.h"  // Упражнение 9: "9) Рисование кристалла и прожектора"
#include "JetShadow.h"    // Упражнение 10: "10) Модель самолета и его тени"
#include "SphereWorld.h"  // Упражнение 11: "11) Освещенный мир сфер"
  
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
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;
    case 8:
      ChangeSizeJetShiny(w, h);
      break;
    case 10:
      ChangeSizeJetShadow(w, h);
      break;
    case 11:
      ChangeSizeSphereWorld(w, h);
      break;
  }
    
  // Вызвать принудительно визуализацию
  glutPostRedisplay();
}
  
//****************************************************
// Функция обратного вызова обработки выбора пользователя в подменю
void CrystalAndSpotExecuteMenu(int choice)
{
  xRot = yRot = 0;
  ChangeSizeCrystalAndSpot(w, h);
  
  switch(choice){
    case 0:
      iShade = MODE_FLAT;
      break;
    case 1: 
      iShade = MODE_SMOOTH;
      break;
    case 2: 
      iTess = MODE_VERYLOW;
      break;
    case 3:
      iTess = MODE_MEDIUM;
      break;
    case 4:
        default:
            iTess = MODE_VERYHIGH;
  }
  
  // Устанавливаем режим и вызываем принудительно 
  // функцию визуализации, где размещен
  // вызов функции рисования кристалла и прожектора
  ::choice = 9;
  
  // Вызвать принудительно визуализацию
  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;
    case 8:
      RenderSceneJetShiny();
      break;
    case 9:
      RenderSceneCrystalAndSpot();
      break;
    case 10:
      RenderSceneJetShadow();
      break;
    case 11:
      RenderSceneSphereWorld();
      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;
    case 8:
      ChangeSizeJetShiny(width, height);
      break;
    case 9:
      ChangeSizeCrystalAndSpot(width, height);
      break;
    case 10:
      ChangeSizeJetShadow(width, height);
      break;
    case 11:
      ChangeSizeSphereWorld(width, height);
      break;
  }
}
  
//**********************************************************
// Обработчик события таймера
void TimerFunc(int value)
{
  glutPostRedisplay(); // Перерисовка сцены
  
  switch(::choice)
  {
    case 11:
      glutTimerFunc(3, TimerFunc, 1);  
      break;
    default:
      glutTimerFunc(30, TimerFunc, 1);
  }
}
  
//**********************************************************
// Управление с клавиатуры стрелками
// для задания новых значений матрицы поворота
void SpecialKeys(int key, int x, int y)
{
  switch(::choice)
  {
    default:
      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);
      }
      break;
    case 11:
      SpecialKeysSphereWorld(key);
  }
  
  // Вызвать принудительно визуализацию с помощью 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);    // Для управления с клавиатуры
    
  // Создаем подменю для рисования кристалла и прожектора
  int CrystalAndSpot_menu = glutCreateMenu(CrystalAndSpotExecuteMenu);
  glutAddMenuEntry("Плоское затенение поверхностей", 0);
  glutAddMenuEntry("Гладкое (градиентное) затенение", 1);
  glutAddMenuEntry("Низкая тесселяция (крупные грани)", 2);
  glutAddMenuEntry("Средняя тесселяция (больше граней)", 3);
  glutAddMenuEntry("Высокая аппроксимация (много граней)", 4);
  
  // Создание меню и добавление опций выбора
  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);
  glutAddMenuEntry("8) Самолет с зеркальным отражением", 8);
  glutAddSubMenu("9) Рисование кристалла и прожектора", CrystalAndSpot_menu);
  glutAddMenuEntry("10) Модель самолета и его тени", 10);
  glutAddMenuEntry("11) Освещенный мир сфер", 11);
  
  glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем 
  // Конец создания меню
  
  glutMainLoop();  // Цикл сообщений графического окна
}
Листинг 23.35. Код подключения мира сфер в файле LightAndMaterial.cpp
  • Запустите приложение и выполните упражнение 11, результат должен быть примерно таким

  • Поуправляйте сценой с помощью клавиш-стрелок и попытайтесь разобраться с кодом


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