前言

在openGL中渲染场景,经常用到单缓冲、双缓冲技术,提高或者降低FPS,以达到某种特效。比方说:

场景1:场景中有一团烟雾,在微风的情况下,烟雾袅袅升起,随风慢慢飘摇

场景2:场景中有一团烟雾,在大风的情况下,烟雾喷涌而出,随风瞬间飘散

如果这两种情况都用同样的FPS,效果就是一样的,无法区分。所有我们需要控制FPS

详解

1.现象

GLUT_SINGLE  指定单缓存窗口

GLUT_DOUBLE  指定双缓存窗口

应用程序使用单缓冲绘图时可能会存在图像闪烁的问题。 这是因为生成的图像不是一下子被绘制出来的,而是按照从左到右,由上而下逐像素地绘制而成的。最终图像不是在瞬间显示给用户,而是通过一步一步生成的,这会导致渲染的结果很不真实。为了规避这些问题,我们应用双缓冲渲染窗口应用程序。缓冲保存着最终输出的图像,它会在屏幕上显示;而所有的的渲染指令都会在缓冲上绘制。当所有的渲染指令执行完毕后,我们交换(Swap)前缓冲和后缓冲,这样图像就立即呈显出来,之前提到的不真实感就消除了。

2、原理

GLUT_SINGLE单缓冲,屏幕显示调用glFlush(),将图像在当前显示缓存中直接渲染,会有图形跳动(闪烁)问题

GLUT_DOUBLE双缓冲,屏幕显示调用glutSwapBuffers(),将图像先绘制在另外的缓存中,渲染完毕之后,将其整个缓存贴到当前的窗口,能消除闪烁,一般动画要用双缓冲.

如果缓冲与函数不对应的话,则会出错。

所谓双缓冲技术,是指两个缓冲区:前台缓冲和后台缓冲。前台缓冲即我们看到的屏幕,

后台缓冲则在内存当中,对我们来说是不可见的。每次我们绘图都在后台缓冲中进行的,

当绘图完成时,就必须把绘制的最终结果复制到屏幕上。在opengl中glutSwapBuffers函数就

可以实现双缓冲技术的一个重要函数。该函数的作用就是交换两个缓冲区的指针,从而把绘制

结果图复制到屏幕上,从而使用户可见。否则在后台缓冲中,使得绘图结果不可见。

那么在程序中怎么使用双缓冲呢?

一般在main函数中开启双缓冲,主要是调用glutInitDisplayMode函数,该函数的功能是设

置初始显示模式。函数原型:void glutInitDisplayMode(unsigned int mode);里面的

mode可以取一下值或其组合:


其中里面就有一个双缓存窗口。

开启双缓冲之后就要用函数glutSwapBuffers()函数以及来交换两个缓冲区

指针。此函数一般用于绘制操作完成后。在main函数中用glutDisplayFunc

函数注册一个绘图函数就可以调用绘图函数,从而就可以调用双缓冲交换函数。

编码

使用glutSwapBuffers()函数可以实现双缓冲效果,但是并不能控制FPS,依然不能满足我们的需求,这时就需要用到glutPostRedisplay();和glutIdleFunc(idelFunc);以及glutTimerFunc(16, OnTimer, 1);

先看下烟雾效果

//添加定时器glutTimerFunc(16, OnTimer, 1);glutIdleFunc(idelFunc);glutSwapBuffers();  // 交换缓存 glutPostRedisplay();

主要代码

// SmokeSimulate.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
// 包含有关OpenGL函数的头文件
//#include "GL/GL.H"
//#include "GL/GLU.h"
//#include "GL/GLAUX.H"
//#include "GL/glut.h"
//#include <iostream>//#include <iostream>
//#include <Windows.h>
#include "tools.h"
#include "Grids.h"
#include "3dmap.h"
#include "xFreeTypeLib.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"#define SMOKEX 32
#define SMOKEY 32typedef struct {float r;float g;float b;
} COL;GLuint mainWindow = 0;
GLuint alpha = 0;
GLuint nearPlane = -1000;
GLuint farPlane = 1920;float deltaTime = 0.0f;
float lastFrame = 0.0f;
const float fps = 60.0f;
float msPerFrame = 1.0f / fps;/
bool RenderScene();
int InitGL(GLvoid);
void Fuoco(void);
void ShowSmoke(float x, float y, float z, float dim);
COL Colore(float k);
void OnTimer(int value);
void idelFunc();    //空闲函数bool    freeze;
int     frame;
GLuint  Texture[1];
unsigned char Bsmoke[SMOKEX][SMOKEY];int xFar = 0.0f, yFar = 0.0f, zFar = 0.0f;
int wWidth = 1366, wHeight = 768;
int oldX, oldY;
bool gIsStartTrackBall = false;
bool gIsMoveMap = false;
TrackBall trackball;
_3dMap map;xFreeTypeLib g_FreeTypeLib;
float ratio;
//wchar_t g_UnicodeString[]=L"aaabb/x4E2D/x6587/x0031/x0032/x0033";
const char g_UnicodeString[] = "0     1      2      3     4      5     6  时间(天)\" ";
const char g_UnicodeStringScript[] = "-10   0     10      20      30     40      50  \" ";
const char g_UnicodeStringScriptHz[] = "0       1000        2000         3000       4000        5000  频率(MHz)\" ";
const char g_UnicodeStringScriptLevel[] = "0    1000    2000    3000   4000   5000  能量电平(Db)\" ";extern stuxCharTexture g_TexID[65536];LPWSTR AnsiToUnicode(LPCSTR lpcstr);
void drawText(wchar_t* _strText, int x, int y, int maxW, int h);void displayEvent()
{//烟雾相关RenderScene();//glFlush();glutSwapBuffers();  // 交换缓存
}void mouseMoveEvent(int x, int y)
{if (gIsStartTrackBall){trackball.MouseMove(x, y);//glutPostRedisplay();glutSetWindow(mainWindow);//glutPostRedisplay();}if (gIsMoveMap){xFar -= oldX - x;yFar += oldY - y;oldX = x;oldY = y;//glutPostRedisplay();}
}void idelFunc()
{float currentFrame = GetTickCount() * 0.1f;deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;//checkCollision();if (deltaTime > msPerFrame){displayEvent();}
}// 鼠标事件函数
void mouseEvent(int button, int state, int x, int y)
{if (state == GLUT_UP && button == GLUT_WHEEL_UP)  //鼠标滚轮{zFar -= 80;//glutPostRedisplay();}if (state == GLUT_UP && button == GLUT_WHEEL_DOWN){zFar += 80;//glutPostRedisplay();}if (button == GLUT_LEFT_BUTTON){if (state == GLUT_DOWN){oldX = x;oldY = y;trackball.setXY(x, y);gIsStartTrackBall = true;}else if (state == GLUT_UP){oldX = x;oldY = y;gIsStartTrackBall = false;}//glutPostRedisplay();}else if (button == GLUT_RIGHT_BUTTON){if (state == GLUT_DOWN){oldX = x;oldY = y;gIsMoveMap = true;}else if (state == GLUT_UP){oldX = x;oldY = y;gIsMoveMap = false;}}
}
// 窗体尺寸变化事件
void resizeEvent(int w, int h)
{/
//添加窗口缩放时的图形变换函数glViewport(0, 0, w, h);/glMatrixMode(GL_PROJECTION);    // 选择投影矩阵glLoadIdentity();              // 设置投影矩阵// 根据窗口的比例设置变换gluPerspective(45.0f, (GLfloat)w / (GLfloat)h, 0.01f, 1000.0f);//glOrtho(0, 0, w, h, nearPlane, farPlane);glMatrixMode(GL_MODELVIEW);        // 选择模型矩阵glLoadIdentity();              // 设置模型矩阵gluLookAt(0.0f, 0.0f, 10.0f, 0.0f, -10.0f, 0.0f, 0.0f, 1.0f, 0.0f);    //为什么没效果//glutPostRedisplay();}
void processSpecialKeys(int key, int x, int y) {if (key == 101){zFar += 4;//glutPostRedisplay();}if (key == 103){//zFar -= 4;//glutPostRedisplay();}printf("key:%d\n", key);
}void MenuFunc(int data)
{switch (data){case 1:map.setLineOrFill(); break;default:break;}//glutPostRedisplay();
}
void glInit()
{glShadeModel(GL_FLAT);//SMOOTH//GL_FLATglClearColor(1.0f, 1.0f, 1.0f, 0.5f);glClearDepth(1.0f);glEnable(GL_NORMALIZE);glEnable(GL_DEPTH_TEST);glAlphaFunc(GL_GREATER, 0);glDepthFunc(GL_LEQUAL);glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);/*glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);*/glEnable(GL_POINT_SMOOTH);glEnable(GL_LINE_SMOOTH);glEnable(GL_POLYGON_SMOOTH);glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); // Make round points, not square pointsglHint(GL_LINE_SMOOTH_HINT, GL_NICEST);  // Antialias the linesglHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);//glClearColor(1.0f,1.0f,1.0f,0.5f);  //窗口背景设置为白色glMatrixMode(GL_MODELVIEW); //设置投影参数glEnable(GL_COLOR_MATERIAL);glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);//g_FreeTypeLib.load("simhei.ttf",14,14);  // g_FreeTypeLib.load("c://windows//fonts//simhei.ttf",14,14);  g_FreeTypeLib.load("c://windows//fonts//simhei.ttf", 12, 12);//烟雾InitGL();//glDisable(GL_CULL_FACE);//glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  }xCharTexture* getTextChar(wchar_t ch)
{g_FreeTypeLib.loadChar(ch);return &g_TexID[ch];
}LPWSTR AnsiToUnicode(LPCSTR lpcstr)   //参数lpcstr类型也可是char*
{LPWSTR Pwstr;int  i;i = MultiByteToWideChar(CP_ACP, 0, lpcstr, -1, NULL, 0);Pwstr = new WCHAR[i];MultiByteToWideChar(CP_ACP, 0, lpcstr, -1, Pwstr, i);return (Pwstr);
}void drawText(wchar_t* _strText, int x, int y, int maxW, int h)
{int sx = x;int sy = y;int maxH = h;size_t nLen = wcslen(_strText);for (int i = 0; i < nLen; i++){if (_strText[i] == '/n'){sx = x; sy += maxH + 12;continue;}xCharTexture* pCharTex = getTextChar(_strText[i]);glBindTexture(GL_TEXTURE_2D, pCharTex->m_texID);                          //绑定到目标纹理  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);glEnable(GL_BLEND);glDepthMask(GL_TRUE);//打开或关闭OpenGL的特殊功能  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);  //特殊的像素算法  //glBlendFunc(GL_ONE, GL_ZERO);glEnable(GL_TEXTURE_2D);    //去透明int w = pCharTex->m_Width;int h = pCharTex->m_Height;int ch_x = sx + pCharTex->m_delta_x;int ch_y = sy - h - pCharTex->m_delta_y;if (maxH < h) maxH = h;glBegin(GL_QUADS);                                                    // 定义一个或一组原始的顶点  {glTexCoord2f(0.0f, 1.0f); glVertex3f(ch_x, ch_y, 1.0f);glTexCoord2f(1.0f, 1.0f); glVertex3f(ch_x + w, ch_y, 1.0f);glTexCoord2f(1.0f, 0.0f); glVertex3f(ch_x + w, ch_y + h, 1.0f);glTexCoord2f(0.0f, 0.0f); glVertex3f(ch_x, ch_y + h, 1.0f);}glEnd();sx += pCharTex->m_adv_x;if (sx > x + maxW){sx = x; sy += maxH + 12;}}
}//
//                      场景绘制与渲染
//
bool RenderScene()
{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glLoadIdentity();frame++;frame = frame % 2;if (frame == 0){if (freeze == false)Fuoco();  // 生成烟雾}glTranslatef(0.1f, 0.0f, -2.7f);ShowSmoke(0, 0, 0, (float)0.2);     // 显示烟雾//::SwapBuffers(m_pDC->GetSafeHdc());     // 交互缓冲区//glFlush();//glutPostRedisplay();//glutSwapBuffers();  // 交换缓存 /*glutSetWindow(mainWindow);glutPostRedisplay();*///glutPostRedisplay();//glFlush();//glutSwapBuffers();  // 交换缓存 return true;
}COL Colore(float k)
{COL c;//   if( k<64 )// {//     c.r=k/64;//        c.g=k/64;//        c.b=k/64;//    }// else//  {if (k < 128){c.r = k / 128;c.g = k / 128;c.b = k / 128;}else{if (k < 192){c.r = k / 256;c.g = k / 256;c.b = k / 256;}else{c.r = 1;c.g = 1;c.b = 1;}}//  }return(c);
}void ShowSmoke(float x, float y, float z, float dim)
{float xi, yi;float ka, kb, kc, kd;COL col;int xd, yd;yi = y + dim * SMOKEY / 2;for (yd = 0; yd < SMOKEY - 1; yd++){xi = x - dim * SMOKEX / 2;for (xd = 1; xd < SMOKEX - 1; xd++){ka = (float)Bsmoke[xd][yd];kb = (float)Bsmoke[xd + 1][yd];kc = (float)Bsmoke[xd + 1][yd + 1];kd = (float)Bsmoke[xd][yd + 1];glBegin(GL_QUADS);     // 绘制四边形col = Colore(kd);glColor3f(col.r, col.g, col.b);glVertex3f(xi, yi, z);col = Colore(kc);glColor3f(col.r, col.g, col.b);glVertex3f(xi + dim, yi, z);col = Colore(kb);glColor3f(col.r, col.g, col.b);glVertex3f(xi + dim, yi + dim, z);col = Colore(ka);glColor3f(col.r, col.g, col.b);glVertex3f(xi, yi + dim, z);glEnd();xi += dim;}yi -= dim;}
}void Fuoco(void)
{int x, y;int k;for (x = 8; x < SMOKEX - 8; x++)Bsmoke[x][SMOKEY - 1] = rand() % 192;for (x = 0; x < 5; x++)Bsmoke[rand() % SMOKEX][SMOKEY - 1] = 0;for (y = 0; y < SMOKEY - 1; y++){for (x = 1; x < SMOKEX - 1; x++){k = Bsmoke[x][y] + Bsmoke[x - 1][y + 1] + Bsmoke[x + 1][y + 1] + Bsmoke[x][y + 1];k = k / 4 - 2;if (k < 0)k = 0;Bsmoke[x][y] = (unsigned char)k;}}
}int InitGL(GLvoid)
{glShadeModel(GL_SMOOTH);glClearColor(0.0f, 0.0f, 0.0f, 0.5f);glClearDepth(1.0f);glEnable(GL_DEPTH_TEST);glDepthFunc(GL_LEQUAL);glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);for (int y = 0; y < SMOKEY; y++){for (int x = 0; x < SMOKEX; x++)Bsmoke[x][y] = 0;}freeze = false;frame = 0;return TRUE;
}void OnTimer(int value)
{/*alpha++;alpha = (alpha % 256);*///displayEvent();glutPostRedisplay();glutTimerFunc(16, OnTimer, 1);
}int main(int argc, char* argv[])
{//ANSI字符串,内容长度7字节     char sz[20] = "中文123";//UNICODE字符串,内容长度5个wchar_t(10字节)     wchar_t   wsz[100] = L"/x4E2D/x6587/x0031/x0032/x0033";//运行时设定当前ANSI编码,VC格式     setlocale(LC_ALL, ".936");//GCC中格式     setlocale(LC_ALL, "zh_CN.GBK");//VisualC++中使用小写%s,按照setlocale指定编码输出到文件     //GCC中使用大写%S     //把UNICODE字符串按照setlocale指定的编码转换成字节     wcstombs(sz, wsz, 20);//把字节串按照setlocale指定的编码转换成UNICODE字符串     mbstowcs(wsz, sz, 20);map.initMap();glutInit(&argc, argv);                                             //初始化GLUTglutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_MULTISAMPLE);  //设置显示模式glutInitWindowPosition(0, 0);  //设置显示窗口的左上角位置glutInitWindowSize(wWidth, wHeight);         //设置窗口的长和高mainWindow = glutCreateWindow("3DMap");     //创造显示窗口glInit();                                       //开始初始化过程int main_version, sub_version, release_version;const char* version = (const char*)glGetString(GL_VERSION);sscanf(version, "%d.%d.%d", &main_version, &sub_version, &release_version);printf("OpenGL 版本:%s\n", version);printf("主版本号:%d\n", main_version);printf("次版本号:%d\n", sub_version);printf("发行版本号:%d\n", release_version);glutReshapeFunc(resizeEvent);   //当窗口尺寸改变时,图形比例不发生变化glutDisplayFunc(displayEvent);glutIdleFunc(idelFunc);glutMouseFunc(mouseEvent);glutSpecialFunc(processSpecialKeys);glutMotionFunc(mouseMoveEvent);//添加定时器glutTimerFunc(16, OnTimer, 1);glutCreateMenu(MenuFunc);glutAddMenuEntry("填充/网格", 1);glutAttachMenu(GLUT_MIDDLE_BUTTON);glutMainLoop();    //显示所有并等候getchar();return 0;
}

工程源码下载地址

openGL控制FPS (每秒传输的帧数)相关推荐

  1. 神经网络学习小记录72——Parameters参数量、FLOPs浮点运算次数、FPS每秒传输帧数等计算量衡量指标解析

    神经网络学习小记录72--Parameters参数量.FLOPs浮点运算次数.FPS每秒传输帧数等计算量衡量指标解析 学习前言 网络的运算时组成 我们要关注网络的什么指标 1.Parameters参数 ...

  2. 60帧/秒摄像头 视频帧数最佳选择!

    随着网络的普及,作为电脑外设产品的摄像头也迅速进入千家万户.这一重大商机也给摄像头行业的发展带来一片繁荣景象.在这个进入门槛低.公模横行的行业,摄像头产品在外观设计.用户应用范围.新功能技术指标等方面 ...

  3. fps 每秒刷新的频率

    (1)FPS是指画面的帧数 FPS指画面每秒传输帧数,通俗来讲就是指游戏和动画或视频的画面数,每秒帧数愈多,所显示的动作就会愈流畅,这个值是越高越好,因为越高代表了你每秒帧数就越多,帧数越多,每秒所刷 ...

  4. [译]GLUT教程 - 每秒帧数

    Lighthouse3d.com >> GLUT Tutorial >> Extras >> Frames per Second 你的程序实际上跑得多快? 有时我们 ...

  5. Unity中帧数FPS的显示查看

    Unity中帧数FPS的显示查看 显示帧数 开发测试时的Game视图 开发测试时的Profiler视图 发布后的FPS显示 显示帧数 FPS可以用3中方式查看: 开发测试时的Game视图 如下图显示的 ...

  6. 安卓流畅度测试方法二:FPS Meter测试安卓帧数

    http://pcedu.pconline.com.cn/508/5084799_1.html http://pcedu.pconline.com.cn/508/5084799_1.html http ...

  7. 画质、码率、帧数、分辨率、体积的基础编码知识

    转自:http://support.shangzhibo.tv/hc/kb/article/1028655/ 很多新手对这方面的概念都比较模糊,这是我在网上总结来的有关画质.码率.帧数.分辨率.体积的 ...

  8. 实时帧数手机_过度营销还是真实刚需?三分钟带你了解手机高刷新率的那些事...

    关注机锋网,有趣的科技媒体 对于很多电竞玩家而言,高刷屏幕早已经不是什么新鲜设备 随着前两年"吃鸡"的大火,也间接带动了一波电竞设备的火爆,其中高刷新率屏幕首当其冲,成为了不少高玩 ...

  9. FPS (每秒传输帧数(Frames Per Second))

    FPS是图像领域中的定义,是指画面每秒传输帧数,通俗来讲就是指动画或视频的画面数.FPS是测量用于保存.显示动态视频的信息数量.每秒钟帧数越多,所显示的动作就会越流畅.通常,要避免动作不流畅的最低是3 ...

最新文章

  1. Java清空数组的数据
  2. iptables自定义链增加和删除
  3. spring整合junit问题分析
  4. 使用 NVM 管理不同的 Node.js 版本
  5. 企业应用程序开发框架的分类
  6. 超图Cesium鼠标事件处理
  7. postman 字符串中有冒号_【接口测试】Postman入门09 Postman获取HTTP请求
  8. 2016年2月23日----Javascript运算符
  9. 是否进行“ git导出”(如“ svn导出”)?
  10. java.lang.NoSuchMethodError示例
  11. 我购买了一台acer笔记本
  12. PMP_考前冲刺题(2022)(3A通过分享)(180题附答案及解析)
  13. UDP socket编程: C++发送 | C#接收
  14. QuartusII下载程序报错,无法正常下载
  15. linux源码编译ipk,OpenWrt-SDK-编译生成ipk软件包
  16. qcom usb驱动下载_艾肯Mobile Q驱动-艾肯Mobile Q usb外置声卡驱动下载v1.35.20 官方最新版-西西软件下载...
  17. JavaScript笔记 Object对象
  18. 萌新小白的HTML第一天学习
  19. 在线观看视频--使用代码倍速播放
  20. rs485接口上下拉_详解RS-485上下拉电阻的选择

热门文章

  1. Kubernetes学习二:资源管理及入门实战
  2. Button边框线隐藏
  3. 【报告分享】2021全球自由行报告-中国旅游研究院马蜂窝(附下载)
  4. 2011 我们的七夕
  5. spss-鸢尾花观测数据
  6. Sublime Text 3 的插件安装(完美解决插件安装出错的问题)及常用插件推荐
  7. RationalDMIS 2020旋转坐标系
  8. 图像生成论文阅读:GLIDE算法笔记
  9. 快递查询单号查询,对物流进行分析
  10. 【python】批量获取企业公司的统一社会代码