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%。接着我们将让这个进度条能够对传输过来的信号产生反馈。将整个过程进行拆分,我们可以按照如下步骤进行实现。
- 构建一个OpenGL窗口,能够根据本地按键实现进度条控制
- 让OpenGL的窗口能够接收其他终端发送过来的消息
- 让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成功建立上连接成功,理想中,这个时候应该是没有问题了,但是运行以后,显示依然有问题。
这里我猜测是因为recv阻塞了整个进程,造成后续画笔绘制没办法绘制。因为当我在client中发送一个f之后,server中的窗口就立刻能够正常移动进度条了。
最终的代码如下:
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了。