首页 > 代码库 > OpenGL学习03_双缓冲DoubleBuffering

OpenGL学习03_双缓冲DoubleBuffering

双缓冲的是什么?

我们看电视时,看到的屏幕称为OSD层,也就是说,只有在OSD层上显示图像我们才能看到。现在,我需要创建一个虚拟的、看不见但是可以在上面画图(比如说画点、线)的OSD层,我称之为offscreen(后台缓冲区)。这个offscreen存在于内存中,我们在上面画图,这个offscreen上面的东西可以显示在OSD层上,需要一个创建这个offscreen的函数,返回这个offscreen的句柄(整型指针)、宽度、高度、指向新建offscreen数据缓冲区的指针,该缓冲区是一个在函数外创建的offscreen的数据缓冲区,大小是offscreen的高度*宽度*每个像素点数据的大小。闪烁是图形编程的一个常见问题。需要多重复杂绘制操作的图形操作会导致呈现的图像闪烁或具有其他不可接受的外观。双缓冲的使用解决这些问题。双缓冲使用内存缓冲区来解决由多重绘制操作造成的闪烁问题。当启用双缓冲时,所有绘制操作首先呈现到内存缓冲区,而不是屏幕上的绘图图面。所有绘制操作完成后,内存缓冲区直接复制到与其关联的绘图图面。因为在屏幕上只执行一个图形操作,所以消除了由复杂绘制操作造成的图像闪烁。

OpenGL中实现双缓冲

OpenGL中并没有直接提供双缓冲的接口,因为并非所有硬件都支持这一技术,并且双缓冲技术的实现是高度依赖于视窗操作系统的,不过GLUT函数库提供了相应的接口,GLUT库解决了平台间差异的问题。

可以通过以下方法来设置窗口的展示模式为双缓冲

glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB);//GLUT_DOUBLE为双缓冲

在需要进行显示变化时交换缓冲区数据

glutSwapBuffers();//交换缓冲区

下面实现了一个矩形的旋转动画,演示了双缓冲的使用。

//
//  main.cpp
//  OpenGL_02_DoubleBuffering
//
//  Created by apple on 14/12/28.
//  Copyright (c) 2014年 cc. All rights reserved.
//

#include <iostream>
//GLUT库,提供一套兼容各种操作系统的OpenGL的图形UI接口
#include <GLUT/GLUT.h>

static GLfloat spin = 0.0f;

/**
 * 初始化操作
 */
void init() {
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    //设置着色模式,GL_FLAT 采用恒定着色,使用图元中某个顶点的颜色来渲染整个图元。
    glShadeModel(GL_FLAT);
}

/**
 *  展示绘制效果
 */
void display() {
    //清理颜色缓冲区
    glClear(GL_COLOR_BUFFER_BIT);
    //将当前矩阵压入栈中保存
    glPushMatrix();
    //设置颜色
    glColor3f(1.0f, 0.0f, 0.0f);
    //旋转操作,绕z轴旋转spin度
    glRotatef(spin, 0.0f, 0.0f, 1.0f);
    //构造一个矩形
    glRectf(-25.0f, -25.0f, 25.0f, 25.0f);
    //还原绘制变换前的矩阵
    glPopMatrix();
    //交换缓冲区,将正在显示的颜色缓冲区和正在绘制的颜色缓冲区数据交换
    glutSwapBuffers();
}

/**
 *  调整窗口尺寸
 *
 *  @param width  宽度
 *  @param height 高度
 */
void reshape(int width, int height) {
    //设置视口矩形区域,在默认情况下,视口被设置为占据打开窗口的整个像素矩形
    glViewport(0, 0, (GLsizei)width, (GLsizei)height);
    //对投影矩阵应用随后的矩阵操作
    glMatrixMode(GL_PROJECTION);
    //等于是将之前矩阵变换导致变化过的栈顶矩阵重新归位,置为单位矩阵!等于是之前的矩阵变换带来的影响到此为止了!
    glLoadIdentity();
    //设置裁剪坐标系
    glOrtho(-50.0f, 50.0f, -50.0f, 50.0f, -1.0f, 1.0f);
    //对模型视图矩阵应用随后的矩阵操作
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/**
 *  播放旋转动画
 */
void spinDisplay() {
    spin += 3.0f;
    if (spin > 360.0f) {
        spin -= 360.0f;
    }
    
    //标记当前窗口需要重新绘制。通过glutMainLoop下一次循环时,窗口显示将被回调以重新显示窗口的正常面板。多次调用glutPostRedisplay,在下一个显示回调只产生单一的重新显示回调。
    glutPostRedisplay();
}

/**
 *  鼠标事件回调
 *
 *  @param button 按钮
 *  @param state  状态
 *  @param x      x
 *  @param y      y
 */
void mouse(int button, int state, int x, int y) {
    switch (button) {
        //按下鼠标左键
        case GLUT_LEFT_BUTTON:
            if (state == GLUT_DOWN) {
                //设置全局的回调函数,当没有窗口事件到达时,GLUT程序功能可以执行后台处理任务或连续动画。如果启用,这个idle function会被不断调用,直到有窗口事件发生。
                //播放旋转动画
                glutIdleFunc(spinDisplay);
            }
            break;
        //按下鼠标右键
        case GLUT_RIGHT_BUTTON:
            if (state == GLUT_DOWN) {
                //停止函数的回调
                //停止旋转动画
                glutIdleFunc(nullptr);
            }
        default:
            break;
    }
}

int main(int argc, const char * argv[]) {
    
    //初始化GLUT库
    glutInit(&argc, (char**)argv);
    //设置双缓冲,RGB像素格式的窗口
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    //设置窗口大小
    glutInitWindowSize(250, 250);
    //创建窗口
    glutCreateWindow("Double Buffer");
    
    //初始化操作
    init();
    
    //设置展示的回调方法
    glutDisplayFunc(display);
    //设置窗口改变时的回调方法
    glutReshapeFunc(reshape);
    //设置鼠标事件回调方法
    glutMouseFunc(mouse);
    //绘制线程开始循环
    glutMainLoop();
    
    return 0;
}
技术分享

OpenGL学习03_双缓冲DoubleBuffering