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

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

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

В начале этой работы мы упоминали возможность прямой загрузки значений в текущие матрицы преобразования командами:

  • glLoadIdentity()
  • glLoadMatrix{fd}(const TYPE *m)
  • glLoadTransposeMatrix{fd}(const TYPE *m)

В данном упражнении Transform мы применим эти возможности для моделирования вращающегося тора.

Мы сами будем предварительно вычислять матрицу преобразования M размером 4x4, и применять команду загрузки glLoadMatrix(), передавая в качестве аргумента указатель на M. В программном представлении матрица M должна быть одномерным массивом m, элементы которого следуют в порядке расположения столбцов. Это связано с тем, что двумерные массивы в языке C, C++ располагаются в памяти в порядке следования строк. Графическая система OpenGL рассматривает последовательность расположенных в памяти 16 элементов как следующие друг за другом 4 столбца матрицы M.

Это значит, что имея математическое представление матрицы преобразования M для правильной ее загрузки командой glLoadMatrix() мы должны массив m сформировать в порядке следования столбцов матрицы M. Другой способ - загружать матрицу M с помощью команды glLoadTransposeMatrix() как транспонированную.


  • Добавьте к приложению новый заголовочный файл с именем Transform.h
  • Наполните файл Transform.h следующим кодом
//**********************************************************
// Упражнение 6: "6) Каркасный тор - своя матрица"
#include <math.h>
#define GLT_PI  3.14159265358979323846
#define GLT_PI_DIV_180 0.017453292519943296
typedef GLfloat GLTVector3[3];      // Вещественная точка
typedef GLfloat GLTMatrix[16]; 
  
//**********************************************************
// Прототипы
void DrawTorus(void);
void RenderSceneTransform(void);
void ChangeSizeTransform(int, int);
void RotationMatrix(float, float, float, float, GLTMatrix);
void LoadIdentityMatrix(GLTMatrix);
void TransformPoint(const GLTVector3, const GLTMatrix, GLTVector3);
  
//**********************************************************
void DrawTorus(void)
{
  GLTMatrix   mTransform;    // Объявление матрицы поворота
  static GLfloat yRot = 0.0f;  // Угол поворота для анимации в градусах
  
  // Строим матрицу преобразования
  // Угол преобразуем в радианы
  RotationMatrix(GLT_PI_DIV_180 * yRot, 0.0f, 1.0f, 0.0f, mTransform);
  mTransform[12] = 0.0f;
  mTransform[13] = 0.0f;
  mTransform[14] = -2.5f;
  
  GLfloat majorRadius = 0.35f;
  GLfloat minorRadius = 0.15f;
  GLint   numMajor = 40;
  GLint   numMinor = 20;
  GLTVector3 objectVertex;         // Вершины в исходном состоянии
  GLTVector3 transformedVertex;    // Преобразованные вершины
  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);
  
        // Первая точка
        objectVertex[0] = x0 * r;
        objectVertex[1] = y0 * r;
        objectVertex[2] = z;
        TransformPoint(objectVertex, mTransform, transformedVertex);
        glVertex3fv(transformedVertex);
  
        // Вторая точка
        objectVertex[0] = x1 * r;
        objectVertex[1] = y1 * r;
        objectVertex[2] = z;
        TransformPoint(objectVertex, mTransform, transformedVertex);
        glVertex3fv(transformedVertex);
      }
        glEnd();
  }
  
  // Наращиваем анимацию
  yRot += 1.5f;
  
  // Переключаем буферы
  glutSwapBuffers();
}
  
//**********************************************************
void RenderSceneTransform(void)
{
  // Сохраняем атрибуты в стеке атрибутов!!!
  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);
  
  DrawTorus();
  
  // Восстанавливаем атрибуты из стека атрибутов!!!
  glPopAttrib();
  
  // Прокачка сообщений
  glFlush();
}
  
//**********************************************************
void ChangeSizeTransform(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();
}
  
//**********************************************************
// Создание матрицы поворота 4x4
void RotationMatrix(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)
  {
    LoadIdentityMatrix(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 LoadIdentityMatrix(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 TransformPoint(const GLTVector3 vSrcVector, const GLTMatrix mMatrix, GLTVector3 vOut)
{
  vOut[0] = mMatrix[0] * vSrcVector[0] + mMatrix[4] * vSrcVector[1] + mMatrix[8] *  vSrcVector[2] + mMatrix[12];
  vOut[1] = mMatrix[1] * vSrcVector[0] + mMatrix[5] * vSrcVector[1] + mMatrix[9] *  vSrcVector[2] + mMatrix[13];
  vOut[2] = mMatrix[2] * vSrcVector[0] + mMatrix[6] * vSrcVector[1] + mMatrix[10] * vSrcVector[2] + mMatrix[14];    
}
Листинг 21.20. Код файла Transform.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) Простая модель атома"
#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) Каркасный тор - своя матрица"
  
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
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;
  }
  
  // Вызвать принудительно визуализацию
  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;
  }
}
  
//**********************************************************
// Вызывается библиотекой 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;
  }
}
  
//**********************************************************
// Обработчик события таймера
void TimerFunc(int value)
{
  glutPostRedisplay(); // Перерисовка сцены
  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);    // Включили режим отсечения сторон
}
  
//**********************************************************
// Управление с клавиатуры стрелками
// для задания новых значений матрицы поворота
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);  // Создали таймер
  SetupRC();
  glutSpecialFunc(SpecialKeys);    // Для управления с клавиатуры
    
  // Создание меню и добавление опций выбора
  glutCreateMenu(ExecuteMenu);
  glutAddMenuEntry("1) Простая модель атома", 1);
  glutAddMenuEntry("2) Брусок в ортогональной проекции", 2);
  glutAddMenuEntry("3) Брусок в перспективной проекции", 3);
  glutAddMenuEntry("4) Модель атома в перспективе", 4);
  glutAddMenuEntry("5) Система \"Солнце-Земля-Луна\"", 5);
  glutAddMenuEntry("6) Каркасный тор - своя матрица", 6);
  
  glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем 
  // Конец создания меню
  
  glutMainLoop();  // Цикл сообщений графического окна
}
Листинг 21.21. Код файла ProjectionMatrix.cpp после добавления шестого упражнения
  • Разберитесь с кодом, запустите упражнение

На динамической модели мгновенные снимки экрана вращающегося вокруг оси 0y тора могут быть такими



Александр Очеретяный
Александр Очеретяный
Украина, Киев
Анастасия Балыбердина
Анастасия Балыбердина
Украина, Киев, НТУУ КПИ