各位高人指点啊!!!如何绘制cpu内存使用率过高图?请把所调用的函数及函数原型写下。急啊~

2010年12月 C/C++大版内专家分月排行榜第三
本帖子已过去太久远了,不再提供回复功能。博客访问: 27453
博文数量: 65
博客积分: 0
博客等级: 民兵
技术积分: 0
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
原文地址: 作者:
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。同步调用是三者当中最简单的,而回调又常常是异步调用的基础,因此,下面我们着重讨论回调机制在不同软件架构中的实现。
什么是回调
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。同步调用是三者当中最简单的,而回调又常常是异步调用的基础,因此,下面我们着重讨论回调机制在不同软件架构中的实现。
对于不同类型的语言(如结构化语言和对象语言)、平台(Win32、JDK)或构架(CORBA、DCOM、WebService),客户和服务的交互除了同步方式以外,都需要具备一定的异步通知机制,让服务方(或接口提供方)在某些情况下能够主动通知客户,而回调是实现异步的一个最简捷的途径。
对于一般的结构化语言,可以通过回调函数来实现回调。回调函数也是一个函数或过程,不过它是一个由调用方自己实现,供被调用方使用的特殊函数。
在面向对象的语言中,回调则是通过接口或抽象类来实现的,我们把实现这种接口的类成为回调类,回调类的对象成为回调对象。对于象C++或Object Pascal这些兼容了过程特性的对象语言,不仅提供了回调对象、回调方法等特性,也能兼容过程语言的回调函数机制。
Windows平台的消息机制也可以看作是回调的一种应用,我们通过系统提供的接口注册消息处理函数(即回调函数),从而实现接收、处理消息的目的。由于Windows平台的API是用C语言来构建的,我们可以认为它也是回调函数的一个特例。
对于分布式组件代理体系CORBA,异步处理有多种方式,如回调、事件服务、通知服务等。事件服务和通知服务是CORBA用来处理异步消息的标准服务,他们主要负责消息的处理、派发、维护等工作。对一些简单的异步处理过程,我们可以通过回调机制来实现。
下面我们集中比较具有代表性的语言(C、Object Pascal)和架构(CORBA)来分析回调的实现方式、具体作用等。
过程语言中的回调(C)
回调在C语言中是通过函数指针来实现的,通过将回调函数的地址传给被调函数从而实现回调。因此,要实现回调,必须首先定义函数指针,请看下面的例子:
void Func(char *s);// 函数原型void (*pFunc) (char *);//函数指针
可以看出,函数的定义和函数指针的定义非常类似。
一般的化,为了简化函数指针类型的变量定义,提高程序的可读性,我们需要把函数指针类型自定义一下。
typedef void(*pcb)(char *);
回调函数可以象普通函数一样被程序调用,但是只有它被当作参数传递给被调函数时才能称作回调函数。
被调函数的例子:
void GetCallBack(pcb callback){/*do something*/}用户在调用上面的函数时,需要自己实现一个pcb类型的回调函数:void fCallback(char *s) {/* do something */} 然后,就可以直接把fCallback当作一个变量传递给GetCallBack,GetCallBack(fCallback);
如果赋了不同的值给该参数,那么调用者将调用不同地址的函数。赋值可以发生在运行时,这样使你能实现动态绑定。
参数传递规则
到目前为止,我们只讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。许多编译器有几种调用规范。如在Visual C++中,可以在函数类型前加_cdecl,_stdcall或者_pascal来表示其调用规范(默认为_cdecl)。C++ Builder也支持_fastcall调用规范。调用规范影响编译器产生的给定函数名,参数传递的顺序(从右到左或从左到右),堆栈清理责任(调用者或者被调用者)以及参数传递机制(堆栈,CPU寄存器等)。
将调用规范看成是函数类型的一部分是很重要的;不能用不兼容的调用规范将地址赋值给函数指针。例如:
// 被调用函数是以int为参数,以int为返回值__stdcall int callee(int); // 调用函数以函数指针为参数void caller( __cdecl int(*ptr)(int)); // 在p中企图存储被调用函数地址的非法操作__cdecl int(*p)(int) = // 出错
指针p和callee()的类型不兼容,因为它们有不同的调用规范。因此不能将被调用者的地址赋值给指针p,尽管两者有相同的返回值和参数列
C语言的标准库函数中很多地方就采用了回调函数来让用户定制处理过程。如常用的快速排序函数、二分搜索函数等。
快速排序函数原型:
void qsort(void *base, size_t nelem, size_t width, int (_USERENTRY *fcmp)(const void *, const void *));二分搜索函数原型:void *bsearch(const void *key, const void *base, size_t nelem,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
size_t width, int (_USERENTRY *fcmp)(const void *, const void *));
其中fcmp就是一个回调函数的变量。
下面给出一个具体的例子:
#include #include int sort_function( const void *a, const void *b);int list[5] = { 54, 21, 11, 67, 22 };int main(void){&& int& x;&& qsort((void *)list, 5, sizeof(list[0]), sort_function);&& for (x = 0; x < 5; x++)&&&&& printf("%i\n", list[x]);&& return 0;}int sort_function( const void *a, const void *b){&& return *(int*)a-*(int*)b;}
2.4 面向对象语言中的回调(Delphi)
Dephi与C++一样,为了保持与过程语言Pascal的兼容性,它在引入面向对象机制的同时,保留了以前的结构化特性。因此,对回调的实现,也有两种截然不同的模式,一种是结构化的函数回调模式,一种是面向对象的接口模式。
2.4.1 回调函数
回调函数类型定义:
type&& TCalcFunc=function (a:b:integer):
按照回调函数的格式自定义函数的实现,如
function Add(a:b:integer):integerbegin& result:=a+b;function Sub(a:b:integer):integerbegin& result:=a-b;
回调的使用
function Calc(calc:TcalcFa:b:integer):integer
下面,我们就可以在我们的程序里按照需要调用这两个函数了
c:=calc(add,a,b);//c=a+bc:=calc(sub,a,b);//c=a-b
2.4.2 回调对象
什么叫回调对象呢,它具体用在哪些场合?首先,让我们把它与回调函数对比一下,回调函数是一个定义了函数的原型,函数体则交由第三方来实现的一种动态应用模式。要实现一个回调函数,我们必须明确知道几点:该函数需要那些参数,返回什么类型的值。同样,一个回调对象也是一个定义了对象接口,但是没有具体实现的抽象类(即接口)。要实现一个回调对象,我们必须知道:它需要实现哪些方法,每个方法中有哪些参数,该方法需要放回什么值。
因此,在回调对象这种应用模式中,我们会用到接口。接口可以理解成一个定义好了但是没有实现的类,它只能通过继承的方式被别的类实现。Delphi中的接口和COM接口类似,所有的接口都继承与IInterface(等同于IUnknow),并且要实现三个基本的方法QueryInterface, _AddRef, 和_Release。
定义一个接口
type IShape=interface(IInterface)&&&&&&&& procedure Dend
实现回调类
type TRect=class(TObject,IShape)&&&&&&&& protected&&&&& function QueryInterface(const IID: TGUID; out Obj): HR&&&&& function _AddRef: Ifunction _Release: I&&& public&&&&&&&& & procedure Dtype TRound=class(TObject,IShape)&&&&&&&& protected&&&&& function QueryInterface(const IID: TGUID; out Obj): HR&&&&& function _AddRef: Ifunction _Release: I&&& public&&&&&&&& & procedure D
使用回调对象
procedure MyDraw(shape:IShape);var shape:ISbeginshape.D
如果传入的对象为TRect,那么画矩形;如果为TRound,那么就为圆形。用户也可以按照自己的意图来实现IShape接口,画出自己的图形:
MyDraw(Trect.Create);MyDraw(Tround.Create);
2.4.3 回调方法
回调方法(Callback Method)可以看作是回调对象的一部分,Delphi对windows消息的封装就采用了回调方法这个概念。在有些场合,我们不需要按照给定的要求实现整个对象,而只要实现其中的一个方法就可以了,这是我们就会用到回调方法。
回调方法的定义如下:
TNotifyEvent = procedure(Sender: TObject) TMyEvent=procedure(Sender:TEventId:Integer)
TNotifyEvent 是Delphi中最常用的回调方法,窗体、控件的很多事件,如单击事件、关闭事件等都是采用了TnotifyEvent。回调方法的变量一般通过事件属性的方式来定义,如TCustomForm的创建事件的定义:
property OnCreate: TNotifyEvent read FOnCreate write FOnCreate stored IsF
我们通过给事件属性变量赋值就可以定制事件处理器。
用户定义对象(包含回调方法的对象):
type TCallback=Class&&& procedure ClickFunc(sender:TObject);procedure Tcallback.ClickFunc(sender:TObject);begin& showmessage('the caller is clicked!');
窗体对象:
type TCustomFrm=class(TForm)& public&&&&&&&& procedure RegisterClickFunc(cb:procedure(sender:Tobject) of object);procedure TcustomFrm..RegisterClickFunc(cb:TNotifyEvent);begin& self.OnClick=
使用方法:
var& frm:TcustomFbegin& frm:=TcustomFrm.Create(Application);& frm.RegisterClickFunc(Tcallback.Create().ClickFunc);
回调在分布式计算中的应用(CORBA)
回调接口模型
CORBA的消息传递机制有很多种,比如回调接口、事件服务和通知服务等。回调接口的原理很简单,CORBA客户和服务器都具有双重角色,即充当服务器也是客户客户。
回调接口的反向调用与正向调用往往是同时进行的,如果服务端多次调用该回调接口,那么这个回调接口就变成异步接口了。因此,回调接口在CORBA中常常充当事件注册的用途,客户端调用该注册函数时,客户函数就是回调函数,在此后的调用中,由于不需要客户端的主动参与,该函数就是实现了一种异步机制。
从CORBA规范我们知道,一个CORBA接口在服务端和客户端有不同的表现形式,在客户端一般使用桩(Stub)文件,服务端则用到框架(Skeleton)文件,接口的规格采用IDL来定义。而回调函数的引入,使得服务端和客户端都需要实现一定的桩和框架。下面是回调接口的实现模型:
下面给出了一个使用回调的接口文件,服务端需要实现Server接口的框架,客户端需要实现CallBack的框架:
module cb{&&&&&&&& interface CallB&&&&&&&& interface Sinterface CallBack {&&& &&&& void OnEvent(in long Source,in long msg);};& &&&&&& interface Server {&&& &&&& long RegisterCB(in CallBack cb);&&&&&&&&&&&&&&&& void UnRegisterCB(in long hCb);};};
客户端首先通过同步方式调用服务端的接口RegistCB,用来注册回调接口CallBack。服务端收到该请求以后,就会保留该接口引用,如果发生某种事件需要向客户端通知的时候就通过该引用调用客户方的OnEvent函数,以便对方及时处理
做个比喻,不太恰当 我委托你做一件事儿(过程或者对象),并且给你一个电话(回调函数), 当你遇到了某些约定的情况,会用这个电话联系我(触发了回调). --------------------------------------------------------------- 回调函数和普通函数的区别在哪里 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 没区别,就像一把刀,你说它是凶器还是切菜的,关键看他的用途。 EnumChildWindows(hl,@getedit,0); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 这个函数不是单单寻找TEdit的,它能枚举所有窗口类的控件,比如TButton等等,他每次枚举一个都会条用GetEdit这个函数,你在函数里面来判断他是不是你需要的。 如果他枚举出来20个控件,这个&GetEdit&就会被调用20次。 这个GetEdit其实是由EnumChildWindows来调用的。 ------------------------------------------------ 下面是抄来的: 回调函数: 回调函数是这样一种机制:调用者在初始化一个对象(这里的对象是泛指,包括OOP中的对象、全局函数等)时,将一些参数传递给对象,同时将一个调用者可以访问的函数地址传递给该对象。这个函数就是调用者和被调用者之间的一种通知约定,当约定的事件发生时,被调用者(一般会包含一个工作线程)就会按照回调函数地址调用该函数。 --------------------------------------------------------------- 这种方式,调用者在一个线程,被调用者在另一个线程。 在Windows&API中有一些函数使用回调函数,例如CreateThread、SetWindowLong等。对应的回调函数定义为如下形式: function&CallBackFunc(Wnd:&HWND;&Msg,&WParam,&LParam:&Longint):&L procedure&ThreadFunction(Ptr:&Pointer); --------------------------------------------------------------- 消息: 消息也可以看作是某种形式的回调,因为消息也是在初始化时由调用者向被调用者传递一个句柄和一个消息编号,在约定的事件发生时被调用者向调用者发送消息。 这种方式,调用者在主线程中,被调用者在主线程或者工作线程中。 --------------------------------------------------------------- 普通函数是由程序调用的;回调函数是由WINDOWS调用的。我就理解这么多。 --------------------------------------------------------------- 很久以前写的自己写的一个例子,那时也是刚知道回调函数,拿出来供大家参考 function&getedit(HWND:HWND;LPARAM&:lParam): var &&classname&:& begin &&GetMem(classname,100); &&ZeroMemory(classname,100); &&while&hwnd0&do &&begin &&&&GetClassName(hwnd,classname,100); &&&&if&classname='Edit'&then &&&&begin &&&&&&SetWindowText(HWND,pchar('123')); &&&&&&Result&:=& &&&& &&
procedure&TForm1.Button1Click(Sender:&TObject); var &&hl&:&TH begin &&hl&:=&FindWindow(nil,'Form1'); &&if&hl=0&then&exit&else &EnumChildWindows(hl,@getedit,0);
Win32SDKAPISetWindowsHookExHHOOK & SetWindowsHookEx(int & idHook,HOOKPROC & lpfn,HINSTANCE & hMod,DWORD & dwThreadId);DLLSDKAPICallNextHookExTRUE&
我们经常在C++?
APIAPIC++CALLBACKFAR PASCAL
SetWindowsHookExVirtualQueryEx
的地址传给调用者从而实现调用。使用是必要的,在我们想通过一个统一接口实现不同的内容,这时用回掉函数非常合适。比如,我们为几个不同的设备分别写了不同的显示函数:void TVshow(); void ComputerShow(); void NoteBookShow()...void show(void (*ptr)()); 。
&&&&&& c的例子,其中一个不带参数,另一个带参数。
#include #include
int Test1(){&&&& for (i=0; i<30; i++)&& {&&&& printf("The %d th charactor is: %c\n", i, (char)('a' + i%26));&&& }&& return 0;}int Test2(int num){&&&& for (i=0; i< i++)&& {&&& printf("The %d th charactor is: %c\n", i, (char)('a' + i%26));&&& }&& return 0;}
void Caller1(void (*ptr)())//{&& (*ptr)();}void Caller2(int n, int (*ptr)())//,
{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&//void Caller2(int (*ptr)(int n))&& (*ptr)(n);&&}int main(){
&& printf("************************\n");&& Caller1(Test1);&//Test2();&& printf("&&&&&&************************\n");&& Caller2(30, Test2);&//Test2(30);&& return 0;}
&&&&&& 的地址传给调用者从而实现调用,但是需要注意的是带参的用法。要实现回调,必须首先定义函数指针。函数指针的定义这里稍微提一下。比如:
&&&& int (*ptr)(); ptr(*ptr)
CallBackFun.rar
阅读(272) | 评论(0) | 转发(0) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。本文主要整理自/article/p-1704414.html,感谢作者wangdingqiaoit
传统的绘图方法
传统立即模式Immediate Mode绘图,性能低且设置复杂(新版ogl建议丢弃该绘制功能)
每次绘制CPU向GPU传递数据并绘制:
//包含头文件
include &GL/glew.h&
include &GL/freeglut.h&
#pragma comment(lib,&glew32d.lib&)
//函数原型声明
void userInit();//自定义初始化函数
void display(void);//绘制回调函数
void keyboardAction(unsigned char key, int x, int y);//按键回调函数
//入口函数
int main(int argc, char **argv)
glutInit(&argc, argv);//初始化GLUT
glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);//设置显示模式
glutInitWindowPosition(100, 100);//设置窗口起始位置
glutInitize(512, 512);//设置绘图窗口的初始高度和宽
glutCreateWindow(&Triangle demo&);//在屏幕上创建一个窗口
glewInit();//使用GLEW时,使用该函数初始化GLEW
userInit();//自定义的初始化函数
glutDisplayFunc(display);//注册绘制窗口回调函数
glutKeyboardFunc(keyboardAction);//注册键盘事件回调函数
glutMainLoop();//开始事件循环
//自定义初始化函数
void userInit()
glClearColor(0.0, 0.0, 0.0, 0.0);//设置清屏颜色
glColor4f(1.0, 1.0, 0.0, 0.0);//设置绘制颜色
//绘制回调函数
void display(void)
glClear(GL_COLOR_BUFFER_BIT);//清除颜色缓存
glBegin(GL_TRIANGLES);
glVertex3f(-0.5, -0.5, 0.0);
glVertex3f(0.5, 0.0, 0.0);
glVertex3f(0.0, 0.5, 0.0);
glFlush();//强制刷新
//键盘按键回调函数
void keyboardAction(unsigned char key, int x, int y)
switch (key) {
// Escape key
case &#39;q&#39;: case &#39;Q&#39;:
exit(EXIT_SUCCESS);//退出程序
显示列表Display
List绘图,数据一次性写入显存中,绘制时候不能修改
一些2D UI系统中会使用该模式,例如/ios ui绘图,拥有高性能,但是不灵活。
//依赖库glew32.lib freeglut.lib
//使用顶点列表绘制三角形(已过时,仅为学习目的)
include &GL/glew.h&
include &GL/freeglut.h&
#pragma comment(lib,&glew32d.lib&)
void userInit();
void reshape(int w, int h);
void display(void);
void keyboardAction(unsigned char key, int x, int y);
//显示列表句柄
GLuint displayListId;
int main(int argc, char **argv)
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(512, 512);
glutCreateWindow(&Triangle demo&);
glewInit();
userInit();
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutKeyboardFunc(keyboardAction);
glutMainLoop();
//自定义初始化函数
void userInit()
glClearColor(0.0, 0.0, 0.0, 0.0);
glColor4f(1.0, 1.0, 0.0, 0.0);
//创建显示列表,和向显存中写入顶点数据
displayListId = glGenLists(1);
glNewList(displayListId, GL_COMPILE);
glBegin(GL_TRIANGLES);
glVertex3f(-0.5, -0.5, 0.0);
glVertex3f(0.5, 0.0, 0.0);
glVertex3f(0.0, 0.5, 0.0);
glEndList();
//调整窗口大小回调函数
void reshape(int w, int h)
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
//绘制回调函数
void display(void)
glClear(GL_COLOR_BUFFER_BIT);
//利用显示列表,绘制三角形
glPushMatrix();
glTranslatef(-0.5, 0, 0);
glCallList(displayListId); // 绘制一次
glPopMatrix();
glPushMatrix();
glTranslatef(0.5, 0, 0);
glCallList(displayListId); // 绘制二次
glPopMatrix();
glFlush();
//键盘按键回调函数
void keyboardAction(unsigned char key, int x, int y)
switch (key)
// Escape key
exit(EXIT_SUCCESS);
现代的绘图方法
顶点数组,类&#2顶点缓存和索引缓存绘图,每帧还是需要传递数据到xian
这里的VAO还是Vertex Array Object, 的VAO(CPU RAM中的)。
使用顶点数组方式,需要利用glEnableClientState开启一些特性,这里开启顶点数组特性使用glEnableClientState(GL_VERTEX_ARRAY)。
使用顶点数组时,用户定义好顶点的数据,在调用glDrawArrays、glDrawElements之类的函数时,通过glVertexPointer设定的指针,传送数据到GPU。当调用完glDrawArrays后,GPU中已经有了绘图所需数据,用户可以释放数据(其实是一帧结束后,每帧还是要向GPU传递数据的)。
一次draw&call传递数据到显存中,绘制到后台缓存中,只是封装了glBegin/glEnd。
&&glFlush()将后台缓存提交到,gluSwapBuffer翻转交换链提交到显示器
//依赖库glew32.lib freeglut.lib
//使用Vertex Arrays顶点数组绘制三角形(不推荐使用)
include &GL/glew.h&
include &GL/freeglut.h&
comment(lib, &glew32d.lib&)
void userInit();
void reshape(int w, int h);
void display(void);
void keyboardAction(unsigned char key, int x, int y);
//定义一个包含3个float的结构体
//为了保持简单,暂时未引入c++类概念
struct vec3f {
GLfloat x, y,
int main(int argc, char **argv)
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(512, 512);
glutCreateWindow(&Triangle demo&);
glewInit();
userInit();
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutKeyboardFunc(keyboardAction);
glutMainLoop();
//自定义初始化函数
void userInit()
glClearColor(0.0, 0.0, 0.0, 0.0);
glColor4f(1.0, 1.0, 0.0, 0.0);
//调整窗口大小回调函数
void reshape(int w, int h)
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
//绘制回调函数
void display(void)
glClear(GL_COLOR_BUFFER_BIT);
//利用顶点数组,绘制三角形
const int num_indices = 3;
//创建保存顶点的结构体数组
vec3f *vertices = new vec3f[num_indices];
vertices[0].x = -0.5f;
vertices[0].y = -0.5f;
vertices[0].z = 0.0f;
vertices[1].x = 0.5f;
vertices[1].y = 0.0f;
vertices[1].z = 0.0f;
vertices[2].x = 0.0f;
vertices[2].y = 0.5f;
vertices[2].z = 0.0f;
// 启用vertex arrays
glEnableClientState(GL_VERTEX_ARRAY);
//定义顶点数组
glVertexPointer(
// 每个顶点的维度
// 顶点数据类型
// 连续顶点之间的间隙,这里为0
//指向第一个顶点的第一个坐标的指针
// 一次draw call传递数据到显存中,绘制到后台缓存中,只是封装了glBegin/glEnd
glDrawArrays(GL_TRIANGLES, 0, num_indices);
glDisableClientState(GL_VERTEX_ARRAY);
//释放内存空间
// 将后台缓存提交到显示器,gluSwapBuffer翻转交换链提交到显示器
glFlush();
//键盘按键回调函数
void keyboardAction(unsigned char key, int x, int y)
switch (key)
// Escape key
exit(EXIT_SUCCESS);
单纯VBO 形式,渲染状态在客户端,需glEnableClientState和glVertexPointer指定数据
VBO绘制函数(有display list存储在GPU优点,也有顶点数组修改数据的优点, 没用Shader还是在客户端设置和维护渲染状态)
VBO即Vertex Buffer Object,是一个在高速视频卡中的内存缓冲,用来保存顶点数据,也可用于包含诸如归一化向量、纹理和索引等数据。
VBO存储了实际的数据,真正重要的不是它存储了数据,而是他将数据存储在GPU中。这意味着VBO它会很快,因为存在RAM中的数据需要被传送到GPU中,因此这个传送是有代价的。
但是VBO的相关状态,在没有启用Shader情况下,VBO相关状态还是在CPU中设置的。
初始化时,用glBufferData, glBufferDataX拷贝数据到GPU以后,就可以释放掉数据了, Shader中也一样。
单纯的VBO形式,非VAO绘制数据:
//依赖库glew32.lib freeglut.lib
//使用VBO绘制三角形(现代OpenGL方式)
include &GL/glew.h&
include &GL/freeglut.h&
comment(lib, &glew32d.lib&)
void userInit();
void reshape(int w, int h);
void display(void);
void keyboardAction(unsigned char key, int x, int y);
GLuint vboId;
int main(int argc, char **argv)
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(512, 512);
glutCreateWindow(&Triangle demo&);
glewInit();
userInit();
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutKeyboardFunc(keyboardAction);
glutMainLoop();
//自定义初始化函数
void userInit()
glClearColor(0.0, 0.0, 0.0, 0.0);
glColor4f(1.0, 1.0, 0.0, 0.0);
//创建顶点数据
GLfloat *vertices = (GLfloat*)malloc(sizeof(GLfloat) * 9);
//memset(vertices, 0, sizeof(GLfloat)* 9);
GLfloat vertices2[]= {
-0.5, -0.5, 0.0,
0.5, 0.0, 0.0,
0.0, 0.5, 0.0
memcpy(vertices, vertices2, sizeof(GLfloat)* 9);
//分配vbo句柄
glGenBuffersARB(1, &vboId);
//GL_ARRAY_BUFFER_ARB表示作为顶点数组解析
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);
//拷贝数据
glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(GLfloat)*9,
vertices, GL_STATIC_DRAW_ARB);
// 对CPU禁用VBO
glBindBufferARB(GL_VERTEX_ARRAY, 0);
free(vertices); // glBufferData,glBufferDataARB 函数会一次拷贝完数据到GPU中,后面清除即可。
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);//绑定vbo
glEnableClientState(GL_VERTEX_ARRAY);//启用顶点数组属性
glVertexPointer(3, GL_FLOAT, 0, 0);
//调整窗口大小回调函数
void reshape(int w, int h)
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
//绘制回调函数
void display(void)
glClear(GL_COLOR_BUFFER_BIT);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);//绑定vbo
//glEnableClientState(GL_VERTEX_ARRAY);//启用顶点数组属性
// 如何解析vbo中数据, 用VBO传递NULL数据进入,GPU会取到当前激活的VBO中的顶点数据;
// 没有VAO则每帧需要glEnableClientState和glVertexPointer指定,当然绘制一个数据 可以不指定,绘制多个应该有问题。
//glVertexPointer(3, GL_FLOAT, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 3);
//glDisableClientState(GL_VERTEX_ARRAY);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);//解除绑定
glFlush();
//键盘按键回调函数
void keyboardAction(unsigned char key, int x, int y)
switch (key)
// Escape key
exit(EXIT_SUCCESS);
VBO, VAO形式,渲染状态在客户端,需glEnableClientState和glVertexPointer指定数据
这里的VAO还是Vertex Array
Object, 客户端的VAO(CPU RAM中的)。
VAO即Vertex Array Object ,是一个包含一个或多个VBO的对象,被设计用来存储一个完整被渲染对象所需的信息。
VAO代表的是一些描述存储在VBO中对象的属性。VAO可以被视为指向对象的高级内存指针,有点类&#20284;于指针,但比地址多了跟多的跟踪作用。他们很复杂。
VAO很像是辅助对象,而不是实际的数据存储对象。他们记录了当前绘制过程中的属性,描述对象的属性,而不是已经存储在VBO中原始数据。
VAO并不与VBO直接相关,进过初看起来如此。VAOs节省了设置程序所需的状态的时间。如果没有VAO,你需要调用一堆类&#20284;gl*之类的命令(例如glVertexPointer 在绘制时候调用,设置时候真正关联VAO和VBO还是用glVertexPointer等函数的)。
VAO只是存储了顶点数组对象集合,每个顶点数组对象关联VBO,和关联绘制函数绘制状态,需要切换绘制物体,直接一句glBindVertexArray(VAO[i]);切换激活的顶点数组对象即可。
VBO将真实的顶点数据存储在GPU显卡中,提供绘图数据传递性能,绘制时候还是要用glDrawElements等绘图函数,这个glDrawElements在VBO下没有将数据从CPU传递到GPU,但是驱动了GPU中的顶点数据,进行渲染管道的转换和光照计算,像素融合计算,进行stencil
depth test, 抖动融合逻辑操作等,提交到当前帧中的后台颜色缓存中,所以在VBO下也要靠减少Draw call来提高图形性能的。
//依赖库glew32.lib freeglut.lib
//使用VBO绘制三角形(现代OpenGL方式)
include &GL/glew.h&
include &GL/freeglut.h&
comment(lib, &glew32d.lib&)
void userInit();
void reshape(int w, int h);
void display(void);
void keyboardAction(unsigned char key, int x, int y);
GLuint vboId[2];
GLuint vaoId[2];
int main(int argc, char **argv)
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize();
glutCreateWindow(&Triangle demo&);
glewInit();
userInit();
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutKeyboardFunc(keyboardAction);
glutMainLoop();
//自定义初始化函数
void userInit()
glClearColor(0.0, 0.0, 0.0, 0.0);
glGenVertexArrays(2, vaoId);
//分配vbo句柄
glGenBuffersARB(2, vboId);
//创建顶点数据
GLfloat *vertices = (GLfloat*)malloc(sizeof(GLfloat)* 9);
memset(vertices, 0, sizeof(GLfloat)* 9);
glBindVertexArray(vaoId[0]);
//GL_ARRAY_BUFFER_ARB表示作为顶点数组解析
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId[0]);//绑定vbo
glColor4f(1.0, 1.0, 0.0, 0.0);
GLfloat vertices1[] = {
-0.5, -0.5, 0.0,
0.0, 0.0, 0.0,
-0.5, 0.5, 0.0
memcpy(vertices, vertices1, sizeof(GLfloat)* 9);
//拷贝数据
glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(GLfloat)* 9,
vertices, GL_STATIC_DRAW_ARB);
glEnableClientState(GL_VERTEX_ARRAY);//启用顶点数组属性
glVertexPointer(3, GL_FLOAT, 0, 0);
glBindVertexArray(vaoId[1]);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId[1]);
GLfloat vertices2[] = {
0, 0, 0.0,
0.5, -0.5, 0.0,
0.5, 0.5, 0.0
memcpy(vertices, vertices2, sizeof(GLfloat)* 9);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(GLfloat)* 9,
vertices, GL_STATIC_DRAW_ARB);
glEnableClientState(GL_VERTEX_ARRAY);//启用顶点数组属性
glVertexPointer(3, GL_FLOAT, 0, 0);
// 对CPU禁用VBO
glBindBufferARB(GL_VERTEX_ARRAY, 0);
free(vertices); // glBufferData,glBufferDataARB 函数会一次拷贝完数据到GPU中,后面清除即可。
//调整窗口大小回调函数
void reshape(int w, int h)
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
//绘制回调函数
void display(void)
glClear(GL_COLOR_BUFFER_BIT);
for (int i = 0; i & 2; ++i)
float r = 0;
float g = 0;
if (i == 0)
glColor4f(r, g, 0.0, 0.0);
/*glPushMatrix();
glTranslatef(0.5 * i, 0.0f, 0.0f);*/
// vao是关联了VBO的索引结构,通过glBindVertexArray(vaoId);可以切换不同的VBO数据。
glBindVertexArray(vaoId[i]);
glDrawArrays(GL_TRIANGLES, 0, 3);
//glPopMatrix();
glFlush();
//键盘按键回调函数
void keyboardAction(unsigned char key, int x, int y)
switch (key)
// Escape key
exit(EXIT_SUCCESS);
VBO,VAO,Shader形式
VBO,VAO,Shader基本认识
端VAO(绘制索引为简单), VBO(顶点存放),Shader(可管线控制)绘制,每帧绘制只用glDraw
使用VBO,向GPU VBO中传递数据
这里的VAO是:Vertex
Array Object和vertex attribute object
(1)VAO需要glBindVertexArray(vaoId);和glEnableVertexAttribArray(index);&glVertexAttribPointer关联顶点属性数据和Shader输入属性索引,不能用客户端的的glVertexPointer关联VAO和VBO了,因为要考虑Shader的顶点属性的输入。
(2)无论是VBO数据,还是Shader程序都是载入一次到GPU中,后面每帧渲染时候激活和去激活即可。
(3) 数据不改变时候可以封装到Vertex
Array Object中, 实现display时候简单切换;即可以在创建VBO时候,关联好VAO如何解析VBO(也就是glEnableVertexAttribArray(index);&glVertexAttribPointer会根据绘制截断指定的数据,但不能glDisableVertexAttribArray(VBO的关闭却是可以的))的状态和索引集合,绘制时候glBindVertexArray(vaoId)切换绘制状态集合,给glUseProgram(programId)绘制即可。
但当Vertext Shader中的数据是改变的,那么在每帧display中都需要指定:glEnableVertexAttribArray,glVertexAttribPointer,而不能封装到Vertex
Array Object中。
(4)顶点属性是并行输入给顶点Shader的,片元Shader是在光栅化插&#20540;位置后,也是并行输入处理逐个像素的。
(5)一次DrawCall就会进行一次图形渲染流水线过程(无论是固定管线,还是Shader的,顶点无论是VBO 显卡中,还是CPU RAM中的),进行顶点变换,光照计算,三角形背面剔除顶点Shader;硬件透视除法视口转换,光栅化;片元着色,计算纹理,抗锯齿,雾计算片元着色器计算;alpha检测,深度检测,stencil检测; alpha融合,抖动,逻辑操作,载入当前的后台FrameBuffer中。
所以减少DrawCall次数,单次提交的数据量,和启用的渲染状态计算量,才能正真提高GPU渲染的性能。
CPU的性能在CPU端的运算。
内存的压力在于GPU显存的使用,CPU计算的数据量和算法复杂度, CPU计算的频率。
IO的压力在于场景大小数据量的大小,和是否能够预加载和重用数据。
Shader基础
着色器就是运行在GPU上的一个程序而已。在绘制管线中有几个可能的着色器阶段,每个阶段都有它自己的输入输出。着色器的目的是将输入包括潜在的其他类型数据,转换到输出集中。
每个着色器都在输入集上执行。&#20540;得注意的是,在任何阶段,一个着色器都完全独立于那一阶段的其他着色器(最适合并发简单处理,而不是复杂的CPU逻辑)。独立执行的着色器之间不会有交叉。每个输入集的处理从着色器开始到结束阶段。着色器定义了它的输入输出,通常,没有完成输出数据任务的着色器是非法的。
着色器有着输入输出,就好比一个有参数和返回&#20540;的函数一样。
输入和输出来自和转到一些地方去了。因此,输入position&肯定在某处被填充了数据。那么这些数据来自哪里呢?顶点着色器的输入被称为顶点属性(vertex&attributes)。
每个顶点着色器的输入有一个索引位置称作属性索引(attribute&index.),
用于标识从顶点属性(顶点数组)那个位置开始取得顶点属性数据。
Shader 着色器文件和,要经过编译才会编译为Shader Object(着色器对象),一个或多个着色器对象要经过Linker(链接)变成一个Shader
Program Object 也就是GPU可执行汇编指令程序。
所以OGL中使用Shader还需要,CPU对Shader文件进行处理:
shader = glCreateShader(eShaderType);//根据类型创建shader
const&char&*&strFileData&=&strShaderFile.c_str();
glShaderSource(shader,1,&strFileData,NULL);//绑定shader字符串
glCompileShader(shader);//编译shader,得到Shader&Object
GLuint&programId&=&glCreateProgram();//创建program
for(std::vector&GLuint&::size_type&iLoop&=&0;iLoop&&&shaderList.size();iLoop&#43;&#43;)
&&&&&&&&glAttachShader(programId,shaderList[iLoop]);//绑定shader
glLinkProgram(programId);//链接shader&Program&Object,&将Shader程序装载到GPU中。
顶点着色器处理时候,会从glVertexAttribPointer(0)里面取得一个顶点属性,从glVertexAttribPointer(1)里也获得一个顶点属性,对这些顶点属性会进行一次输入输出;像素着色器是在光栅化之后,所以顶点着色器中输出的颜色&#20540;,会是OGL光栅化后片元中对应的插&#20540;后颜色&#20540;,作为像素着色器的输入。
#ifndef _SHADER_H_
#define _SHADER_H_
#include &vector&
#include &string&
#include &cstring&
#include &GL/glew.h&
class Shader {
static GLuint createShader(GLenum eShaderType, const std::string &strShaderFile);
static GLuint createShader(GLenum eShaderType, const char* fileName);
static GLuint createProgram(const std::vector&GLuint& &shaderList);
Shader.cpp
#include &fstream&
#include &sstream&
#include &Shader.h&
//从字符串流构造着色器对象
GLuint Shader::createShader(GLenum eShaderType, const std::string &strShaderFile)
GLuint shader = glCreateShader(eShaderType);//根据类型创建shader
const char * strFileData = http://blog.csdn.net/blues1021/article/details/strShaderFile.c_str();
glShaderSource(shader, 1, &strFileData, NULL);//绑定shader字符串
glCompileShader(shader);//编译shader
//检查shader状态
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
GLint infoLogL
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar *strInfoLog = new GLchar[infoLogLength + 1];
glGetShaderInfoLog(shader, infoLogLength, NULL, strInfoLog);
const char * strShaderType = NULL;
switch (eShaderType)
case GL_VERTEX_SHADER: strShaderType = "vertex";
case GL_GEOMETRY_SHADER: strShaderType = "geometry";
case GL_FRAGMENT_SHADER: strShaderType = "fragment";
fprintf(stderr, "Compile failure in %s shader:/n%s/n", strShaderType, strInfoLog);
delete[] strInfoL
//从文件构造着色器对象
GLuint Shader::createShader(GLenum eShaderType, const char* fileName)
std::ifstream infile(fileName);
if (!infile)
fprintf(stderr, "Could not open file : %s for reading.", fileName);
buffer << infile.rdbuf();
infile.close();
return Shader::createShader(eShaderType, buffer.str());
//构造着色器程序对象
GLuint Shader::createProgram(const std::vector &shaderList)
GLuint programId = glCreateProgram();//创建program
for (std::vector::size_type iLoop = 0; iLoop < shaderList.size(); iLoop++)
glAttachShader(programId, shaderList[iLoop]);//绑定shader
glLinkProgram(programId);//链接shader
//检查program状态
glGetProgramiv(programId, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
GLint infoLogL
glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar *strInfoLog = new GLchar[infoLogLength + 1];
glGetProgramInfoLog(programId, infoLogLength, NULL, strInfoLog);
fprintf(stderr, "Linker failure: %s/n", strInfoLog);
delete[] strInfoL
for (size_t iLoop = 0; iLoop < shaderList.size(); iLoop++)
glDetachShader(programId, shaderList[iLoop]);
return programId;
VAO_VBO_Shader.CPP
////依赖库glew32.lib freeglut.lib
////使用VAO VBO和着色器绘制三角形(现代OpenGL方式)
//#include &string&
//#include &vector&
//#include &GL/glew.h&
//#include &GL/freeglut.h&
//#pragma comment(lib, &glew32d.lib&)
//#include &Shader.h&
//void userInit();
//void reshape(int w, int h);
//void display(void);
//void keyboardAction(unsigned char key, int x, int y);
//GLuint vboId;//vertex buffer object句柄
//GLuint vaoId;//vertext array object句柄
//GLuint programId;//shader program 句柄
//int main(int argc, char **argv)
// glutInit(&argc, argv);
// glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
// glutInitWindowPosition(100, 100);
// glutInitWindowSize(512, 512);
// glutCreateWindow(&Triangle demo&);
// glewInit();
// userInit();
// glutReshapeFunc(reshape);
// glutDisplayFunc(display);
// glutKeyboardFunc(keyboardAction);
// glutMainLoop();
// return 0;
////自定义初始化函数
//void userInit()
// glClearColor(0.0, 0.0, 0.0, 0.0);
// //创建顶点数据
// const GLfloat vertices[] = {
-0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.0f, 0.0f, 1.0f,
0.0f, 0.5f, 0.0f, 1.0f
// //创建vertex array object对象
// glGenVertexArrays(1, &vaoId);
// glBindVertexArray(vaoId);
// //创建vertex buffer object对象
// glGenBuffers(1, &vboId);
// glBindBuffer(GL_ARRAY_BUFFER, vboId);
// glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// //glVertexPointer(4, GL_FLOAT, 0, 0);// 一定要VBO激活的时候才能使用,传递数据
// //glEnableClientState(GL_VERTEX_ARRAY);
// glBindBuffer(GL_ARRAY_BUFFER, 0);
// //创建着色器
// const std::string vertexStr(
&in vec4\n&// layout(location=0) 这个可以不使用,默认应该是该值
&void main()\n&
&{gl_Position =}\n&
// const std::string fragmentStr(
&#version 330\n&
&out vec4 outputC\n&
&void main()\n&
&{outputColor = vec4(1.0f,1.0f,0.0f,1.0f);}\n&
// std::vector&GLuint& idV
// idVector.push_back(Shader::createShader(GL_VERTEX_SHADER, vertexStr)); // 指定Shader文件对应的着色器类型
// idVector.push_back(Shader::createShader(GL_FRAGMENT_SHADER, fragmentStr));
// programId = Shader::createProgram(idVector);
////调整窗口大小回调函数
//void reshape(int w, int h)
// glViewport(0, 0, (GLsizei)w, (GLsizei)h);
////绘制回调函数
//void display(void)
// glClear(GL_COLOR_BUFFER_BIT);
// glUseProgram(programId);
// glBindBuffer(GL_ARRAY_BUFFER, vboId);
// //glBindVertexArray(vaoId);
// // 当然不用VAO的话也可以只用VBO,这里就是glEnableVertexAttribArray(0),glVertexAttribPointer指定
// glEnableVertexAttribArray(0);
// glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
// glDrawArrays(GL_TRIANGLES, 0, 3);
// glBindBuffer(GL_ARRAY_BUFFER, 0);
// glUseProgram(0);
// glDisableVertexAttribArray(0);
// glutSwapBuffers();
////键盘按键回调函数
//void keyboardAction(unsigned char key, int x, int y)
// switch (key)
// case 033:
// Escape key
exit(EXIT_SUCCESS);
//依赖库glew32.lib freeglut.lib
//使用着色器颜色插值绘制三角形
#include &string&
#include &vector&
#include &GL/glew.h&
#include &GL/freeglut.h&
comment(lib, &glew32d.lib&)
#include &shader.h&
void userInit();
void reshape(int w, int h);
void display(void);
void keyboardAction(unsigned char key, int x, int y);
GLuint vboId;//vertex buffer object句柄
GLuint vaoId;//vertext array object句柄
GLuint programId;//shader program 句柄
GLuint offsetLocationId;
int main(int argc, char **argv)
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(512, 512);
glutCreateWindow(&Triangle demo&);
glewInit();
userInit();
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutKeyboardFunc(keyboardAction);
glutMainLoop();
//自定义初始化函数
void userInit()
glClearColor(0.0, 0.0, 0.0, 0.0);
//顶点位置和颜色数据
const GLfloat vertexData[] = {
-0.5f, 0.0f, 0.0f, 1.0f,
0.5f, 0.0f, 0.0f, 1.0f,
0.0f, 0.5f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f
//创建vertex array object对象
glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);
//创建vertex buffer object对象
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
//启用顶点位置属性索引,要在display中指定的,因为需要开启顶点属性索引才能绘制,特别是绘制物体多的时候,需要切换才能正确绘制。
// 也可以封装在VAO中,只负责启用glEnableVertexAttribArray不关闭即可。
glEnableVertexAttribArray(0); // 激活顶点属性数组
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); //指定Position顶点属性数据格式,大小会根据glDrawArrays截断。
//启用顶点颜色属性索引
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void*)48);// 指定Color顶点属性数据格式,大小会根据glDrawArrays截断。
glBindBuffer(GL_ARRAY_BUFFER, 0);
// 这里不能关闭,否则Shader取不到数据
//glDisableVertexAttribArray(0); // 去激活VAO的顶点属性
//glDisableVertexAttribArray(1);
//从文件创建着色器
/*std::vector&GLuint& idV
string strPre = &E:\\OpenGL\\OpenGl7thEdition-master\\OpenGl7thEdition-master\\OpenGL_MyProject\\hello\\data&;
idVector.push_back(Shader::createShader(GL_VERTEX_SHADER, strPre + &\\vertex.glsl&));
idVector.push_back(Shader::createShader(GL_FRAGMENT_SHADER, strPre + &\\fragment.glsl&));
programId = Shader::createProgram(idVector);*/
//从文件创建着色器
std::vector&GLuint& idV
const std::string vertexStr(
&#version 330\n&
&in vec4\n&
&in vec4\n&
&uniform vec2\n&
&smooth out vec4\n&
&void main()\n&
&vec4 totalOffset = vec4(offset.x, offset.y, 0.0, 0.0);\n&
&gl_Position = pos + totalO\n&
&thecolor =}\n&
const std::string fragmentStr(
&#version 330\n&
&smooth in vec4\n&
&out vec4 outputC\n&
&void main()\n&
&{outputColor =}\n&
idVector.push_back(Shader::createShader(GL_VERTEX_SHADER, vertexStr));// &data\\vertex.glsl&));
idVector.push_back(Shader::createShader(GL_FRAGMENT_SHADER, fragmentStr));// &data\\fragment.glsl&));
programId = Shader::createProgram(idVector);
offsetLocationId = glGetUniformLocation(programId, &offset&);
//int nStereoSupport = 0;
//glGetIntegerv(GL_STEREO, &nStereoSupport); // Win7 OGL 3.1不支持
//int nDoubleFrameBufferSupport = 0;
//glGetIntegerv(GL_DOUBLEBUFFER, &nDoubleFrameBufferSupport);// Win7 OGL 3.1支持
//int nAluColorBuffer = 0;
//glGetIntegerv(GL_AUX_BUFFERS, &nAluColorBuffer);// Win7 OGL 3.1不支持,只有0个颜色辅助缓存
//调整窗口大小回调函数
void reshape(int w, int h)
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
//根据时间计算偏移量
void ComputePositionOffsets(GLfloat &fXOffset, GLfloat &fYOffset)
const GLfloat fLoopDuration = 5.0f;
const GLfloat fScale = 3.14159f * 2.0f / fLoopD
GLfloat fElapsedTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;
GLfloat fCurrTimeThroughLoop = fmodf(fElapsedTime, fLoopDuration);
fXOffset = cosf(fCurrTimeThroughLoop * fScale) * 0.5f;
fYOffset = sinf(fCurrTimeThroughLoop * fScale) * 0.5f;
//绘制回调函数
//void display(void)
// glClear(GL_COLOR_BUFFER_BIT);
// // 绑定到VAO状态,也就是封装了通过VAO 的glEnableVertexAttribArray,glVertexAttribPointer关联起来可以解释的VBO数据作为输入
// // 这样通过VAO的切换,就可以在轻松的切换VBO数据源,且正确的解释VBO数据源作为Shader的输入,能够方便的进行绘制切换。
// glBindVertexArray(vaoId);
// glUseProgram(programId);// 启用GPU中的Shader机器码程序
// GLfloat fXOffset = 0.0f, fYOffset = 0.0f;
// ComputePositionOffsets(fXOffset, fYOffset);
// glUniform2f(offsetLocationId, fXOffset, fYOffset);//偏移量发送到顶点着色器
// //绘制三角形,用glDrawElemenets不能正确绘制,因为这里需要连续的
// glDrawArrays(GL_TRIANGLES, 0, 3);
// glUseProgram(0);
// // 关闭GL_ARRAY_BUFFER,glDisableVertexAttribArray,也是可以正确绘制的,
// // 说明glBindVertexArray(vaoId)是正确封装了需要关联了启用状态和索引关系的集合,直接glBindVertexArray切换绘制即可。
// //glBindBuffer(GL_ARRAY_BUFFER, 0); // 去激活GPU中的该VBO
// //glDisableVertexAttribArray(0); // 去激活VAO的顶点属性
// //glDisableVertexAttribArray(1);
// glutSwapBuffers();
//绘制回调函数
void display(void)
glClear(GL_COLOR_BUFFER_BIT);
//计算偏移量
GLfloat fXOffset = 0.0f, fYOffset = 0.0f;
ComputePositionOffsets(fXOffset, fYOffset);
glUseProgram(programId);
glUniform2f(offsetLocationId, fXOffset, fYOffset);//偏移量发送到顶点着色器
glBindBuffer(GL_ARRAY_BUFFER, vboId);
//启用顶点位置属性索引
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
//绘制三角形
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(0);
glDisableVertexAttribArray(0);
glutSwapBuffers();
glutPostRedisplay();//不断刷新
//键盘按键回调函数
void keyboardAction(unsigned char key, int x, int y)
switch (key)
// Escape key
exit(EXIT_SUCCESS);
VBO中数据的更新
更新的时候,顶点属性索引必须在display中设置:
glEnableVertexAttribArray(0);
glVertexAttribPointer(0,&4,&GL_FLOAT,&GL_FALSE,&0,&0);
在CPU中计算顶点数据并上传到缓存对象中&
在CPU中计算顶点的偏移量并传递给着色器,让它来计算顶点数据&
仅通过CPU提供基本参数,让着色器完成更多的任务
更新VBO中的数据,可以CPU计算结果传递给Shader, 也可以传递基本的参数给GPU,让GPU端在Shader中计算结果,具体看具体场景和数据量。
更新操作:
1)全部在CPU中,&&glBufferSubData只拷贝不申请内存:
&glBindBuffer(GL_ARRAY_BUFFER,vboId);
&glBufferSubData(GL_ARRAY_BUFFER,0,sizeof(vertices),&vertexList[0]);
&glBindBuffer(GL_ARRAY_BUFFER,0);
2)CPU和GPU中的结合,结合时候有主要计算分配在CPU中,还是GPU中:
shader中:
uniform&vec2&
cpp中init:
GLuint&offsetLocationId&=&glGetUniformLocation(programId,&&offset&);
cpp中display:
glUseProgram(programId);
glUniform2f(offsetLocationId,&fXOffset,&fYOffset);
glBindBuffer(GL_ARRAY_BUFFER,&vboId);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0,&4,&GL_FLOAT,&GL_FALSE,&0,&0);
具体的更新见:
/article/p-1704418.html
OGL绘图方法总结
使用立即模式的缺点很明显,数据量大一点的话,代码量增加,而且数据发送到需要开销;使用显示列表,显示列表是一个服务端函数,因此它免除了传送数据的额外开销。但是,显示列表一旦编译后,其中的数据无法修改。使用顶点数组,可以减少函数调用和共享顶点数据的冗余。但是,使用顶点数组时,顶点数组相关函数是在客户端,因此数组中数据在每次被解引用时必须重新发送到服务端,额外开销不可忽视。使用VBO在服务端创建缓存对象,并且提供了访问函数来解引用数组;例如在顶点数组中使用的函数如 glVertexPointer(), glNormalPointer(), glTexCoordPointer()。同时,VBO内存管理会根据用户提示,&target&& 和&usage&模式,将缓存对象放在最佳地方。因此内存管理会通过在系统内存、AGP内存和视频卡内存(,
AGP and video memory)这3中内存见平衡来优化缓存。另外,不像显示列表,VBO中数据可以通过映射到客户端内存空间而被用户读取和更新。VBO的另外一个优势是它像显示列表和纹理一样,能和多个客户端共享缓存对象。可见使用VBO优势很明显。而VAO是方便绘制时候的切换,减少绘制设置函数。Shader是可编程管线的控制,绘图,且将顶点和着色运算都灵活的在GPU上控制,所以可以得到非常丰富的效果,和自行优化提高性能。因此VBO,结合VAO和Shader的绘制方式是当前最优秀的。

我要回帖

更多关于 内存使用率过高 的文章

 

随机推荐