文章转自:http://blog.donews.com/atombomb/archive/category/opengl
在上面的例子里,我们已经能在窗口中画出一个完整的3D 图形了,并且可以将它旋转,也知道如何将它平移,和缩放(第四节讲的)。可是这里还有几个问题:
首先是大家可能已经发现,在我们之前提到的所有例子中,在图形的旋转过程中整个图形都有一定程度的闪烁现象,显得图形的过渡极不平滑,这当然不是我们所要的效果,幸好opengl 支持一个称为双缓存的技术,可以有效的帮助我们解决这个问题。我们知道在我们电脑中,屏幕中显示的东西都会被放在一个称为显示缓存的地方,在通常情况下我们只有一个这样的缓冲区,也就是单缓冲,在单缓冲中任何绘图的过程都会被显示在屏幕中,这也就是我们为什么会看到闪烁,而所谓双缓冲就是再这个显示的缓冲区之外再建立一个不显示的缓冲区,我们所有的绘图都将在这个不显示的缓冲区中进行,只有当一帧都绘制完了之后才会被拷贝到真正的现实缓冲区显示出来,这样中间过程对于最终用户就是不可见的了,那即使是速度比较慢也只会出现停顿而不会有闪烁的现象出现。
在OpenGL 中我们可以通过glut 库中的函数
void glutSwapBuffers(void) 来实现从非显示缓冲区到显示缓冲区的复制
当然在使用双缓冲之前我们也要在 glutInitDisplayMode 设定参数的时候选择GLUT_DOUBLE 而不是之前我们用的 GLUT_SINGLE 相信这两个参数的意思也应该很明白了
在解决了闪烁的问题之后我们来解决另一个问题首先我们在窗口中画两个交叉的分别位于ZOX 和 ZOY 平面的长方形并用不同的颜色填充它们,然后让它们旋转很快我们发现有个比较严重的问题发生了,因为我们发现opengl显然没有空间的位置观念因为它根本不能分辨物体的前后关系,当然也不能做出适当的显示,在opengl中我们使用深度缓冲区来解决这个问题,在这个缓冲区中存放着每个象素深度信息,当有一个新的象素需要显示的时候,我们可以通过一个被称为深度测试的函数来确定它们的先后关系,要使用深度缓冲区有以下几个步骤:
1. 在函数 glutInitDisplayMode(mode) 中将GLUT_DEPTH置位表明要使用深度缓冲区
2. 使用函数glEnable(GL_DEPTH_TEST) 打开深度测试函数
void glEnable(GLenum cap);
void glDisable(GLenum cap);
这两个函数属于基本状态管理函数用来打开和关闭一些常用的功能,常用的参数有以下几个
GL_BLEND GL_DEPTH_TEST GL_FOG GL_LINE_STIPPLE GL_LIGHTING
3. 是用函数glDepthFunc()来设置深度测试函数
void glDepthFunc(GLenum func)
这里我们比较常用的深度测试函数有 GL_LESS 和 GL_LEQUAL 两者的区别在于当深度相同时是显示新的象素还是老的象素
4. 使用下面的函数来设置深度缓冲区的值
void glClearDepth(GLclampd depth)
这里的参数是背景的深度一般来说我们都将背景深度设成最大值1
5. 最后在我们使用glClear(bits) 刷新背景的同时我们要将GL_DEPTH_BUFFER_BIT置位表示我们在刷新背景颜色的同时用刷新背景深度
下面我们将看到一个完整的使用了消隐和双缓冲的例子大家可以把其中的相关语句去除看看其中的差别
#define GLUT_DISABLE_ATEXIT_HACK#include <math.h>
#include <gl/glut.h>
#include <gl/gl.h>
bool mouseisdown=false;
bool loopr=false;
int mx=0,my=0;
int ry=50;
int rx=50;
void timer(int p)
{
ry-=5;
glutPostRedisplay();
if (loopr)
glutTimerFunc(200,timer,0);
}
void mouse(int button, int state, int x, int y)
{
if(button == GLUT_LEFT_BUTTON)
{
if(state == GLUT_DOWN)
{ mouseisdown=true; loopr=false;}
else
mouseisdown=false;
}
if (button== GLUT_RIGHT_BUTTON)
if(state == GLUT_DOWN)
{loopr=true; glutTimerFunc(200,timer,0);}
}
void motion(int x, int y)
{
if(mouseisdown==true)
{
ry+=x-mx;
rx+=y-my;
mx=x;
my=y;
glutPostRedisplay();
}
}
void init() //设置OpenGL的一些状态变量的初始值
{
glEnable(GL_DEPTH_TEST); //深度测试
glDepthFunc(GL_LESS); //设置深度测试函数---GL_LESS 和 GL_LEQUAL 两者的区别在于当深度相同时是显示新的象素 还是老的象素
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); //设置多边形显示模式为正反面都是填充显示
glClearColor(1,1,1,1); //设置刷新背景色
glClearDepth(1); //设置清除深度缓冲区所用的值
}
void display()
{
float red[3]={1,0,0};
float green[3]={0,1,0};
float blue[3]={0,0,1};
float yellow[3]={1,1,0};
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //刷新背景颜色+刷新背景深度
glLoadIdentity();
glRotatef(ry,0,1,0);
glRotatef(rx,1,0,0);
glBegin(GL_QUADS);
glColor3fv(blue);
glVertex3f(0.5,0.5,0);
glVertex3f(-0.5,0.5,0);
glVertex3f(-0.5,-0.5,0);
glVertex3f(0.5,-0.5,0);
glEnd();
glBegin(GL_QUADS);
glColor3fv(red);
glVertex3f(0.5,0.5,0.3);
glVertex3f(-0.5,0.5,-0.3);
glVertex3f(-0.5,-0.5,-0.3);
glVertex3f(0.5,-0.5,0.3);
glEnd();
glFlush();
glutSwapBuffers(); //更新窗口
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE| GLUT_RGBA|GLUT_DEPTH); //设置显示模式:单缓冲区, RGBA颜色模式
glutInitWindowSize (400, 400); //设置窗口宽度、高度
glutInitWindowPosition(100,100); //设置窗口位置
glutCreateWindow ("double");
init();
glutDisplayFunc (display); //设置窗口刷新的回调函数
glutMouseFunc(mouse); //设置鼠标器按键回调函数
glutMotionFunc(motion);
glutMainLoop(); //开始主循环
return 0;
}