Ubuntu下使用GLUT编写OpenGL程序
在Linux下以及Windows操作系统下编写OpenGL程序已经变得非常便捷了,我们只需安装freeglut工具包即可。老旧的GLUT已经废弃了,现在如果我们要在一些类Unix系统上编写OpenGL程序的话需要安装FreeGLUT。如果是在Ubuntu上,则输入以下命令即可:
sudo apt-get install freeglut3-dev
现在FreeGLUT最新的稳定版本为3.0.0,因此我们用上述命令即可安装好最新版本的FreeGLUT工具。
接着,我们为了方便编译,可以写一个shell程序,命名为build.sh,如下所示:
clang main.c -std=gnu11 -lglut -lGL -o glutDemo
这里假定我们要编译的C源文件名为main.c。如果各位想用GCC编译的话,只需将上述的clang改写为gcc即可,其他不动。这里我们只需连接libGL与libglut这两个库即可。我们在安装freeglut时,这两个库都会被下载。
下面我们就来看一下main.c源文件内容:
#include <stdio.h>
#include <GL/freeglut.h>
#include <GL/gl.h>
#ifndef var
#define var __auto_type
#endif
static const GLfloat sRectVertices[] = {
// top left
-0.4f, 0.4f,
// bottom left
-0.4f, -0.4f,
// top right
0.4f, 0.4f,
// bottom right
0.4f, -0.4f
};
static const GLfloat sTriangleVertices[] = {
// top center
0.0f, 0.4f,
// bottom left
-0.4f, -0.4f,
// bottom right
0.4f, -0.4f
};
static const GLfloat sColors[] = {
// red
1.0f, 0.0f, 0.0f, 1.0f,
// green
0.0f, 1.0f, 0.0f, 1.0f,
// blue
0.0f, 0.0f, 1.0f, 1.0f,
// white
1.0f, 1.0f, 1.0f, 1.0f
};
static void RenderHandler(void)
{
glClear(GL_COLOR_BUFFER_BIT);
// Draw rectangle
glVertexPointer(2, GL_FLOAT, 0, sRectVertices);
glColorPointer(4, GL_FLOAT, 0, sColors);
glLoadIdentity();
glTranslatef(-0.5f, 0.0f, -2.0f);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Draw triangle
glVertexPointer(2, GL_FLOAT, 0, sTriangleVertices);
glColorPointer(4, GL_FLOAT, 0, sColors);
glLoadIdentity();
glTranslatef(0.5f, 0.0f, -2.1f);
glDrawArrays(GL_TRIANGLES, 0, 3);
glFlush();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE);
glutInitWindowSize(480, 480);
glutInitWindowPosition(200, 100);
glutCreateWindow("OpenGL GLUT Demo");
glutDisplayFunc(RenderHandler);
var vendor = (const char*)glGetString(GL_VENDOR);
var renderer = (const char*)glGetString(GL_RENDERER);
var version = (const char*)glGetString(GL_VERSION);
printf("The vendor is: %s\n", vendor);
printf("The renderer is: %s\n", renderer);
printf("The GL version is: %s\n", version);
glViewport(0, 0, 480, 480);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glShadeModel(GL_SMOOTH);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 3.0f);
glMatrixMode(GL_MODELVIEW);
glutMainLoop();
}
完成之后,我们直接用终端进入main.c与build.sh所在的目录。然后直接执行bash build.sh
进行编译,最后生成glutDemo这一可执行文件。我们如果是在命令行直接执行它,那么还能看到当前系统环境中的OpenGL驱动商以及所使用的渲染器名称。
下面再介绍一下如何在GLUT环境下刷新OpenGL渲染,从而可以使得它以一定的帧率进行绘制物体。
#include <stdio.h>
#include <GL/freeglut.h>
#include <GL/gl.h>
#ifndef var
#define var __auto_type
#endif
static const GLfloat sRectVertices[] = {
// top left
-0.4f, 0.4f,
// bottom left
-0.4f, -0.4f,
// top right
0.4f, 0.4f,
// bottom right
0.4f, -0.4f
};
static const GLfloat sTriangleVertices[] = {
// top center
0.0f, 0.4f,
// bottom left
-0.4f, -0.4f,
// bottom right
0.4f, -0.4f
};
static const GLfloat sColors[] = {
// red
1.0f, 0.0f, 0.0f, 1.0f,
// green
0.0f, 1.0f, 0.0f, 1.0f,
// blue
0.0f, 0.0f, 1.0f, 1.0f,
// white
1.0f, 1.0f, 1.0f, 1.0f
};
static int sRotAngle = 0;
static void TimerHandler(int value);
static void RenderHandler(void)
{
// 以50FPS的帧率进行刷新
glutTimerFunc(20, TimerHandler, 0);
glClear(GL_COLOR_BUFFER_BIT);
// Draw rectangle
glVertexPointer(2, GL_FLOAT, 0, sRectVertices);
glColorPointer(4, GL_FLOAT, 0, sColors);
glLoadIdentity();
glTranslatef(-0.5f, 0.0f, -2.0f);
glRotatef(sRotAngle, 0.0f, 0.0f, 1.0f);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Draw triangle
glVertexPointer(2, GL_FLOAT, 0, sTriangleVertices);
glColorPointer(4, GL_FLOAT, 0, sColors);
glLoadIdentity();
glTranslatef(0.5f, 0.0f, -2.1f);
glRotatef(-sRotAngle, 0.0f, 0.0f, 1.0f);
glDrawArrays(GL_TRIANGLES, 0, 3);
glFlush();
if(++sRotAngle == 360)
sRotAngle = 0;
}
static void TimerHandler(int value)
{
RenderHandler();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE);
glutInitWindowSize(480, 480);
glutInitWindowPosition(200, 100);
glutCreateWindow("OpenGL GLUT Demo");
glutDisplayFunc(RenderHandler);
var vendor = (const char*)glGetString(GL_VENDOR);
var renderer = (const char*)glGetString(GL_RENDERER);
var version = (const char*)glGetString(GL_VERSION);
printf("The vendor is: %s\n", vendor);
printf("The renderer is: %s\n", renderer);
printf("The GL version is: %s\n", version);
glViewport(0, 0, 480, 480);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glShadeModel(GL_SMOOTH);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 3.0f);
glMatrixMode(GL_MODELVIEW);
glutMainLoop();
}
GLUT有一个glutTimerFunc
函数,用于提供定时器任务。其原型为:
void glutTimerFunc(unsigned int msecs, void (*func)(int value), int value);
这里,msecs用于指定定时器触发的时间间隔,单位为毫秒。参数func则是指向我们自己定义的定时器处理函数。参数value是我们传递给回调函数func的参数值,这个值可以用来标识当前定时器回调函数的状态,可自定义其语义。这里各位要注意的是,glutTimerFunc
函数在调用之后只会触发一次定时器,如果我们要不断触发定时器,那么就得将它放在渲染处理函数之中重复调用。
我们运行上述代码之后就能看到一个不断转动的正方形和一个三角形了。不过各位注意到,三角形和正方形在旋转时边缘会出现锯齿。GLUT提供了一种非常便捷的方式来开启OpenGL所实现的多重采样抗锯齿(MSAA)效果。我们只需要在glutInitDisplayMode
函数参数中添加上GLUT_MULTISAMPLE
标志,然后调用glutSetOption
函数来设置样本个数,一般来说用4个样本就足够了。最后使用glEnable(GL_MULTISAMPLE_ARB)
来开启多重采样效果即可。
另外,为了使得渲染效率提升,我们也可以使用双缓存机制。我们在glutInitDisplayMode
函数参数中添加上GLUT_DOUBLE
即可开启双缓存模式。当我们开始渲染一个帧缓存时,调用glutSwapBuffers();
函数,切换到另一个帧缓存,使得下一次渲染直接在另一个帧缓存上进行,这样即便当前渲染的速度稍慢也不会对帧率产生太大影响。当然,使用双缓存的话,OpenGL所要花费的存储空间肯定要增大,这就是所谓的用空间来换时间。下面请看代码:
#include <stdio.h>
#include <GL/freeglut.h>
#include <GL/gl.h>
#ifndef var
#define var __auto_type
#endif
static const GLfloat sRectVertices[] = {
// top left
-0.4f, 0.4f,
// bottom left
-0.4f, -0.4f,
// top right
0.4f, 0.4f,
// bottom right
0.4f, -0.4f
};
static const GLfloat sTriangleVertices[] = {
// top center
0.0f, 0.4f,
// bottom left
-0.4f, -0.4f,
// bottom right
0.4f, -0.4f
};
static const GLfloat sColors[] = {
// red
1.0f, 0.0f, 0.0f, 1.0f,
// green
0.0f, 1.0f, 0.0f, 1.0f,
// blue
0.0f, 0.0f, 1.0f, 1.0f,
// white
1.0f, 1.0f, 1.0f, 1.0f
};
static int sRotAngle = 0;
static void TimerHandler(int value);
static void RenderHandler(void)
{
// 以50FPS的帧率进行刷新
glutTimerFunc(20, TimerHandler, 0);
glClear(GL_COLOR_BUFFER_BIT);
// Draw rectangle
glVertexPointer(2, GL_FLOAT, 0, sRectVertices);
glColorPointer(4, GL_FLOAT, 0, sColors);
glLoadIdentity();
glTranslatef(-0.5f, 0.0f, -2.0f);
glRotatef(sRotAngle, 0.0f, 0.0f, 1.0f);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Draw triangle
glVertexPointer(2, GL_FLOAT, 0, sTriangleVertices);
glColorPointer(4, GL_FLOAT, 0, sColors);
glLoadIdentity();
glTranslatef(0.5f, 0.0f, -2.1f);
glRotatef(-sRotAngle, 0.0f, 0.0f, 1.0f);
glDrawArrays(GL_TRIANGLES, 0, 3);
glFlush();
glutSwapBuffers();
if(++sRotAngle == 360)
sRotAngle = 0;
}
static void TimerHandler(int value)
{
RenderHandler();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_MULTISAMPLE);
glutInitWindowSize(480, 480);
glutInitWindowPosition(200, 100);
glutCreateWindow("OpenGL GLUT Demo");
glutSetOption(GLUT_MULTISAMPLE, 4);
glutDisplayFunc(RenderHandler);
var vendor = (const char*)glGetString(GL_VENDOR);
var renderer = (const char*)glGetString(GL_RENDERER);
var version = (const char*)glGetString(GL_VERSION);
printf("The vendor is: %s\n", vendor);
printf("The renderer is: %s\n", renderer);
printf("The GL version is: %s\n", version);
glViewport(0, 0, 480, 480);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glShadeModel(GL_SMOOTH);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glEnable(GL_MULTISAMPLE_ARB);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 3.0f);
glMatrixMode(GL_MODELVIEW);
glutMainLoop();
}
这样一来,我们就能看到既流畅,又光滑的正方形与三角形了~ ^o^