第三章我们使用 OpenGL ES 2.0在窗口绘制了一个三角形,但我们使用自己的函数去打开和管理窗口,虽然我们的例子很简单,但是它让OpenGL ES 2.0在你的系统工作时减少你的工作量。
为发展编程上下文环境,提供了平台独立的API叫做EGL,用来管理绘制窗口,EGL提供下面的机制。
- 你使用的系统窗口之间的通讯
- 查询可用的类型,配置绘图窗口
- 创建绘图窗口
- 同步OpenGL ES 2.0渲染和其他绘图API的渲染
- 管理渲染资源像贴图纹理
本章介绍基本创建窗口的要求,还要其他操作,像创建贴图纹理、使用EGL命令的要求等。
窗口系统之间通讯
EGL提供OpenGL ES 2.0和你计算机运行的操作系统之间通讯,例如运行X视窗的GNU/Linux系统、微软系统、苹果的Mac Os系统。EGL在决定绘制什么类型的窗口前,需要打开和操作系统的通讯连接。
每个操作系统有不同的语法,EGL提供一个基本的不透明的类——EGLDisplay——它封装了与操作系统相关的连接。使用EGL第一步是创建并初始化一个使用本操作系统EGL显示的连接。包括两步:
1.初始化EGL
EGLint majorVersion;
EGLint minorVersion;
EGLDisplay display;
display=eglGetDisplay(EGL_DEFAULT_DISPLAY);
if(display==EGL_NO_DISPLAY)
{
//不能打开连接本地窗口系统
}
if(!eglInitialize(display,&majorVersion,&minorVersion))
{
//未能初始化EGL 处理和覆盖
}
打开一个EGL显示服务连接使用:
EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id);
EGLNativeDisplayType定义使用者的操作系统,如果连接没有建立,eglGetDisplay将返回EGL_NO_DISPLAY,这个错误指示EGL不可用,你将不能使用OpenGL ES 2.0.
检查错误
大多数情况下EGL返回EGL_TRUE或者EGL_FALSE,当然可以得到更多的信息,如果返货EGL——FALSE,我们能够查询发生错误的原因,可以使用
EGLint eglGetError();
查询错误码
初始化EGL
当你成功的创建了一个连接,接下来需要初始化:
EGLBoolean eglInitialize(EGLDisplay display,EGLint *majorVersion,EGLint *minorVersion);
初始化EGL内部数据,返回EGL主次版本号。如果EGL不能初始化,它将返回EGL——FALSE,设定错误码如下:
- 如果现实指定的不是合法的EGLDisplay,返回EGL_BAD_DISPLAY
- 如果EGL不能初始化,返回EGL_NOT_INITIAL
设定可用的窗口配置
初始化EGL后,我们需要设定渲染何种类型的窗口和配置信息。这需要两步。
1.查询窗口配置,找到最好的选择。
2.指定要求,让EGL做出最好的匹配。
大多数情况下第二步和第一步是类似的。或者EGL将返回一个EGLConfig结构,它标示了窗口和它特定的信息,颜色位数,深度缓冲区,可使用eglGetConfigAttribute函数查询EGLConfig的属性。
EGLBoolean eglGetConfigs(EGLDisplay display,EGLConfig *configs,EGLint maxReturnConfigs,EGLint *numConfigs);
如果成功返回EGL_TRUE.
有两种方式调用eglGetConfigs,输入参数如果是NULL,系统将返回EGL_TRUE,并设定可用的EGLConfigs数目numConfigs,但不包含其他信息,但找到了可用的EGLConfigs数目,你就可以分配合适的内存数目来存储整个EGLConfigs.
EGLConfig是一个可供查询的属性列表,我们现在只讨论查询某个属性时使用:
EGLBoolean eglGetConfigAttrib(EGLDisplay display,EGLConfig config,EGLint attribute,EGLint*value)
将返回你查询的属性,这将让你控制你选择的实行创建渲染窗口。
例-2 配置EGL属性
EGLint attribList[]=
{
EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE,5,
EGL_GREEN_SIZE,6,
EGL_BLUE_SIZE,5,
EGL_DEPTH_SIZE,1,
EGL_NONE
};
例-3 演示怎么使用选择的属性值,查询窗口配置
const EGLint MaxConfigs=10;
EGLConfig configs[MaxConfigs]; //只接受10个配置
EGLint numConfigs;
if(!eglChooseConfig(dpy,attribList,configs,MaxConfigs,&numConfigs))
{
//一些没有运行处理错误情况
}
else
{
//一切正常,继续创建一个渲染界面
}
如果eglChooseConfig返回成功,一系列的符合要求的EGLConfigs将返回。
创建在屏显示渲染区:EGL窗口
一旦我们有一个合适的EGLConfig,可以调用下面函数创建窗口:
EGLSurface eglCreateWindowSurface(EGLDisplay display,EGLConfig config,EGLNativeWindowType window,const EGLint*attribList)
这个函数让程序联系到操作系统的窗口管理,窗口参数为EGLConfig,要求操作系统先前已经创建一个窗口,EGL是操作系统和OpenGL ES 2.0之间的软件层 。
由于某些原因调用eglCreateWindowSurface会失败,这时函数调用返回EGL_NO_SURFACE,我们可以使用eglGetError查询错误发生的原因。
例-4 创建EGL窗口
EGLRenderSurface window;
EGLint attribList[]=
{
EGL_RENFER_BIFFER,EGL_BACK_BUFFER,EGL_NONE
};
window=eglCreateWindowSurface(dpy,window,config,attrbList);
if(window==EGL_NO_SURFACE)
{
switch(eglGetError())
{
case EGL_BAD_MATCH:
break;
case EGL_BAD_CONFIG:
break;
case EGL_BAD_NATIVE_WINDOW:
break;
case EGL_BAd_ALLOC:
break;
}
}
可见窗口不是我们唯一能够绘制图形的地方,还可以绘制不能显示的窗口平面,称为pbuffers(像素缓冲区)。Pbuffeers能充分利用硬件,加速OpenGL ES 2.0.pbuffers常用来产生贴图纹理,如果你想使用贴图,你可以使用帧缓冲区来代替pbuffers,这时效率会更高。
创建pbuffers非常简单,有一点不同,也是使用EGLConfig,但修改EGL_SURFACE_TYPE值包括EGL_PBUFFEER_BIT,有了合适的EGLConfig后。使用下面的函数创建pbuffers.
EGLSurface eglCreatePbufferSurface(EGLDisplay display,EGLConfig config,const EGLint*attribList);
窗口创建后,我们注意力放到我们选择的EGLConfig属性,操作系统显示管理。
如果创建eglCreatePbufferSurface失败,这是函数调用返回EGL_NO_SURFACE,
例-5 创建EGL pbuffer
EGLint attribList[]=
{
EGL_SURFACE_TYPE,EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE,5,
EGL_GREEN_SIZE,6,
EGL_BLUE_SIZE,5,
EGL_DEPTH_SIZE,1,
EGL_NONE
};
const EGLint MaxConfig=10;
EGLConfig configs[MaxConfigs];
EGLint numConfigs;
if(!eglChooseConfig(dpy,attribList,configs,MaxConfigs,&numConfigs))
{
//出现错误不工作
}
else
{
//已经创建了一个pbuffer可用的EGLConfig
}
//继续创建一个512*512的pbuffer
EGLRenderSurface pbuffer;
EGLint attribList[]=
{
EGL_WIDTH,512,
EGL_HEIGHT,512,
EGL_LARGEST_PBUFFER,EGL_TRUE,
EGL_NONE
};
pbuffer=eglCreatePbufferSurface(dpy,config,attribList);
pbuffer=eglCreatePbufferSurface(dpy,config,attribList);
if(pbuffer==EGL_NO_SURFACE)
{
switch(eglGetError())
{
case EGL_BAD_ALLOC:
break;
case EGL_BAD_CONFIG:
break;
case EGL_BAD_PARAMETER:
break;
case EGL_BAD_MATCH:
break;
}
}
//检查我们分配的pbuffer是多大的
EGLint width;
EGLnt height;
if(!eglQuerySurface(dpy,pbuffer,EGL_WIDTH,&width)||
!eglQuerySurface(dpy,pbuffer,EGL_LENGTH,&height))
{
//不能询问窗口信息
}
Pbuffers和视窗一样支持所有的OpenGL ES 2.0渲染设备,最主要的区别——除了在你的屏幕上不能显示Pbuffers内容,当你完成渲染后,不是交换缓冲区,而是从Pbuffers中拷贝值到你的应用程序中或者绑定Pbuffers作为贴图。
创建渲染环境(上下文)
渲染环境指OpenGL ES 2.0的包含所有项目运行需要的设置的数据结构。例如着色点、片段着色器、顶点数据矩阵。OpenGL ES 2.0绘图前需要一个可用的context。
创建context,使用
EGLContext eglCreateContext(EGLDisplay display,EGLConfig config,EGLContext shareContext,const EGLint* attribList);
之后用EGLConfig建立显示连接你的应用程序,参数shareContext允许建立EGLContexts到各种类型数据的链接,像着色器、贴图。输入EGL_NO_CONTEXT作为shareContext的值,说明不和其他contexts分享资源。
最后像其他EGL函数一样eglCreateContext指定一系列的属性,如下表:
创建eglCreateContext成功后,它返回一个新创建的context的句柄。如果context不能被创建,eglCreateContext返回 EGL_NO_CONTEXT.可用eglGetError查询失败原因,唯一原因是EGLConfig不是有效的,返回的错误码是EGL_BAD_CONFIG.
例-6 显示怎样使用一个合适的EGLConfig创建context
const EGLint attrbList[]={
EGL_CONTEXT_CLIENT_VERSION,2,
EGL_NONE
};
EGLContext context;
context=eglCreateContext(dpy,config,EGL_NO_CONTEXT,attribList);
if(context==EGL_NO_CONTEXT)
{
EGLError error=eglGetError();
if(error==EGL_BAD_CONFIG)
{
//处理错误并重新运行
}
}
别的错误也可能被eglCreateContext产生,但目前我们只检查EGLConfig错误。成功创建EGLContext后,渲染前还需最后一步。
确定当前的EGLContext
一个应用可能创建多种EGLContext,我们需要确定联系到一个特殊的EGLContext,渲染使用的EGLContext,也叫作make current.
使用:
EGLBoolean eglmakeCurrent(EGLDisplay display,EGLSurface draw,EGLSurface read,EGLContext context);
函数使用了两个EGLSurface,这也是允许的,但是我们设定这两个值为相同的值,即先前创建的窗口。
本章流程:
- 初始化EGL
- 绑定一个EGLContext到EGLRenderSurface
- 如果窗口被创建,没有任何错误,应用结束了。
例-7 完整的创建EGL窗口的过程
EGLBoolean initializeWindow(EGLNativeWindow nativeWindow)
{
const EGLint configAttribs[]={
EGL_RENDER_TYPE,EGL_WINDOW_BIT,
EGL_RED_SIZE,8,
EGL_GREEN_SIZE,8,
EGL_BLUE_SIZE,8,
EGL_DEPTH_SIZE,8,
EGL_NONE
};
const EGLint contextAttribs[]=
{
EGL_CONTEXT_CLIENT_VERSION,2,
EGL_NONE
};
EGLDisplay dpy;
dpy=eglGetNativeDisplay(EGL_DEFAULT_DISPLAY);
if(dpy==EGL_NO_DISPLAY)
{
return EGL_FALSE;
}
EGLint major,minor;
if(!eglInitialize(dpy,&major,&minor))
{
return EGL_FALSE;
}
EGLConfig config;
EGLint numConfigs;
if(!eglChooseConfig(dpy,configAttribs,&config,1,&numConfigs)){
return EGL_FALSE;
}
EGLSurface window;
window=eglCreateWindowSurface(dpy,config,nativeWindow,NULL);
if(window==EGL_NO_SURFACE)
{
return EGL_FALSE;
}
EGLContext context;
context=eglCreateContext(dpy,config,EGL_NO_CONTEXT,contextAttribs);
if(context==EGL_NO_CONTEXT)
{
return EGL_FALSE;
}
if(!eglMakeCurrent(dpy,window,window,context))
{
return EGL_FALSE;
}
return EGL_TRUE;
}
例-8 显示打开一个512*512的窗口
ESContext esContext;
const char* title="OpenGL ES Application Window Title";
if(esCreateWindow(&esContext,title,512,512,ES_WINDOW_RGB|ES_WINDOW_DEPTH))
{
//窗口创建失败
}
这些窗口属性值将用来填充EGLConfig的属性列表。