Непрограммируемый конвейер в OpenGL
Упражнение 6. Прямая загрузка матриц преобразований (Transform)
В начале этой работы мы упоминали возможность прямой загрузки значений в текущие матрицы преобразования командами:
В данном упражнении 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 тора могут быть такими


