OpenGL|ES入门与分析

看到一篇不错的关于openGLES入门的文章,准备分析和学习一下。文章是刘鹏在去年愚人节发布在linuxgraphic.cn上的,题目为《OpenGL ES系列教程》,分为七章,从作者的描述来看,他也是转载的,文章原始出处在www.play3d.net,作者叫做功夫羊,具体是什么位置就不想去查找了。

首先来看看第一篇《了解OpenGL ES社区》。首先作者引用OpenGL|ES官方的说法:focused on the creation of open standard, royalty-free APIs to enable the authoring and accelerated playback of dynamic media on a wide variety of platforms and devices.核心也就两个短语:多平台多设备(移动设备)+免费动态媒体加速API,实质是通过对OpenGL的裁剪使之适合手持嵌入式设备。其次,从目前的资源上讲,主要有官方的文档和书籍、开源社区本身以及第三方的平台(比如ogel1.x2.0es3dGLUT等)。

然后看看系列第二篇文章《初始化EGL》。首先,作者指出OpenGL|ES是一个跨平台的图形库,因此必须要与实际的窗口系统关联起来(比如X-Window)。然后,这篇文章讲述了EGL初始化的整个流程,他画了一张图,如下:

Display → Config → Surface
                        ↑
                      Context
                        ↑
Application → OpenGL Command

由此可以清楚的看出整个流程,该文后续就针对上述步骤分别阐述如下:

  1. 调用eglGetDisplay()获取显示器信息,参数一般为EGL_DEFAULT_DISPLAY,对应X则为XDisplay ID。
  2. 调用eglInitialize()初始化EGL,进行内部初始化并传回ELG版本号(major.minor)。
  3. 调用eglChooseConfig()选择Config,config是Framebuffer的参数,对应X则为Visual。也可用eglGetConfigs()获取所有configs,并保存到config[]中。Config包含很多不可修改的参数,决定Framebuffer的格式和能力。
  4. 调用eglCreateWindowSurface()构造Surface,包含EGL_HEIGHT/WIDTH/TEXTURE_FORMAT等参数。
  5. 调用eglCreateContext()创建Context状态机,负责在程序提交Vertex坐标后进行颜色、纹理坐标、变换矩阵、渲染模式等一系列处理,最终形成填充Framebuffer的像素值。
  6. 应用程序调用eglSwapBuffers()将处理完成后的帧显示出来。

接着,来看看系列的第三篇《初始化GLES》,跟OpenGL的初始化类似,只不过由于其平台无关性需要通过Window Surface实现建立不同平台窗口的过程。总的来说OpenGL|ES的初始化如下:

  1. 调用eglInitialize()初始化egl库。
  2. 调用eglChooseConfig()选择合适的Framebuffer。
  3. 调用eglCreateWindowSurface()创建EGL Surface。
  4. 调用eglCreateContext()创建RenderContext(RC)。

接着,来看看系列的第四篇《Hello EGL》。首先作者介绍了WinCE下环境的过程,和我关系不大,跳过。然后就是通过用三角形“堆出”HELLO EGL这几个3D字母的方式介绍整个过程。由于OpenGL|ES仅支持通过VertexArray方式批量发送顶点坐标的方式,所以作者需要先调用glEnableClient()打开相关支持,并用ColorArray为每个顶点指定颜色,然后通过glVertexPointer和glColorPointer将数据发出去,最后通过glDrawArray()进行绘制。之后作者给出了“HELLO EGL”这几个3D字母的顶点坐标,并说明了该数组中的Glfixed数据类型(16bit.16bit的整点类型)和Float2Fixed宏的实现方式(直接乘以65536,再强制类型转换)。

接着,来看看系列的第五篇《加载模型》。首先必须从3DMax或者Maya等3D建模软件中将模型导出,然后才能开始绘制的步骤,为了方便,下面将源码和注释放在一起分析:

37 void draw(HWND hWnd, HDC hdc)
38 {
39   if ( ! g_egl.isInitialized() ) return;
40
41   if ( g_egl.makeCurrent() )
42   {
43     glClear(GL_COLOR_BUFFER_BIT);
44
45     glEnableClientState( GL_VERTEX_ARRAY );  
46     // 指示OpenGL状态机打开对Vertex/Normal/UB Array的支持
47     glEnableClientState( GL_NORMAL_ARRAY );  //同上
48     // glEnableClientState( GL_UV_ARRAY );
49     // 3 means (x,y,z) for one vertex, coord: fixed number, stride: 0
50     glVertexPointer( 3, GL_FIXED, 0, g_model.vertexs ); // 发送模型数据
51     glNormalPointer( GL_FIXED, 0, g_model.normals );    // 同上
52     // glTexCoordPointer( 3, GL_FIXED, 0, g_model.uvs );// 同上
53
54     glDrawElements( GL_TRIANGLES, g_model.faceCount * 3, 
55          GL_UNSIGNED_SHORT, g_model.faces );  // 通过额外的顶点数组定义面
56     g_egl.swapBuffers();
57   }
58
59   reportError();
60 }

接着,来看看本系列最后一篇:第六篇《材质纹理》。给3D模型贴上纹理的目的是表现表面细节(其实对于台式机而言,通过纹理表现细节已经无法满足需求,而需要对像素点的处理)。首先是通过光照模型给3D模型上颜色。这一步主要需要处理好物体表面被点光源照射后所产生三个区域:高光区、过度色区和环境色区。编程人员可以通过调用glMaterialx()函数将其分别处理为镜面反射、漫反射以及非光照部分。此后还必须调用glEnable(GL_Lighting/GL_Light0)启用光照计算。如果需要复杂的纹理,则还需要通过纹理贴图(纹理一般是放在显存中,因此对于台式机的显卡而言,贴图速率就是核心指标之一)来实现,本过程可分为以下四步:

  1. 在内存中准备好纹理数据。
  2. 调用glTexImage2D将准备好的纹理数据上传到显卡,并通知当前流水线设置纹理。如果某纹理需要反复调用,则可以通过glGenTextures()和glBindTexture()将其反复设置为“当前”纹理。
  3. 设置贴图参数。控制贴图过程主要包括:glEnable(GL_TEXTURE_2D)使能纹理贴图,glTexParameterx()设置当面生成的光栅与纹理不能一一对应时的处理方案(包括调整纹理坐标、缩小纹理尺寸以及放大纹理尺寸等等)。
  4. 在绘制过程中提供贴图坐标。这是通过通过调用glEnableClientState()和glTexCoordPointer()给模型加入uv坐标。

以上就是这一系列文章的主体了,引文的最后还加入了一篇别人博客的文章,更加细致的描述了整个过程,点击此处查看。

Advertisements

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s