GLUT Tutorials 16:游戏模式

大憨熊 提交于 2020-07-27 11:23:01

博客转自: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;
}

显示效果如下

本人电脑运行游戏模式不同分辨率切换时候会崩溃。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!