GLUT Tutorials 16:游戏模式
博客转自:http://www.lighthouse3d.com/tutorials/glut-tutorial/game-mode/
GLUT的游戏模式是使能高性能全屏渲染。然而,总是会有些GLUT函数拖累高性能的需求,例如菜单弹窗,子窗口等。在这个小节,将引入GLUT的游戏模式。我的这个教程的资料。我没有找到官方的文档,或者其他的教程有讲解到这一部分的。所以我也不确定这个教程是100%正确的。我的GLUT游戏模式的经验来自以前自己创建的正常工作的例程,但是由于只在有限的硬件配置测试过,所有可能会有一些部分不精确或不对的陈述。如果你使用教程里的概念设计中遇到了问题,请给我问题的描述,方便我分析调查原因。如果你已经熟悉了GLUT的游戏模式,并再次发现了对游戏模式理解错误的地方,如果将问题反馈给我,我将非常感激,也可以帮助我解决那些问题。
现在我已经完成的我的声明,我们接下来可以开始这部分的教程。首先是声明游戏模式,例如,全屏。这些设置可能包括屏幕分辨率,像素深度和刷新率。总而言之,我们可以设置我们想要的任意的分辨率(硬件允许的范围内)。
These settings for full screen mode are specified on a string. The format is as follows
“WxH:Bpp@Rr” Parameters: W – the screen width in pixels H – the screen height in pixels Bpp – the number of bits per pixel Rr – the vertical refresh rate in hertz
进行下一步之前,这些设置只是一个对硬件的请求。如果声明的模式是不可行的,这些设置会被忽略。
Examples:
- “800×600:32@100” – screen size 800×600; true color (32 bits); 100Hz vertical
- “640×480:16@75” – screen size 640×480; high color (16 bits); 75 hertz
Specifying all the components is a little bit stressfull. Although we usually have a clear idea of the screen resolution, and sometimes we may require a particular color mode, the refresh rate may be trickier. Fortunately we don’t have to specify everything. We can leave some bits out and let GLUT fill in the blanks. The following template strings for partially specifying the desired full screen settings are allowed:
声明所有的配置信息是比较难的。尽管我们 通常有一个清晰的想法对屏幕分辨率,有时我们可能需要一个特殊的颜色模式,刷新率的设置也是需要技巧的。幸运的是,我们不需要声明一切。我们可以设置部分内容,其他部分让GLUT自动填充。下面是一些声明全屏设置的模板。
- “WxH”
- “WxH:Bpp”
- “WxH@Rr”
- “@Rr”
- “:Bpp”
- “Bpp:@Rr”
基本上来说,GLUT可以处理所有的组合只要他们的组合在预先存储的设置模式内。例如,在像素深度之前声明刷新率是不行的。如果我们只关心分辨率而并不关系像素深度和刷新率,我们可以如此设置 "800×600".另外一方面,如果我们只想要在当前分辨率下全屏模式,同时像素深度是32,可以如此设置“:32”.这些例子并不能全部覆盖全屏设置,我们可以使用上面列出的任何一个。
Ready to move on? OK. First we must provide GLUT with the requested settings for the full screen mode. The GLUT’s function to set the game mode is glutGameModeString. The syntax is as follows:
void glutGameModeString(const char *string); Parameters: string – a string containing the desired settings as specified above
尽管函数glutGameModeString并不返回错误码,但是GLUT还是会校验参数设置是否正确,GLUT提供了一个函数检查声明的模式是否合规。
int glutGameModeGet(GLenum info); Parameters: info – the requested information
In order to check if the supplied mode is valid, info takes the value of a GLUTs pre defined constant: GLUT_GAME_MODE_POSSIBLE.
返回非零值代表设置的模式OK。但是GLUT的操作说明警告称:尽管声明的模式OK,但是并不能保证针对屏幕的设置会被成功更新。
设想我们获得了一个非零返回值,之后我们可以进入或者至少可以尝试进入游戏模式。如果设置OK,下面的函数可以使屏幕进入设置的状态。
void glutEnterGameMode(void);
The main function to initialize a GLUT application in game mode at 800 by 600 could be something like this:
int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA); /* glutInitWindowPosition(100,100); glutInitWindowSize(640,360); glutCreateWindow("SnowMen from Lighthouse3D"); */ // setting the game mode replaces the above // calls to set the window size and position. glutGameModeString("800x600:32"); // enter full screen if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)) glutEnterGameMode(); else { printf("The select mode is not available\n"); exit(1); } // register all callbacks init(); glutMainLoop(); return 1; }}
The function init should register all the necessary callbacks as well as perform the openGL required initializations, for instance we could write something like this:
void init() { // register callbacks glutDisplayFunc(renderScene); glutReshapeFunc(changeSize); glutIdleFunc(renderScene); glutIgnoreKeyRepeat(1); glutKeyboardFunc(processNormalKeys); glutSpecialFunc(pressKey); glutSpecialUpFunc(releaseKey); glutMouseFunc(mouseButton); glutMotionFunc(mouseMove); // OpenGL init glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); }
It may be the case that we want to be able to switch between game mode and window mode during the application. The following piece of code assumes that we’re starting in window mode. The user can then press F1 to switch to game mode. F6 brings the user back to window mode. In this case the main function must define the window properties, register the callbacks, and enter the main loop.
Before we look at the code here goes the function that tells glut to leave game mode.
void glutLeaveGameMode(void);
The function that will process the special keys is the one that will perform the mode switch. The following function performs the required operations:
void pressKey(int key, int x, int y) { switch (key) { ... case GLUT_KEY_F1: // define resolution, color depth glutGameModeString("640x480:32"); // enter full screen if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)) { glutEnterGameMode(); // register callbacks again init(); } break; case GLUT_KEY_F6: // return to default window glutLeaveGameMode(); break; } }
There is a detail which is very important in the function above, when we enter the game mode with glutEnterGameMode we must register the callbacks again, and redefine the OpenGL context. The game mode is just like a new window, with a different OpenGL and GLUT context. This implies that the callbacks for the window mode will have no effect in game mode. In order to use callback functions we must register them again. Furthermore, the OpenGL context needs to be defined again. For instance display lists created for the window mode need to be defined again when entering the game mode.
GLUT is an excellent API, and as such it also gives the programmer ways to query the current state of affairs. GLUT has a special function for querying the state settings for the game mode, glutGameModeGet. The syntax for this function was already introduced above when we mentioned that one possible value for the argument was GLUT_GAME_MODE_POSSIBLE.
There are several possibilities for the argument of glutGameModeGet that cover all the needs for correct game mode programming. The return values for each case are presented bellow:
- GLUT_GAME_MODE_ACTIVE – If the app is running in game mode then glutGameModeGet will return a non-zero value, if in window mode it will return zero.
- GLUT_GAME_MODE_POSSIBLE – As mentioned before this can be used to test the string which specifies the game mode setings. It is good policy to call glutGameModeGet with this value before entering game mode.
- GLUT_GAME_MODE_DISPLAY_CHANGED – As mentioned before when entering the game mode there is no guarantee that the display mode is indeed changed. This value can be used to test if the game mode was really entered. If we were previously already in game mode then this value can be used to test if the settings were changed.
- GLUT_GAME_MODE_WIDTH – returns the width of the screen
- GLUT_GAME_MODE_HEIGHT – returns the height of the screen.
- GLUT_GAME_MODE_PIXEL_DEPTH – returns the bits per pixel of the current mode.
- GLUT_GAME_MODE_REFRESH – the actual refresh rate in hertz.
The last four options are meaningful only if we are in game mode. These options will cause glutGameModeGet to return -1 if the latest string specifying the game mode settings is not valid, even if we are already in game mode. So for instance if we’re runnig an app in game mode at 640 by 480 and requested a change to 1600 by 1200, and the mode is not valid for the actual hardware configuration, then GLUT does not change the resolution and the game mode stays at 640 by 480. However when asking for the current height we’ll get -1 and not 480, although the actual height is 480.
下面的代码是对是否正确进入游戏模式的一个显示处理。
if (glutGameModeGet(GLUT_GAME_MODE_ACTIVE) == 0) sprintf(currentMode,"Current Mode: Window"); else sprintf(currentMode, "Current Mode: Game Mode %dx%d at %d hertz, %d bpp", glutGameModeGet(GLUT_GAME_MODE_WIDTH), glutGameModeGet(GLUT_GAME_MODE_HEIGHT), glutGameModeGet(GLUT_GAME_MODE_REFRESH_RATE), glutGameModeGet(GLUT_GAME_MODE_PIXEL_DEPTH));
完整的代码如下
#include <stdio.h> #include <stdlib.h> #include <math.h> #ifdef __APPLE__ #include <GLUT/glut.h> #else #include <GL/glut.h> #endif // angle of rotation for the camera direction float angle = 0.0f; // actual vector representing the camera's direction float lx=0.0f,lz=-1.0f; // XZ position of the camera float x=0.0f, z=5.0f; // the key states. These variables will be zero // when no key is being pressesed float deltaAngle = 0.0f; float deltaMove = 0; int xOrigin = -1; // color for the snowman's nose float red = 1.0f, blue=0.5f, green=0.5f; // scale of snowman float scale = 1.0f; // default font void *font = GLUT_STROKE_ROMAN; // width and height of the window int h,w; // variables to compute frames per second int frame; long time, timebase; char s[60]; char currentMode[80]; // this string keeps the last good setting // for the game mode char gameModeString[40] = "640x480"; void init(); void changeSize(int ww, int hh) { h = hh; w = ww; // Prevent a divide by zero, when window is too short // (you cant make a window of zero width). if (h == 0) h = 1; float ratio = w * 1.0 / h; // Use the Projection Matrix glMatrixMode(GL_PROJECTION); // Reset Matrix glLoadIdentity(); // Set the viewport to be the entire window glViewport(0, 0, w, h); // Set the correct perspective. gluPerspective(45.0f, ratio, 0.1f, 100.0f); // Get Back to the Modelview glMatrixMode(GL_MODELVIEW); } void drawSnowMan() { glScalef(scale, scale, scale); glColor3f(1.0f, 1.0f, 1.0f); // Draw Body glTranslatef(0.0f ,0.75f, 0.0f); glutSolidSphere(0.75f,20,20); // Draw Head glTranslatef(0.0f, 1.0f, 0.0f); glutSolidSphere(0.25f,20,20); // Draw Eyes 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(); // Draw Nose glColor3f(red, green, blue); 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 renderStrokeFontString( float x, float y, float z, void *font, char *string) { char *c; glPushMatrix(); glTranslatef(x, y,z); glScalef(0.002f, 0.002f, 0.002f); for (c=string; *c != '\0'; c++) { glutStrokeCharacter(font, *c); } glPopMatrix(); } void restorePerspectiveProjection() { glMatrixMode(GL_PROJECTION); // restore previous projection matrix glPopMatrix(); // get back to modelview mode glMatrixMode(GL_MODELVIEW); } void setOrthographicProjection() { // switch to projection mode glMatrixMode(GL_PROJECTION); // save previous matrix which contains the //settings for the perspective projection glPushMatrix(); // reset matrix glLoadIdentity(); // set a 2D orthographic projection gluOrtho2D(0, w, h, 0); // switch back to modelview mode glMatrixMode(GL_MODELVIEW); } void computePos(float deltaMove) { x += deltaMove * lx * 0.1f; z += deltaMove * lz * 0.1f; } void renderScene(void) { if (deltaMove) computePos(deltaMove); // Clear Color and Depth Buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Reset transformations glLoadIdentity(); // Set the camera gluLookAt( x, 1.0f, z, x+lx, 1.0f, z+lz, 0.0f, 1.0f, 0.0f); // Draw ground 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(); // Draw 36 SnowMen char number[3]; for(int i = -3; i < 3; i++) for(int j=-3; j < 3; j++) { glPushMatrix(); glTranslatef(i*10.0f, 0.0f, j * 10.0f); drawSnowMan(); sprintf(number,"%d",(i+3)*6+(j+3)); renderStrokeFontString(0.0f, 0.5f, 0.0f, (void *)font ,number); glPopMatrix(); } // Code to compute frames per second 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(); void *font= GLUT_BITMAP_8_BY_13; glPushMatrix(); glLoadIdentity(); renderBitmapString(30,15,0,font,(char *)"GLUT Tutorial @ Lighthouse3D"); renderBitmapString(30,30,0,font,s); renderBitmapString(30,45,0,font,(char *)"F1 - Game Mode 640x480 32 bits"); renderBitmapString(30,60,0,font,(char *)"F2 - Game Mode 800x600 32 bits"); renderBitmapString(30,75,0,font,(char *)"F3 - Game Mode 1024x768 32 bits"); renderBitmapString(30,90,0,font,(char *)"F4 - Game Mode 1280x1024 32 bits"); renderBitmapString(30,105,0,font,(char *)"F5 - Game Mode 1920x1200 32 bits"); renderBitmapString(30,120,0,font,(char *)"F6 - Window Mode"); renderBitmapString(30,135,0,font,(char *)"Esc - Quit"); renderBitmapString(30,150,0,font,currentMode); glPopMatrix(); restorePerspectiveProjection(); glutSwapBuffers(); } // ----------------------------------- // KEYBOARD // ----------------------------------- void processNormalKeys(unsigned char key, int xx, int yy) { switch (key) { case 27: if (glutGameModeGet(GLUT_GAME_MODE_ACTIVE) != 0) glutLeaveGameMode(); exit(0); break; } } 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; case GLUT_KEY_F1: // define resolution, color depth glutGameModeString("640x480:32"); // enter full screen if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)) { glutEnterGameMode(); sprintf(gameModeString,"640x480:32"); // register callbacks again // and init OpenGL context init(); } else glutGameModeString(gameModeString); break; case GLUT_KEY_F2: // define resolution, color depth glutGameModeString("800x600:32"); // enter full screen if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)) { glutEnterGameMode(); sprintf(gameModeString,"800x600:32"); // register callbacks again // and init OpenGL context init(); } else glutGameModeString(gameModeString); break; case GLUT_KEY_F3: // define resolution, color depth glutGameModeString("1024x768:32"); // enter full screen if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)) { glutEnterGameMode(); sprintf(gameModeString,"1024x768:32"); // register callbacks again // and init OpenGL context init(); } else glutGameModeString(gameModeString); break; case GLUT_KEY_F4: // define resolution, color depth glutGameModeString("1280x1024:32"); // enter full screen if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)) { glutEnterGameMode(); sprintf(gameModeString,"1280x1024:32"); // register callbacks again // and init OpenGL context init(); } else glutGameModeString(gameModeString); break; case GLUT_KEY_F5: // define resolution, color depth glutGameModeString("1920x1200"); // enter full screen if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)) { glutEnterGameMode(); sprintf(gameModeString,"1920x1200"); // register callbacks again // and init OpenGL context init(); } else glutGameModeString(gameModeString); break; case GLUT_KEY_F6: // return to default window w = 800;h = 600; if (glutGameModeGet(GLUT_GAME_MODE_ACTIVE) != 0) { glutLeaveGameMode(); //init(); } break; } if (glutGameModeGet(GLUT_GAME_MODE_ACTIVE) == 0) sprintf(currentMode,"Current Mode: Window"); else sprintf(currentMode, "Current Mode: Game Mode %dx%d at %d hertz, %d bpp", glutGameModeGet(GLUT_GAME_MODE_WIDTH), glutGameModeGet(GLUT_GAME_MODE_HEIGHT), glutGameModeGet(GLUT_GAME_MODE_REFRESH_RATE), glutGameModeGet(GLUT_GAME_MODE_PIXEL_DEPTH)); } void releaseKey(int key, int x, int y) { switch (key) { case GLUT_KEY_UP : case GLUT_KEY_DOWN : deltaMove = 0;break; } } // ----------------------------------- // MOUSE // ----------------------------------- void mouseMove(int x, int y) { // this will only be true when the left button is down if (xOrigin >= 0) { // update deltaAngle deltaAngle = (x - xOrigin) * 0.001f; // update camera's direction lx = sin(angle + deltaAngle); lz = -cos(angle + deltaAngle); } } void mouseButton(int button, int state, int x, int y) { // only start motion if the left button is pressed if (button == GLUT_LEFT_BUTTON) { // when the button is released if (state == GLUT_UP) { angle += deltaAngle; xOrigin = -1; } else {// state = GLUT_DOWN xOrigin = x; } } } void init() { // register callbacks glutDisplayFunc(renderScene); glutReshapeFunc(changeSize); glutIdleFunc(renderScene); glutIgnoreKeyRepeat(1); glutKeyboardFunc(processNormalKeys); glutSpecialFunc(pressKey); glutSpecialUpFunc(releaseKey); glutMouseFunc(mouseButton); glutMotionFunc(mouseMove); // OpenGL init glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); } // ----------------------------------- // MAIN // ----------------------------------- int main(int argc, char **argv) { // init GLUT and create window glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA); glutInitWindowPosition(100,100); glutInitWindowSize(800,600); glutCreateWindow("Lighthouse3D - GLUT Tutorial"); // register callbacks init(); // enter GLUT event processing cycle glutMainLoop(); return 1; }
显示效果如下
本人电脑运行游戏模式不同分辨率切换时候会崩溃。