opengl
opengl简介
gl是一套图形编程API,是图形应用开发的工业标准接口,每个平台都有相应的实现。可视化的操作系统都提供了图形编程接口,通常这一系列的接口作为操作系统的一个系统服务,为客户提供窗口渲染。那为什么还用gl呢?系统提供的这些服务并不是针对gpu进行优化的,通常接口是为2d渲染提供服务的,3d没提供直接支持。还有各个平台的接口不一样,移植很困难。而使用opengl除了移植外,它针对gpu提供了高效的操作,为3d开发提供了原生的支持。gl并不作为系统的一个核心服务,它为众多厂商指定标准,由相应的gpu开发商实现驱动。gl通常作为一个驱动程序安装在系统上面,它同样需要与系统的原生态窗口系统进行交互,把最终渲染数据交由窗口系统。任何系统之上的服务都绕不过系统服务这个坎,而跟系统交互,存在一个上下文切换的问题,系统服务运行在内核态,以提高系统的安全稳健。用户程序中的非系统调用都是运行在用户态,它们的崩溃、出错不会影响系统的不稳定,会被系统关闭。所以这里存在着一个上下文切换的开销。那么使用gl也同样存在上下文切换开销,只不过切换大量减少了。对于直接使用系统的GDI,每次调用导致一个切换显然对于专门的图形应用程序来说花销太大,而对于gl驱动来讲,它部分在用户态执行,可以减少一些开销。gl的一个实现mesa3d。
使用gl就是向它发送数据,以及发送命令,命令有对数据的操作命令,也有渲染命令。一开始的时候,gl没有提供在gpu上进行编程的接口,用户不能利用gpu进行编程,无法对将要被渲染的数据进行处理。只能在数据发送给gl之前进行处理,也就改改顶点位置、颜色、纹理坐标等等,要想改变最终的片元颜色,不得而知了。gl1.5后推出了着色器,让gl大显身手,可以对顶点与片元进行编程。代码书写也发生了变化,需要编写着色器代码。下面分析gl的基本接口,以及gl的缓存功能。gl缓存的目的是为了减小数据传输,这个看似一般的功能,在gl中很重要。同时我会张贴一些接口的实现伪代码,方便理解函数实现,接口调用原理。
一个简单的列子
下面查看如何使用着色器渲染一个四边形,现在写渲染都是要编写着色器,着色器代码并不是一两个接口的问题,涉及的接口有点多,但是着色器的创建形式固定。gl一开始需要跟系统的窗口系统建立联系,对于窗口的建立,表面生成,最终的显示,gl都是没有权利直接去做的,必须调用系统的图像服务去完成。下面使用glut库去完成这个功能,glut为不同平台提供了统一接口,网上也有源码。代码如下:
int main(int argc, const char * argv[]) {
glutInit(&argc, (char **)argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowSize (640, 480);
glutInitWindowPosition (1000, 100);
glutCreateWindow ("graphics with opengl");
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
上面代码很固定,glutInitDisplayMode描述了显示模式,GLUT_DOUBLE表明使用两个表面,其中一个离屏的,这样gl把操作的结果都写入这个离屏的表面,最后更显示表面交换,显示器一下子把数据刷新显示到显示器上。不使用双表面会有个问题,当一帧间隔大于显示器刷新频率时,假如2个hz一次渲染计算,显示器60hz/s,那么渲染一半,过了1hz,这个时候显示器刷新了,还没画完整的图被渲染出来了。再过1hz一帧才被完全计算渲染。所有这里用GLUT_DOUBLE。GLUT_RGBA表示表面4个颜色分类。后面的几个函数是设置窗口的大小、左上角位置、窗口标题。加入要自定义窗口,那么就不能用glut,得自己使用系统的窗口系统api了。glutDisplayFunc的参数函数是窗口接收到重绘消息时,调用的,本人MAC系统建立窗口后发出两次重建命令,你可以在display里打印log看下输出几次。此外每次窗口大小改变也会发生窗口重绘消息,调用display。glutMainLoop是进入消息循环,后面retuen 0等到窗口接到关闭窗口后才会被调用。上面没有加入每隔多少毫秒进行帧刷新,使用glutIdleFunc可以实现空闲时刷新,在里面加个时间判断就OK了。现在窗口建立起来了,还需要在display里面向gl传输数据与命令,让它渲染。
void display(){
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
drawQuad();
glSwapAPPLE();
}
上面代码就是向gl发送一些列的命令,其中drawQuad是客户函数,其它三个gl开头的函数是gl接口。第一个glClearColor设置gl上下文中用于清除几个用于渲染的缓存(合成表面)的颜色为黑色,gl有颜色、深度等等缓存,这些缓存有不同作用,颜色缓存存储片元最终颜色,深度缓存存储每个片元的深度。其中上面代码清除颜色帧缓存为全0,glClear执行清除,glSwapAPPLE交换前后表面。之所以要这样做,因为opengl初始化时产生的这几个缓存并不去赋值,里面的数据不得而知,可能是之前显示的一幅画面,当你给它发送渲染命令后,颜色帧缓存中没被覆盖的像素点,就会显示出来,显然不是你想要的结果。所以这里的操作也是一个定式。drawQuad是用来给gl