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


