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

Непрограммируемый конвейер в OpenGL

Упражнение 7. Прямая загрузка матриц преобразований (SphereWorld)

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

  • Добавьте к приложению новый заголовочный файл с именем SphereWorld.h
  • Наполните файл SphereWorld.h следующим кодом
//**********************************************************
// Упражнение 7: "7) Фантастический мир со сферами"
#include <math.h>
#define GLT_PI  3.14159265358979323846
#define GLT_PI_DIV_180 0.017453292519943296
#define gltDegToRad(x)  ((x)*GLT_PI_DIV_180)
#define NUM_SPHERES      50 // Количество сфер
typedef GLfloat GLTVector3[3];      // Вектор
typedef GLfloat GLTMatrix[16];      // Матрица 4x4
typedef struct{                     // Фрейм 3x3
        GLTVector3 vLocation;
        GLTVector3 vUp;
        GLTVector3 vForward;
         } GLTFrame;
GLTFrame    spheres[NUM_SPHERES];
GLTFrame    frameCamera;
  
//**********************************************************
// Прототипы
void SphereWorld();
void DrawGround();
void RenderSceneSphereWorld();
void ChangeSizeSphereWorld(int, int);
void SpecialKeysSphereWorld(int);
void SetupSphereWorld();
  
void gltApplyCameraTransform(GLTFrame *);
void gltApplyActorTransform(GLTFrame *);
void gltGetMatrixFromFrame(GLTFrame *, GLTMatrix);
void gltDrawTorus(GLfloat, GLfloat, GLint, GLint);
void gltNormalizeVector(GLTVector3);
void gltScaleVector(GLTVector3, const GLfloat);
GLfloat gltGetVectorLength(const GLTVector3);
GLfloat gltGetVectorLengthSqrd(const GLTVector3);
void gltInitFrame(GLTFrame *);
void gltMoveFrameForward(GLTFrame *, GLfloat);
void gltRotateFrameLocalY(GLTFrame *, GLfloat);
void gltRotationMatrix(float, float, float, float, GLTMatrix);
void gltRotateVector(const GLTVector3, const GLTMatrix, GLTVector3);
void gltVectorCrossProduct(const GLTVector3, const GLTVector3, GLTVector3);
void gltLoadIdentityMatrix(GLTMatrix);
  
//**********************************************************
// Формировать сцену
void SphereWorld(void)
{
  static GLfloat yRot = 0.0f;  // Угол поворота для анимации в градусах
  
  glPushMatrix();
    gltApplyCameraTransform(&frameCamera);
    // Рисуем сетку
    DrawGround();
    // Рисовать сферы по ранее сгенерированным случайным координатам
    for(int i = 0; i < NUM_SPHERES; i++)
    {
      glPushMatrix();
        gltApplyActorTransform&spheres[i]);
        glutSolidSphere(0.1f, 13, 26);
      glPopMatrix();
    }
    glPushMatrix();
      glTranslatef(0.0f, 0.0f, -2.5f);
      glPushMatrix();
        glRotatef(-yRot * 2.0f, 0.0f, 1.0f, 0.0f);
        glTranslatef(1.0f, 0.0f, 0.0f);
        glutSolidSphere(0.1f, 13, 26);
      glPopMatrix();
      glRotatef(yRot, 0.0f, 1.0f, 0.0f);
      gltDrawTorus(0.35, 0.15, 40, 20);
    glPopMatrix();
  glPopMatrix();
  
  // Наращиваем анимацию
  yRot += 0.5f;
  
  // Переключаем буферы
  glutSwapBuffers();
}
  
//**********************************************************
// Рисовать сетку основания
void DrawGround(void)
{
  GLfloat fExtent = 20.0f;
  GLfloat fStep = 1.0f;
  GLfloat y = -0.4f;
  GLint iLine;
  
  glBegin(GL_LINES);
    for(iLine = -fExtent; iLine <= fExtent; iLine += fStep)
    {
      glVertex3f(iLine, y, fExtent);    // Рисовать Z-линии
      glVertex3f(iLine, y, -fExtent);
  
      glVertex3f(fExtent, y, iLine);
      glVertex3f(-fExtent, y, iLine);
    }
  glEnd();
}
  
//**********************************************************
// Рендеринг
void RenderSceneSphereWorld()
{
  // Сохраняем атрибуты в стеке атрибутов!!!
  glPushAttrib(GL_COLOR_BUFFER_BIT | GL_POLYGON_BIT);
  
  // Устанавливаем белый цвет и голубой фон
  glColor3ub(255, 255, 255); // Белый
  glClearColor(0.0f, 0.0f, .50f, 1.0f );
  // Сбрасываем буферы цвета и глубины
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  // Рисовать с двух сторон в каркасном режиме
  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
  
  SphereWorld(); // Рисуем сцену
  
  // Восстанавливаем атрибуты из стека атрибутов!!!
  glPopAttrib();
  
  // Прокачка сообщений
  glFlush();
}
  
//**********************************************************
void ChangeSizeSphereWorld(int width, int height)
{
  // Предотвращаем деление на нуль
  if(height == 0)
    height = 1;
  
  // Устанавливаем поле просмотра с размерами окна
  glViewport(0, 0, width, height);
  
  // Характеристическое число для соблюдения пропорций
  GLfloat aspectRatio = (GLfloat)width / (GLfloat)height;// Для коррекции
  
  // Устанавливает матрицу преобразования в режим проецирования
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  
  // Устанавливаем перспективную проекцию
  gluPerspective(35.0f, aspectRatio, 1.0f, 50.0f);
  
  // Восстановливает матрицу преобразования в исходный режим вида
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}
  
//**********************************************************
// Управление фреймом камеры
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 gltApplyCameraTransform(GLTFrame *pCamera)
{
  GLTMatrix mMatrix;
  GLTVector3 vAxisX;
  GLTVector3 zFlipped;
  
  zFlipped[0] = -pCamera->vForward[0];
  zFlipped[1] = -pCamera->vForward[1];
  zFlipped[2] = -pCamera->vForward[2];
  
  // Получим вектор X
  gltVectorCrossProduct(pCamera->vUp, zFlipped, vAxisX);
  
  // Формируем матрицу поворота и транспонируем ее
  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;
  
  // Выполним поворот
  glMultMatrixf(mMatrix);
  
  // Теперь отодвигаемся назад
  glTranslatef(-pCamera->vLocation[0], -pCamera->vLocation[1], -pCamera->vLocation[2]);
}
  
//**********************************************************
void gltApplyActorTransform(GLTFrame *pFrame)
{
  GLTMatrix mTransform;
  gltGetMatrixFromFrame(pFrame, mTransform);
  glMultMatrixf(mTransform);
}
  
//**********************************************************
void gltGetMatrixFromFrame(GLTFrame *pFrame, GLTMatrix mMatrix)
{
  GLTVector3 vXAxis;
  
  gltVectorCrossProduct(pFrame->vUp, pFrame->vForward, vXAxis);
  
  memcpy(mMatrix, vXAxis, sizeof(GLTVector3));
  mMatrix[3] = 0.0f;
  
  memcpy(mMatrix+4, pFrame->vUp, sizeof(GLTVector3));
  mMatrix[7] = 0.0f;
  
  memcpy(mMatrix+8, pFrame->vForward, sizeof(GLTVector3));
  mMatrix[11] = 0.0f;
  
  memcpy(mMatrix+12, pFrame->vLocation, sizeof(GLTVector3));
  mMatrix[15] = 1.0f;
}
  
//**********************************************************
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;
  
  for (int 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 (int 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);
  
        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();
  }
}
  
//**********************************************************
void gltNormalizeVector(GLTVector3 vNormal)
{ 
  GLfloat fLength = 1.0f / gltGetVectorLength(vNormal);
  gltScaleVector(vNormal, fLength); 
}
  
//**********************************************************
void gltScaleVector(GLTVector3 vVector, const GLfloat fScale)
{ 
  vVector[0] *= fScale; vVector[1] *= fScale; vVector[2] *= fScale; 
}
  
//**********************************************************
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 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;
}
  
//**********************************************************
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;
}
  
//**********************************************************
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));
}
  
//**********************************************************
// Создание матрицы поворота 4x4
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(x == 0.0f && y == 0.0f && z == 0.0f)
  {
    gltLoadIdentityMatrix(mMatrix);
    return;
  }
  
  // Нормализуем вектор
  vecLength = (float)sqrt( x*x + y*y + z*z );
  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;
}
  
//**********************************************************
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];      
}
  
//**********************************************************
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 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));
}
  
//**********************************************************
// Устанавливается состояние инициализации
void SetupSphereWorld(void)
{
  gltInitFrame(&frameCamera);  // Инициализируем камеру
  
  // Генерируем центры 50 сфер случайным образом
  // в плоскости x0z внутри рамки от -20 до 20
  for(int iSphere = 0; iSphere < NUM_SPHERES; iSphere++)
  {
    gltInitFrame&spheres[iSphere]);    // Инициализируем фрейм
  
    // На плоскости x0z размещаем случайно центры
    // сфер в диапазоне от -20 до 20 с шагом .1
    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;
  }
}
Листинг 21.22. Код файла SphereWorld.h для моделирования мира сфер
  • Скорректируйте файл ProjectionMatrix.cpp для седьмого упражнения. В листинге выделены места внесения изменений
//**********************************************************
// Подключение стандартного файла с библиотекой OpenGL
#include "stdafx.h"
  
//**********************************************************
// Прототипы функций
void ExecuteMenu(int);  // Контекстное меню первого уровня
void TimerFunc(int);    // Обработчик события таймера
void SetupRC(void);      // Начальные настройки OpenGL
void SpecialKeys(int, int, int);  // Обработка нажатия клавиш
void RenderScene(void);
void ChangeSize(int, int);
  
// Глобальная переменная выбранного варианта основного меню
int choice = 1;
  
// Глобальные переменные для создания вращения
// в градусах
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
GLint w, h; // Ширина и высота экрана
  
//**********************************************************
// Подключение файлов с упражнениями
#include "Atom.h"      // Упражнение 1: "1) Простая модель атома"
#include "Ortho.h"      // Упражнение 2: "2) Брусок в ортогональной проекции"  
#include "Perspect.h"    // Упражнение 3: "3) Брусок в перспективной проекции"
#include "AtomPerspect.h"  // Упражнение 4: "4) Модель атома в перспективе"
#include "Solar.h"      // Упражнение 5: "5) Система \"Солнце-Земля-Луна\""
#include "Transform.h"    // Упражнение 6: "6) Каркасный тор - своя матрица" 
#include "SphereWorld.h"  // Упражнение 7: "7) Фантастический мир со сферами"
  
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
  // Сбрасываем углы вращения прежнего варианта
  xRot = yRot = 0;
  
  // Запоминаем выбор в глобальную переменную
  ::choice = choice; 
  
  switch(::choice)
  {
    case 1:
      ChangeSizeAtom(w, h);
      break;
    case 2:
      ChangeSizeOrtho(w, h);
      break;
    case 3:
      ChangeSizePerspect(w, h);
      break;
    case 4:
      ChangeSizeAtomPerspect(w, h);
      break;
    case 5:
      ChangeSizeSolar(w, h);
      break;
    case 6:
      ChangeSizeTransform(w, h);
      break;
    case 7:
      ChangeSizeSphereWorld(w, h);
      break;
  }
  
  // Вызвать принудительно визуализацию
  glutPostRedisplay();
}
  
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
  switch(::choice)
  {
    case 1:
      SetupLight(false);
      RenderSceneAtom();
      break;
    case 2:
      SetupLight(true);
      RenderSceneOrtho();
      break;
    case 3:
      SetupLight(true);
      RenderScenePerspect();
      break;
    case 4:
      SetupLight(false);
      RenderSceneAtomPerspect();
      break;
    case 5:
      SetupLightSolar(true);
      RenderSceneSolar();
      break;
    case 6:
      SetupLight(false);
      RenderSceneTransform();
      break;
    case 7:
      SetupLight(false);
      RenderSceneSphereWorld();
      break;
  }
}
  
//**********************************************************
// Вызывается библиотекой GLUT при изменении размеров окна
void ChangeSize(int width, int height)
{  
  w = width;
  h = height;
  
  switch(::choice)
  {
    case 1:
      ChangeSizeAtom(width, height);
      break;
    case 2:
      ChangeSizeOrtho(width, height);
      break;
    case 3:
      ChangeSizePerspect(width, height);
      break;
    case 4:
      ChangeSizeAtomPerspect(width, height);
      break;
    case 5:
      ChangeSizeSolar(width, height);
      break;
    case 6:
      ChangeSizeTransform(width, height);
      break;
    case 7:
      ChangeSizeSphereWorld(width, height);
      break;
  }
}
  
//**********************************************************
// Обработчик события таймера
void TimerFunc(int value)
{
  glutPostRedisplay(); // Перерисовка сцены
  
  switch(::choice)
  {
    case 6:
      glutTimerFunc(33, TimerFunc, 1);  // Заряжаем новый таймер
      break;
    case 7:
      glutTimerFunc(3, TimerFunc, 1);    // Заряжаем новый таймер
      break;
    default:
      glutTimerFunc(100, TimerFunc, 1);  // Заряжаем новый таймер
  }
}
  
//**********************************************************
// Устанавливается состояние инициализации
void SetupRC(void)
{
  glClearColor(0.0F, 0.0F, 0.0F, 1.0F);// Фон черный непрозрачный
  
  glEnable(GL_DEPTH_TEST);  // Включили проверку глубины
  glFrontFace(GL_CCW);    // Лицевыми будем считать те грани, вершины
                // которых обходятся против часовой стрелки
  glEnable(GL_CULL_FACE);    // Включили режим отсечения сторон
  
  SetupSphereWorld();      // Формируем сферы для упражнения 7
}
  
//**********************************************************
// Управление с клавиатуры стрелками для
// задания новых значений матрицы поворота
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;
  
      break;
    case 7:
      SpecialKeysSphereWorld(key);
      break;
  }
   
  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);  // Создали таймер
  SetupRC();
  glutSpecialFunc(SpecialKeys);    // Для управления с клавиатуры
    
  // Создание меню и добавление опций выбора
  glutCreateMenu(ExecuteMenu);
  glutAddMenuEntry("1) Простая модель атома", 1);
  glutAddMenuEntry("2) Брусок в ортогональной проекции", 2);
  glutAddMenuEntry("3) Брусок в перспективной проекции", 3);
  glutAddMenuEntry("4) Модель атома в перспективе", 4);
  glutAddMenuEntry("5) Система \"Солнце-Земля-Луна\"", 5);
  glutAddMenuEntry("6) Каркасный тор - своя матрица", 6);
  glutAddMenuEntry("7) Фантастический мир со сферами", 7);
  
  glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем 
  // Конец создания меню
  
  glutMainLoop();  // Цикл сообщений графического окна
}
Листинг 21.23. Код файла ProjectionMatrix.cpp после добавления седьмого упражнения
  • Попробуйте разобраться с кодом, запустите упражнение, поуправляйте сценой с помощью стрелок клавиатуры

На динамической модели мгновенные снимки экрана мира сфер могут быть такими



  • Сдайте работу преподавателю


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