OpenGL学习笔记(十)

龙云尧个人博客,转载请注明出处。

CSDN地址:http://blog.csdn.net/michael753951/article/details/72810534

个人blog地址:http://yaoyl.cn/nehexue-xi-bi-ji-shi/


概述

本部分博客将以nehe教程第2课,笔记(三)为蓝本,将Windows中完成的基础实验在Ubuntu中进行实现。

【Ubuntu环境配置】中我们已经对Ubuntu中的OpenGL环境进行了配置,并且完成了最基础的茶壶demo,接下来我们将进行实验相关的后续开发。

需求分析

因为实验中我们需要终端接收到的数据能够在图形界面中实时显示出来,这里我们使用nehe教程的第二课内容,绘制一个矩形作为进度条,起始为0%,最高为100%。接着我们将让这个进度条能够对传输过来的信号产生反馈。将整个过程进行拆分,我们可以按照如下步骤进行实现。

  1. 构建一个OpenGL窗口,能够根据本地按键实现进度条控制
  2. 让OpenGL的窗口能够接收其他终端发送过来的消息
  3. 让OpenGL窗口对接收到的信息进行一定的实时反馈(比如进度条变换)

实验

OpenGL窗口搭建

本次使用的代码是以nehe教程第二课中,Linux代码为蓝本,进行修改实现的。【代码链接】

首先我们将窗口显示中的三角形去掉,留下一个长方形,同时将长方形的右边两个点和左边两个点重合以做出进度条为0%的感觉。代码如下:

进度条代码

按钮控制的实现

我在初始化InitGL的时候,将square_len初始化为0,当有按键触发的时候,square_len++,这样就能够完成进度条的前进工作。

在原始代码中,我们可以看到main函数中,有一个glutKeyboardFunc方法,传入了keyPressed的地址,在keyPressed中,定义了使用ESC按钮进行退出的方法。我们将在这里进行尝试,试试方向键左和方向键右能不能让窗口出现一些反馈。

在经过不短的一段时间的寻找之后,我终于找到了在OpenGL中,各种按键的键值是在glut.h中预定义好的。

键值

参考一片CSDN博客【pengl键盘控制一】,我们可以发现在本次程序中,ESC按键确实也刚好是27,这是不是也就意味着我们可以直接按照上面的方法进行修改了?首先我们将ESC的宏定义值修改为102(十进制,对应0x66),尝试使用左键退出窗体程序。

但是很意外的,没有成功。是不是按键本身的键值并不是102?

我对代码进行进一步修改,当有按键活动的时候,记录下来当前按键的键值,将其存进本地文件中。(亲测不能直接printf,因为根本不会显示出来,至于原因待会会有解释)代码如下:

 1/* The function called whenever a key is pressed. */
 2void keyPressed(unsigned char key, int x, int y) {
 3    /* avoid thrashing this procedure */
 4    //usleep(100);
 5
 6    fp = fopen("key_value.txt", "a+"); // a+意味着在文本最后追加
 7    fprintf(fp, "%d\n", key);
 8    fclose(fp);
 9
10    /* If escape is pressed, kill everything. */
11    if (key == ESCAPE) {
12        /* shut down our window */
13        glutDestroyWindow(window);
14
15        /* exit the program...normal termination. */
16        exit(0);
17    }
18}

尝试按下F1~F12的按键,以及上下左右等按键,以及数字按键之后,我们发现txt文档中只记录下来了数字键值,根本没有其他的键值。

为了解决这个问题,我特地打开了nehe的lesson10的linux代码(因为这一课会用到方向键进行控制)。发现原来上下左右这类按键需要在main函数中使用glutSpecialFunc方法,传入一个操作函数进行操作。这里我定义了一个specialKeyPressed方法。在尝试获取键值,并且成功之后,我开始在这里进行进度条的控制。

 1void specialKeyPressed(int key, int x, int y) {
 2    //usleep(100);
 3
 4    /*
 5    fp = fopen("key_value.txt", "a+");
 6    fprintf(fp, "%d\n", key);
 7    fclose(fp);
 8    */
 9    switch(key) {
10    case GLUT_KEY_LEFT:
11        square_len--;
12        if(square_len <= 0) square_len = 0;
13        break;
14    case GLUT_KEY_RIGHT:
15        square_len++;
16        if(square_len >= 100) square_len = 100;
17        break;
18    }
19}

为了避免越界,我们需要将square_len控制在0-100之间。同时我们直接使用glut中宏定义的键值,进行按键判断(我已经对键值进行过测试。发现和宏定义的键值确实一致)。

到这里,我们完成了本次demo的step1,一个使用按键进行进度条控制的OpenGL窗口已经构建成功。

在OpenGL创建的控制台窗口中使用网络协议传输

首先,我们需要知道,在之前的Socket编程中,我们使用的一直都是控制台窗口程序进行的测试,但是在本次实验中,我的理想状态是使用OpenGL建立的窗口作为server,新建一个控制台作为client,然后实验中使用client对server进行控制。

我先定义了一个tcp_server.h头文件。

 1#include <sys/types.h>
 2#include <sys/socket.h>
 3#include <stdio.h>
 4#include <netinet/in.h>
 5#include <arpa/inet.h>
 6#include <unistd.h>
 7#include <string.h>
 8#include <stdlib.h>
 9#include <fcntl.h>
10#include <sys/shm.h>
11
12#define MYPORT  8887
13#define QUEUE   20
14
15bool tcp_server_init(int &server_sockfd, int &conn) {
16    ///定义sockfd
17    server_sockfd = socket(AF_INET,SOCK_STREAM, 0);
18
19    ///定义sockaddr_in
20    struct sockaddr_in server_sockaddr;
21    server_sockaddr.sin_family = AF_INET;
22    server_sockaddr.sin_port = htons(MYPORT);
23    server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
24
25    ///bind,成功返回0,出错返回-1
26    if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1) {
27        perror("bind");
28        return 0;
29    }
30
31    ///listen,成功返回0,出错返回-1
32    if(listen(server_sockfd,QUEUE) == -1) {
33        perror("listen");
34        return 0;
35    }
36
37    ///客户端套接字
38    struct sockaddr_in client_addr;
39    socklen_t length = sizeof(client_addr);
40
41    ///成功返回非负描述字,出错返回-1
42    conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length);
43    if(conn<0) {
44        perror("connect");
45        return 0;
46    }
47    //printf("before_conn\n");
48    return 1;
49}
50
51bool tcp_server_close(int &server_sockfd, int &conn) {
52    close(conn);
53    close(server_sockfd);
54    return 1;
55}

方法一,DrawGLScene中接收消息

首先我尝试直接在main函数中没有进入glutMainLoop之前,建立tcp连接。(tcp_server_init(server_sockfd, conn);方法在上面已经给出来了)然后在DrawGLScene中接受消息,代码如下。

 1/* The main drawing function. */
 2void DrawGLScene() {
 3
 4    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);     // Clear The Screen And The Depth Buffer
 5    glLoadIdentity();               // Reset The View
 6
 7    memset(buffer,0,sizeof(buffer));
 8    int len = recv(conn, buffer, sizeof(buffer),0);
 9    for(int i = 0; i<len; ++i) {
10        if(buffer[0] == 0x66) {
11            square_len++;
12            if(square_len >= 100) square_len = 100;
13        } else if(buffer[0] == 0x64) {
14            square_len--;
15            if(square_len <= 0) square_len = 0;
16        }
17    }
18    fputs(buffer, stdout);
19    send(conn, buffer, len, 0);
20
21    glTranslatef(-1.5f,0.0f,-6.0f);             // Move Right 3 Units
22
23    // draw a square (quadrilateral)
24    glBegin(GL_QUADS);              // start drawing a polygon (4 sided)
25    glVertex3f(-1.0f, 1.0f, 0.0f);      // Top Left
26    glVertex3f(-1.0f+square_len*0.05, 1.0f, 0.0f);      // Top Right
27    glVertex3f(-1.0f+square_len*0.05,-1.0f, 0.0f);      // Bottom Right
28    glVertex3f(-1.0f,-1.0f, 0.0f);      // Bottom Left
29    glEnd();                    // done with the polygon
30
31    // swap buffers to display, since we're double buffered.
32    glutSwapBuffers();
33}

初次尝试

运行结果如图。

初次尝试-运行

刚看到这种情况的时候我以为是程序运行出错,于是经过很长一段时间的搜索才找到了一种解决办法,这个办法我待会会说,这里先说目前的这个问题的解决办法。

首先我们要知道,为什么会出现这个问题,它其实是TCP_\server_init函数中,执行到conn = accept(server_sockfd, (struct sockaddr*)&client\_addr, &length);的时候,在那里停止了,结果导致glut没有继续绘制窗口,最终造成我们看到的窗口很奇怪。对accept稍作了解便知道,这个方法是提取出所监听套接字的等待连接队列中第一个连接请求,创建一个新的套接字,并返回指向该套接字的文件描述符。(参见【socket编程之accept()函数】)这个时候,所以,OpenGL中绘制的窗口会这么奇怪,其实就只是因为server在等待客户端的连接,所以才会继续没有往下执行而已。

我们打开之前的tcp编程中,tcp_client_demo2项目,编译并且运行,和server成功建立上连接成功,理想中,这个时候应该是没有问题了,但是运行以后,显示依然有问题。

问题2

这里我猜测是因为recv阻塞了整个进程,造成后续画笔绘制没办法绘制。因为当我在client中发送一个f之后,server中的窗口就立刻能够正常移动进度条了。

解决1

最终的代码如下:

  1//
  2// This code was created by Jeff Molofee '99 (ported to Linux/GLUT by Richard Campbell '99)
  3//
  4// If you've found this code useful, please let me know.
  5//
  6// Visit me at www.demonews.com/hosted/nehe
  7// (email Richard Campbell at ulmont@bellsouth.net)
  8//
  9#include <GL/glut.h>    // Header File For The GLUT Library
 10#include <GL/gl.h>  // Header File For The OpenGL32 Library
 11#include <GL/glu.h> // Header File For The GLu32 Library
 12#include <unistd.h>     // Header File For sleeping.
 13#include <stdio.h>
 14#include "tcp_server.h"
 15
 16/* ASCII code for the escape key. */
 17#define ESCAPE 27
 18#define VK_LEFT 37
 19#define VK_RIGHT 39
 20
 21FILE *fp = NULL;//需要注意
 22int square_len;
 23
 24/* TCP 链接  */
 25#define BUFFER_SIZE 1024
 26int server_sockfd, conn;
 27char buffer[BUFFER_SIZE];
 28bool server_init_flag;
 29
 30/* The number of our GLUT window */
 31int window;
 32
 33/* A general OpenGL initialization function.  Sets all of the initial parameters. */
 34void InitGL(int Width, int Height) {        // We call this right after our OpenGL window is created.
 35    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);       // This Will Clear The Background Color To Black
 36    glClearDepth(1.0);              // Enables Clearing Of The Depth Buffer
 37    glDepthFunc(GL_LESS);               // The Type Of Depth Test To Do
 38    glEnable(GL_DEPTH_TEST);            // Enables Depth Testing
 39    glShadeModel(GL_SMOOTH);            // Enables Smooth Color Shading
 40
 41    glMatrixMode(GL_PROJECTION);
 42    glLoadIdentity();               // Reset The Projection Matrix
 43
 44    gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);   // Calculate The Aspect Ratio Of The Window
 45
 46    glMatrixMode(GL_MODELVIEW);
 47
 48    square_len = 0;
 49    server_init_flag = false;
 50}
 51
 52/* The function called when our window is resized (which shouldn't happen, because we're fullscreen) */
 53void ReSizeGLScene(int Width, int Height) {
 54    if (Height==0)              // Prevent A Divide By Zero If The Window Is Too Small
 55        Height=1;
 56
 57    glViewport(0, 0, Width, Height);        // Reset The Current Viewport And Perspective Transformation
 58
 59    glMatrixMode(GL_PROJECTION);
 60    glLoadIdentity();
 61
 62    gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);
 63    glMatrixMode(GL_MODELVIEW);
 64}
 65
 66/* The main drawing function. */
 67void DrawGLScene() {
 68
 69    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);     // Clear The Screen And The Depth Buffer
 70    glLoadIdentity();               // Reset The View
 71
 72    memset(buffer,0,sizeof(buffer));
 73    int len = recv(conn, buffer, sizeof(buffer),0);
 74    for(int i = 0; i<len; ++i) {
 75        if(buffer[0] == 0x66) {
 76            square_len++;
 77            if(square_len >= 100) square_len = 100;
 78        } else if(buffer[0] == 0x64) {
 79            square_len--;
 80            if(square_len <= 0) square_len = 0;
 81        }
 82    }
 83    fputs(buffer, stdout);
 84    send(conn, buffer, len, 0);
 85
 86    glTranslatef(-1.5f,0.0f,-6.0f);             // Move Right 3 Units
 87
 88    // draw a square (quadrilateral)
 89    glBegin(GL_QUADS);              // start drawing a polygon (4 sided)
 90    glVertex3f(-1.0f, 1.0f, 0.0f);      // Top Left
 91    glVertex3f(-1.0f+square_len*0.05, 1.0f, 0.0f);      // Top Right
 92    glVertex3f(-1.0f+square_len*0.05,-1.0f, 0.0f);      // Bottom Right
 93    glVertex3f(-1.0f,-1.0f, 0.0f);      // Bottom Left
 94    glEnd();                    // done with the polygon
 95
 96    // swap buffers to display, since we're double buffered.
 97    glutSwapBuffers();
 98}
 99
100/* The function called whenever a key is pressed. */
101void keyPressed(unsigned char key, int x, int y) {
102
103    /* If escape is pressed, kill everything. */
104    if (key == ESCAPE) {
105        /* shut down our window */
106        glutDestroyWindow(window);
107
108        if(!tcp_server_close(server_sockfd, conn)) {
109            exit(1);
110        }
111        /* exit the program...normal termination. */
112        exit(0);
113    }
114}
115
116void specialKeyPressed(int key, int x, int y) {
117    //usleep(100);
118    switch(key) {
119    case GLUT_KEY_LEFT:
120        square_len--;
121        if(square_len <= 0) square_len = 0;
122        break;
123    case GLUT_KEY_RIGHT:
124        square_len++;
125        if(square_len >= 100) square_len = 100;
126        break;
127    }
128}
129
130int main(int argc, char **argv) {
131
132
133    /* Initialize GLUT state - glut will take any command line arguments that pertain to it or
134       X Windows - look at its documentation at http://reality.sgi.com/mjk/spec3/spec3.html */
135    glutInit(&argc, argv);
136
137    /* Select type of Display mode:
138       Double buffer
139       RGBA color
140       Alpha components supported
141       Depth buffer */
142    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH);
143
144    /* get a 640 x 480 window */
145    glutInitWindowSize(640, 480);
146
147    /* the window starts at the upper left corner of the screen */
148    glutInitWindowPosition(0, 0);
149
150    /* Open a window */
151    window = glutCreateWindow("我的第一个长方形进度条demo");
152
153    /* Register the function to do all our OpenGL drawing. */
154    glutDisplayFunc(&DrawGLScene);
155
156    // 全屏
157    /* Go fullscreen.  This is the soonest we could possibly go fullscreen. */
158    //glutFullScreen();
159
160    /* Even if there are no events, redraw our gl scene. */
161    glutIdleFunc(&DrawGLScene);
162
163    /* Register the function called when our window is resized. */
164    glutReshapeFunc(&ReSizeGLScene);
165
166    /* Register the function called when the keyboard is pressed. */
167    glutKeyboardFunc(&keyPressed);
168
169    /* Register the function called when special keys (arrows, page down, etc) are pressed. */
170    glutSpecialFunc(&specialKeyPressed);
171
172    /* Initialize our window. */
173    InitGL(640, 480);
174
175    // 尝试在glutMainLoop之外建立tcp连接
176    tcp_server_init(server_sockfd, conn);
177
178    /* Start Event Processing Engine */
179    glutMainLoop();
180
181    return 1;
182}

方法二,在空闲回调函数中接受消息

使用空闲回调函数glutIdleFunc。参考博客【Idle回调函数的使用】(原出处未知),以及一篇很有用的博客【OpenGL下图形的交互控制[转]】

在nehe的所有教程中,图像的转变均是在DrawGLScene实现的,这种方法在单纯的图像变换,不存在任何等待的时候,是没有问题的。但是一旦需要等待的时候,就会出现之前截图中那样,图片绘制上出现问题,画面显示会很不流畅,我刚开始接触的时候也以为是自己的代码写的有问题。为了解决这个问题,我们可以试试其他的方法。

在博客中我们也知道,一般更新场景数据的时候,使用的就是Idle Callback。刚好符合我们的需求。下面我将说明一下代码的编写。

首先在main函数中已经定义好的部分回调函数后面加上一行空闲回调函数。

1    //tcp_server_init(server_sockfd, conn);
2    glutIdleFunc(&IdleFun);  // idle 回调函数

有趣的是,我们发现,DrawGLScene方法也是在空闲回调函数中执行的。不过在main函数中出现两个DrawGLScene函数的时候,对程序的执行并不影响。

接下来我们定义IdleFun

 1void IdleFun() { // 回调函数,在控制台中的一些操作,需要在本部分进行控制
 2    //printf("test\n");
 3    if(!server_init_flag) {
 4        //square_len++;
 5        //if(square_len >= 100) square_len = 0;
 6        //printf("init\n");
 7        if(tcp_server_init(server_sockfd, conn)) printf("success\n");
 8        else printf("false\n");
 9        server_init_flag = true;
10        glutPostRedisplay();
11    } else {
12        memset(buffer,0,sizeof(buffer));
13        int len = recv(conn, buffer, sizeof(buffer),0);
14        for(int i = 0; i<len; ++i) {
15            if(buffer[0] == 0x66) {
16                square_len++;
17                if(square_len >= 100) square_len = 100;
18            } else if(buffer[0] == 0x64) {
19                square_len--;
20                if(square_len <= 0) square_len = 0;
21            }
22        }
23        fputs(buffer, stdout);
24        send(conn, buffer, len, 0);
25    glutPostRedisplay();
26    }
27}

为了避免反复创建tcp连接,我们使用一个全局bool变量来标志是否已经创建连接。并且我们在刷新完场景数据之后,一定要调用glutPostRedisplay刷新当前屏幕,否则当前屏幕不会自动刷新,你也将看不到场景变化。具体参考可见【[译]GLUT教程 - glutPostRedisplay函数】。(这是一篇很好的博客)最终的参考代码如下。

  1
  2#include <GL/glut.h>    // Header File For The GLUT Library
  3#include <GL/gl.h>  // Header File For The OpenGL32 Library
  4#include <GL/glu.h> // Header File For The GLu32 Library
  5#include <unistd.h>     // Header File For sleeping.
  6#include <stdio.h>
  7#include "tcp_server.h"
  8
  9/* ASCII code for the escape key. */
 10#define ESCAPE 27
 11#define VK_LEFT 37
 12#define VK_RIGHT 39
 13
 14FILE *fp = NULL;//需要注意
 15int square_len;
 16
 17/* TCP 链接  */
 18#define BUFFER_SIZE 1024
 19int server_sockfd, conn;
 20char buffer[BUFFER_SIZE];
 21bool server_init_flag;
 22
 23/* The number of our GLUT window */
 24int window;
 25
 26/* A general OpenGL initialization function.  Sets all of the initial parameters. */
 27void InitGL(int Width, int Height) {        // We call this right after our OpenGL window is created.
 28    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);       // This Will Clear The Background Color To Black
 29    glClearDepth(1.0);              // Enables Clearing Of The Depth Buffer
 30    glDepthFunc(GL_LESS);               // The Type Of Depth Test To Do
 31    glEnable(GL_DEPTH_TEST);            // Enables Depth Testing
 32    glShadeModel(GL_SMOOTH);            // Enables Smooth Color Shading
 33
 34    glMatrixMode(GL_PROJECTION);
 35    glLoadIdentity();               // Reset The Projection Matrix
 36
 37    gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);   // Calculate The Aspect Ratio Of The Window
 38
 39    glMatrixMode(GL_MODELVIEW);
 40
 41    square_len = 0;
 42    server_init_flag = false;
 43    //if(!tcp_server_init(server_sockfd, conn)) exit(1);
 44}
 45
 46/* The function called when our window is resized (which shouldn't happen, because we're fullscreen) */
 47void ReSizeGLScene(int Width, int Height) {
 48    if (Height==0)              // Prevent A Divide By Zero If The Window Is Too Small
 49        Height=1;
 50
 51    glViewport(0, 0, Width, Height);        // Reset The Current Viewport And Perspective Transformation
 52
 53    glMatrixMode(GL_PROJECTION);
 54    glLoadIdentity();
 55
 56    gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);
 57    glMatrixMode(GL_MODELVIEW);
 58}
 59
 60/* The main drawing function. */
 61void DrawGLScene() {
 62    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);     // Clear The Screen And The Depth Buffer
 63    glLoadIdentity();               // Reset The View
 64
 65    glTranslatef(-1.5f,0.0f,-6.0f);             // Move Right 3 Units
 66
 67    // draw a square (quadrilateral)
 68    glBegin(GL_QUADS);              // start drawing a polygon (4 sided)
 69    glVertex3f(-1.0f, 1.0f, 0.0f);      // Top Left
 70    glVertex3f(-1.0f+square_len*0.05, 1.0f, 0.0f);      // Top Right
 71    glVertex3f(-1.0f+square_len*0.05,-1.0f, 0.0f);      // Bottom Right
 72    glVertex3f(-1.0f,-1.0f, 0.0f);      // Bottom Left
 73    glEnd();                    // done with the polygon
 74
 75    // swap buffers to display, since we're double buffered.
 76    glutSwapBuffers();
 77}
 78
 79/* The function called whenever a key is pressed. */
 80void keyPressed(unsigned char key, int x, int y) {
 81    /* avoid thrashing this procedure */
 82    //usleep(100);
 83
 84    /* If escape is pressed, kill everything. */
 85    if (key == ESCAPE) {
 86        /* shut down our window */
 87        glutDestroyWindow(window);
 88
 89        if(!tcp_server_close(server_sockfd, conn)) {
 90            exit(1);
 91        }
 92        /* exit the program...normal termination. */
 93        exit(0);
 94    }
 95}
 96
 97void specialKeyPressed(int key, int x, int y) {
 98    //usleep(100);
 99
100    switch(key) {
101    case GLUT_KEY_LEFT:
102        square_len--;
103        if(square_len <= 0) square_len = 0;
104        break;
105    case GLUT_KEY_RIGHT:
106        square_len++;
107        if(square_len >= 100) square_len = 100;
108        break;
109    }
110}
111
112void IdleFun() { // 回调函数,在控制台中的一些操作,需要在本部分进行控制
113    //printf("test\n");
114    if(!server_init_flag) {
115        //square_len++;
116        //if(square_len >= 100) square_len = 0;
117        //printf("init\n");
118        if(tcp_server_init(server_sockfd, conn)) printf("success\n");
119        else printf("false\n");
120        server_init_flag = true;
121        glutPostRedisplay();
122    } else {
123        memset(buffer,0,sizeof(buffer));
124        int len = recv(conn, buffer, sizeof(buffer),0);
125        for(int i = 0; i<len; ++i) {
126            if(buffer[0] == 0x66) {
127                square_len++;
128                if(square_len >= 100) square_len = 100;
129            } else if(buffer[0] == 0x64) {
130                square_len--;
131                if(square_len <= 0) square_len = 0;
132            }
133        }
134        fputs(buffer, stdout);
135        send(conn, buffer, len, 0);
136    }
137    glutPostRedisplay();
138}
139
140int main(int argc, char **argv) {
141
142    /* Initialize GLUT state - glut will take any command line arguments that pertain to it or
143       X Windows - look at its documentation at http://reality.sgi.com/mjk/spec3/spec3.html */
144    glutInit(&argc, argv);
145
146    /* Select type of Display mode:
147       Double buffer
148       RGBA color
149       Alpha components supported
150       Depth buffer */
151    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH);
152
153    /* get a 640 x 480 window */
154    glutInitWindowSize(640, 480);
155
156    /* the window starts at the upper left corner of the screen */
157    glutInitWindowPosition(0, 0);
158
159    /* Open a window */
160    window = glutCreateWindow("我的第一个长方形进度条demo");
161
162    /* Register the function to do all our OpenGL drawing. */
163    glutDisplayFunc(&DrawGLScene);
164
165    // 全屏
166    /* Go fullscreen.  This is the soonest we could possibly go fullscreen. */
167    //glutFullScreen();
168
169    /* Even if there are no events, redraw our gl scene. */
170    glutIdleFunc(&DrawGLScene);
171
172    /* Register the function called when our window is resized. */
173    glutReshapeFunc(&ReSizeGLScene);
174
175    /* Register the function called when the keyboard is pressed. */
176    glutKeyboardFunc(&keyPressed);
177
178    //tcp_server_init(server_sockfd, conn);
179    glutIdleFunc(&IdleFun);  // idle 回调函数
180
181    /* Register the function called when special keys (arrows, page down, etc) are pressed. */
182    glutSpecialFunc(&specialKeyPressed);
183
184    /* Initialize our window. */
185    InitGL(640, 480);
186
187    /* Start Event Processing Engine */
188    glutMainLoop();
189
190    return 1;
191}

完成这一步之后,基本上你就能够完成一个能够通过网络通信控制窗口界面的小demo了。