Украина, Киев |
Непрограммируемый конвейер в 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 тора могут быть такими