Украина, Киев |
Опубликован: 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 после добавления седьмого упражнения
- Попробуйте разобраться с кодом, запустите упражнение, поуправляйте сценой с помощью стрелок клавиатуры
На динамической модели мгновенные снимки экрана мира сфер могут быть такими
- Сдайте работу преподавателю