Прежде чем мы начнем давайте вспомним нашу систему обратных вызовов, как она определена, каким образом главное окно и подчиненные окна были созданы:
- idle function – renderSceneAll; - display func for main window – renderScene; - display func for subwindow 1 – renderScenesw1; - display func for subwindow 2 – renderScenesw2; - display func for subwindow 3 – renderScenesw3;
Мы начнем с функции дисплея для каждого окна. Главное окно, покрытое подокнами, мы только хотим покрасить в чёрный. Так как мы работаем с несколькими окнами первое, что необходимо сделать, это вызвать glutSetWindow с соответствующим ID окна. Тогда мы просто очистим буфер цвета с цветом по умолчанию, это всегда черный.
void renderScene() { glutSetWindow(mainWindow); glClear(GL_COLOR_BUFFER_BIT); glutSwapBuffers(); }
Мы должны определить функции отображения для каждого дополнительного окна. В нашем примере, геометрия является одинаковой для всех окон, единственное, что отличается, это точка зрения, или камера.
Функции, где общая геометрия визуализируется, называется renderScene2. Однако, прежде чем вызывать эту функцию, мы должны установить текущее окно, чтобы соответствующее подокно загружало единичную матрицу, очищало матрицу вида модели, и устанавливало камеру с gluLookAt.
Как упоминалось ранее в начальном уроке, посвященном подокнам, у нас есть три подчиненных окна с разными точками зрения одной и той же сцены. Первое подокно отображает сцену с основной камеры. Второе подокно отображает ту же сцену сверху. Третье подокно показывает сцену из камеры справа от текущей позиции.
Следующий код определяет функции дисплея для каждого окна. Этот код представляет собой расширенную версию предыдущего кода. Если вам требуется более подробно, просто повторите предыдущие уроки, а именно перемещение камеры с помощью клавиатуры, растровые шрифты и ортогональные проекции для отображения текста, или вычисление количество кадров в секунду.
Следует отметить, что существует несколько различий в содержании окон. Верхнее окно будет отображать счётчик кадров с помощью растровых строк. Два окна снизу отображают красный конус в положении основной камеры.
// функция рендеринга для всех подокон void renderScene2() { // нарисуем землю glColor3f(0.9f, 0.9f, 0.9f); glBegin(GL_QUADS); glVertex3f(-100.0f, 0.0f, -100.0f); glVertex3f(-100.0f, 0.0f, 100.0f); glVertex3f( 100.0f, 0.0f, 100.0f); glVertex3f( 100.0f, 0.0f, -100.0f); glEnd(); // нарисуем 64 снеговика char number[4]; for(int i = -4; i < 4; i++) for(int j=-4; j < 4; j++) { glPushMatrix(); glTranslatef(i*10.0f, 0.0f, j * 10.0f); drawSnowMan(); glPopMatrix(); } } // Функция рендеринга для главного окна void renderScene() { glutSetWindow(mainWindow); glClear(GL_COLOR_BUFFER_BIT); glutSwapBuffers(); } // Функция рендеринга для подокна 1 void renderScenesw1() { glutSetWindow(subWindow1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt(x, y, z, x + lx,y + ly,z + lz, 0.0f,1.0f,0.0f); renderScene2(); // показать кадры в секунду frame++; time=glutGet(GLUT_ELAPSED_TIME); if (time - timebase > 1000) { sprintf(s,"Lighthouse3D - FPS:%4.2f", frame*1000.0/(time-timebase)); timebase = time; frame = 0; } setOrthographicProjection(); glPushMatrix(); glLoadIdentity(); renderBitmapString(5,30,0,GLUT_BITMAP_HELVETICA_12,s); glPopMatrix(); restorePerspectiveProjection(); glutSwapBuffers(); } // Функция рендеринга для подокна 2 void renderScenesw2() { glutSetWindow(subWindow2); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt(x, y+15, z, x ,y - 1,z, lx,0,lz); // нарисовать красный конус glPushMatrix(); glColor3f(1.0,0.0,0.0); glTranslatef(x,y,z); glRotatef(180-(angle+deltaAngle)*180.0/3.14,0.0,1.0,0.0); glutSolidCone(0.2,0.8f,4,4); glPopMatrix(); renderScene2(); glutSwapBuffers(); } // Функция рендеринга для подокна 3 void renderScenesw3() { glutSetWindow(subWindow3); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt(x-lz*10 , y, z+lx*10, x ,y ,z , 0.0f,1.0f,0.0f); // нарисовать красный конус glPushMatrix(); glColor3f(1.0,0.0,0.0); glTranslatef(x,y,z); glRotatef(180-(angle+deltaAngle)*180.0/3.14,0.0,1.0,0.0); glutSolidCone(0.2,0.8f,4,4); glPopMatrix(); renderScene2(); glutSwapBuffers(); }
Теперь все, что останется сделать, это определить глобальную функции. В нашем примере эта функция renderSceneAll. Эта функция проверяет, если переменные deltaMove и deltaAngle или не равны нулю, и обновляет значения текущей позиции, а также вектор зрения камеры.
Впоследствии мы называем функции дисплея для каждого подокна. Обратите внимание, что мы не призываем функции рендеринга для главного окна, потому что его содержание никогда не меняются.
// Глобальная функция отрисовки void renderSceneAll() { // проверить перемещения с клавиатуры if (deltaMove) computePos(deltaMove); renderScenesw1(); renderScenesw2(); renderScenesw3(); }
Итоговый код
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <glut.h> // угол поворота камеры float angle = 0.0f; // координаты вектора направления движения камеры float lx=0.0f,lz=-1.0f, ly = 0.0f; // XZ позиция камеры float x=0.0f, z=5.5f, y = 1.65f; //Ключи статуса камеры. Переменные инициализируются нулевыми значениями //когда клавиши не нажаты float deltaAngle = 0.0f; float deltaMove = 0; int xOrigin = -1; //ширина и высота окна int h,w; //переменные для вычисления кадров в секунду int frame; long time, timebase; char s[50]; // переменные для хранения идентификаторв окна int mainWindow, subWindow1,subWindow2,subWindow3; //Граница между подокнами int border = 6; void setProjection(int w1, int h1) { float ratio; // предотвращение деления на ноль ratio = 1.0f * w1 / h1; // обнулить координаты матрицы проекции glMatrixMode(GL_PROJECTION); glLoadIdentity(); // установить вьюпорт glViewport(0, 0, w1, h1); // Установить объем отсечения gluPerspective(45,ratio,0.1,1000); glMatrixMode(GL_MODELVIEW); } void changeSize(int w1,int h1) { if(h1 == 0) h1 = 1; //сохраним эти значения w = w1; h = h1; // установить активным подокно 1 glutSetWindow(subWindow1); // изменить размеры и позиция подокна glutPositionWindow(border,border); glutReshapeWindow(w-2*border, h/2 - border*3/2); setProjection(w-2*border, h/2 - border*3/2); // установить активным подокно 2 glutSetWindow(subWindow2); // изменить размеры и позиция подокна glutPositionWindow(border,(h+border)/2); glutReshapeWindow(w/2-border*3/2, h/2 - border*3/2); setProjection(w/2-border*3/2,h/2 - border*3/2); // установить активным подокно 3 glutSetWindow(subWindow3); // изменить размеры и позиция подокна glutPositionWindow((w+border)/2,(h+border)/2); glutReshapeWindow(w/2-border*3/2,h/2 - border*3/2); setProjection(w/2-border*3/2,h/2 - border*3/2); } void drawSnowMan() { glColor3f(1.0f, 1.0f, 1.0f); // тело снеговика glTranslatef(0.0f ,0.75f, 0.0f); glutSolidSphere(0.75f,20,20); // голова снеговика glTranslatef(0.0f, 1.0f, 0.0f); glutSolidSphere(0.25f,20,20); // глаза снеговика glPushMatrix(); glColor3f(0.0f,0.0f,0.0f); glTranslatef(0.05f, 0.10f, 0.18f); glutSolidSphere(0.05f,10,10); glTranslatef(-0.1f, 0.0f, 0.0f); glutSolidSphere(0.05f,10,10); glPopMatrix(); // нос снеговика glColor3f(1.0f, 0.5f, 0.5f); glRotatef(0.0f,1.0f, 0.0f, 0.0f); glutSolidCone(0.08f,0.5f,10,2); glColor3f(1.0f, 1.0f, 1.0f); } void renderBitmapString( float x, float y, float z, void *font, char *string) { char *c; glRasterPos3f(x, y,z); for (c=string; *c != '\0'; c++) { glutBitmapCharacter(font, *c); } } void restorePerspectiveProjection() { glMatrixMode(GL_PROJECTION); //восстановить предыдущую матрицу проекции glPopMatrix(); //вернуться в режим модели glMatrixMode(GL_MODELVIEW); } void setOrthographicProjection() { //выбрать режим проекции glMatrixMode(GL_PROJECTION); //Сохраняем предыдущую матрицу, которая содерж //параметры перспективной проекции glPushMatrix(); //обнуляем матрицу glLoadIdentity(); //устанавливаем 2D ортогональную проекцию gluOrtho2D(0, w, h, 0); //выбираем режим обзора модели glMatrixMode(GL_MODELVIEW); } void computePos(float deltaMove) { x += deltaMove * lx * 0.1f; z += deltaMove * lz * 0.1f; } // Общие элементы визуализации для всех подчиненных окон void renderScene2() { // нарисуем "землю" glColor3f(0.9f, 0.9f, 0.9f); glBegin(GL_QUADS); glVertex3f(-100.0f, 0.0f, -100.0f); glVertex3f(-100.0f, 0.0f, 100.0f); glVertex3f( 100.0f, 0.0f, 100.0f); glVertex3f( 100.0f, 0.0f, -100.0f); glEnd(); // Нарисуем 64 снеговика for(int i = -4; i < 4; i++) for(int j=-4; j < 4; j++){ glPushMatrix(); glTranslatef(i*10.0f, 0.0f, j * 10.0f); drawSnowMan(); glPopMatrix(); } } //функция рендеринга основного окна void renderScene() { glutSetWindow(mainWindow); glClear(GL_COLOR_BUFFER_BIT); glutSwapBuffers(); } // функция рендеринга для подокна 1 void renderScenesw1() { glutSetWindow(subWindow1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt(x, y, z, x + lx,y + ly,z + lz, 0.0f,1.0f,0.0f); renderScene2(); // показать кадры в секунду frame++; time=glutGet(GLUT_ELAPSED_TIME); if (time - timebase > 1000) { sprintf(s,"Informatika - FPS:%4.2f", frame*1000.0/(time-timebase)); timebase = time; frame = 0; } setOrthographicProjection(); glPushMatrix(); glLoadIdentity(); renderBitmapString(5,30,0,GLUT_BITMAP_HELVETICA_12,s); glPopMatrix(); restorePerspectiveProjection(); glutSwapBuffers(); } // функция рендеринга для подокна 2 void renderScenesw2() { glutSetWindow(subWindow2); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt(x, y+15, z, x ,y - 1,z, lx,0,lz); // нарисовать красный конус в области основной камеры glPushMatrix(); glColor3f(1.0,0.0,0.0); glTranslatef(x,y,z); glRotatef(180-(angle+deltaAngle)*180.0/3.14,0.0,1.0,0.0); glutSolidCone(0.2,0.8f,4,4); glPopMatrix(); renderScene2(); glutSwapBuffers(); } // функция рендеринга для подокна 3 void renderScenesw3() { glutSetWindow(subWindow3); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt(x-lz*10 , y, z+lx*10, x ,y ,z , 0.0f,1.0f,0.0f); // нарисовать красный конус в области основной камеры glPushMatrix(); glColor3f(1.0,0.0,0.0); glTranslatef(x,y,z); glRotatef(180-(angle+deltaAngle)*180.0/3.14,0.0,1.0,0.0); glutSolidCone(0.2,0.8f,4,4); glPopMatrix(); renderScene2(); glutSwapBuffers(); } // Глобальная функция рендеринга void renderSceneAll() { // проверить перемещения камеры от клавиатуры if (deltaMove) computePos(deltaMove); renderScenesw1(); renderScenesw2(); renderScenesw3(); } // ----------------------------------- // // клавиатура // // ----------------------------------- // void processNormalKeys(unsigned char key, int xx, int yy) { if (key == 27) { glutDestroyWindow(subWindow1); glutDestroyWindow(subWindow2); glutDestroyWindow(subWindow3); glutDestroyWindow(mainWindow); exit(0); } } void pressKey(int key, int xx, int yy) { switch (key) { case GLUT_KEY_UP : deltaMove = 0.5f; break; case GLUT_KEY_DOWN : deltaMove = -0.5f; break; } } void releaseKey(int key, int x, int y) { switch (key) { case GLUT_KEY_UP : case GLUT_KEY_DOWN : deltaMove = 0;break; } } // ----------------------------------- // // функции мыши // // ----------------------------------- // void mouseMove(int x, int y) { // только когда левая кнопка не активна if (xOrigin >= 0) { // обновить deltaAngle deltaAngle = (x - xOrigin) * 0.001f; // обновить направление камеры lx = sin(angle + deltaAngle); lz = -cos(angle + deltaAngle); } } void mouseButton(int button, int state, int x, int y) { // только начало движение, если левая кнопка мыши нажата if (button == GLUT_LEFT_BUTTON) { // когда кнопка отпущена if (state == GLUT_UP) { angle += deltaAngle; xOrigin = -1; } else {// state = GLUT_DOWN xOrigin = x; } } } // ------------------------------------ // // main() // // ----------------------------------- // void init() { glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); // регистрация вызовов glutIgnoreKeyRepeat(1); glutKeyboardFunc(processNormalKeys); glutSpecialFunc(pressKey); glutSpecialUpFunc(releaseKey); glutMouseFunc(mouseButton); glutMotionFunc(mouseMove); } int main(int argc, char **argv) { // инициализация GLUT и создание окна glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA); glutInitWindowPosition(100,100); glutInitWindowSize(800,800); mainWindow = glutCreateWindow("Урок - 16"); // регистрация вызовов для главного окна glutDisplayFunc(renderScene); glutReshapeFunc(changeSize); glutIdleFunc(renderSceneAll); init(); // для подокон subWindow1 = glutCreateSubWindow(mainWindow, border,border,w-2*border, h/2 - border*3/2); glutDisplayFunc(renderScenesw1); init(); subWindow2 = glutCreateSubWindow(mainWindow, border,(h+border)/2,w/2-border*3/2, h/2 - border*3/2); glutDisplayFunc(renderScenesw2); init(); subWindow3 = glutCreateSubWindow(mainWindow, (w+border)/2,(h+border)/2,w/2-border*3/2,h/2 - border*3/2); glutDisplayFunc(renderScenesw3); init(); // главный цикл glutMainLoop(); return 1; }