×
Namespaces

Variants
Actions

OpenGL ES简介

From Nokia Developer Wiki
Jump to: navigation, search
Article Metadata

文章
翻译:
hoolee
最后由 hamishwillee 在 22 Dec 2011 编辑

这篇文章主要向已经接触过桌面OpenGL的程序员介绍如何使用OpenGL ES在S60上进行开发。

Contents

简介

OpenGL ES是专为内嵌和移动设备设计的一个2D/3D轻量图形库,它是基于OpenGL API设计的。OpenGL ES 1.0版基于OpenGL 1.3,而OpenGL ES 1.1则是基于OpenGL 1.5的。现在主要由Khronos Group来负责管理OpenGL ES的开发维护。

OpenGL ES定义了一个概念,名为profile,它定义了基于原始OpenGL的一组子功能,以及针对OpenGL ES的增强功能,例

如:

  • Common Profile: 这个profile在移动设备如电话或pda中完成;
  • Common-Lite Profile: 这个限制较多,规定了运行3D图形程序所需最少的功能。这个主要针对安全性要求高的设备,它们的可靠性成了最需要考虑的东西。

OpenGL ES同样定义了和窗口系统(定义为EGL)的一个接口。这个EGL API是所有使用OpenGL ES的手机所需的,但是它的完成是硬件相关的。

所需文件

S60第二版FP2之后已经有了编写OpenGL ES 1.0应用程序所需的文件。OpenGL ES 1.1则在S60第三版FP1 SDK中出现 下列头文件是运行OpenGL ES所需的:

#include <GLES/egl.h>
#include <GLES/gl.h>

OpenGL ES由一个DLL完成,因此程序需要这个链接库:

libgles_cm.lib
ws32.lib

第一个文件关系到OpenGL ES和EGL,Common Profile。S60手机不支持Common-Lite Profile。第二个文件是关于Symbian OS

Window server的。在Carbide.c++中,当为手机编译时,这些文件应该有.dso后缀。

教程:如何开始OpenGL ES之旅

下面是开展OpenGL ES之旅的步骤:

  1. 获得缺省的现实设备;
  2. 初始化OpenGL ES;
  3. 选择一个OpenGL ES配置
  4. 生成一个OpenGL ES context
  5. 生成一个可绘制的surface
  6. 激活OpenGL ES context.

作为一个示例,我们使用如下定义的类来完成我们的目的:

#include <e32base.h>
#include <w32std.h>
 
#include "GLES/egl.h"
#include "GLES/gl.h"
 
class CGLRender: public CBase
{
public:
 
// method to create an instance of this class
static CGLRender* NewL (RWindow & aWindow);
 
public:
 
// destructor
~CGLRender ();
 
// double buffering, more on this later
void SwapBuffers ();
 
private:
 
// constructor
CGLRender (RWindow& aWindow);
 
// second part of the two-phase constructor, where
// OpenGL ES is initialized
void ConstructL();
 
private:
 
RWindow iWindow;
 
EGLDisplay iEglDisplay;
EGLConfig iEglConfig;
EGLContext iEglContext;
EGLSurface iEglSurface;
};

这里的iEglDisplay变量表示手机屏幕. 而OpenGL ES配置信息存放在iEglConfig. 而iEglContext变量则表示OpenGL ES的context。最后iEglSurface表示绘制surface。

获得手机屏幕

iEglDisplay = eglGetDisplay (EGL_DEFAULT_DISPLAY);
if (iEglDisplay == EGL_NO_DISPLAY)
User::Panic (_L("Unable to find a suitable EGLDisplay"), 0);

这里EGL_DEFAULT_DISPLAY指向缺省的手机屏幕(一般来说只有一个),如果程序运行失败,那么该函数将会返回

EGL_NO_DISPLAY.

初始化OpenGL ES

if (!eglInitialize (iEglDisplay, 0, 0) )
User::Panic (_L("Unable to initialize EGL"), 0);

最后两个参数是EGL完成的版本。如果你对此不需要,可以传递0进去。否则,他们将返回如下值:

EGLint major, minor;
 
eglInitialize (iEglDisplay, & major, &minor);

例如版本1.0,则 major为1,而minor为0

选择一个OpenGL ES配置

接下来,需要指定一个程序所需最小配置

EGLint numConfigs;
 
if (!eglChooseConfig (iEglDisplay, attribList, &iEglConfig, 1, &numConfigs) )
User::Panic (_L("Unable to choose EGL config"), 0);

这里attribList参数指明了程序运行所需的属性列表,该函数将在iEglConfig参数中返回一个与属性列表配套的可用配置。这个列表的大小被第四个参数所限制(这里我们只需要一个配置),而numConfigs参数则在程序返回后告知有多少个匹配的配置。这个属性列表定义了一组[attribute, value]数组。EGL指定所有支持的属性。如,我们将选择color depth和z-buffer大小:

// attribute list
EGLint attribList [] =
{
EGL_BUFFER_SIZE, 0, // color depth
EGL_DEPTH_SIZE, 15, // z-buffer
EGL_NONE
};
 
 
// here we use the same color depth as the device
// iWindow is a RWindow object
switch (iWindow.DisplayMode() )
{
case EColor4K:
attribList [1] = 12;
break;
case EColor64K:
attribList [1] = 16;
break;
 
case EColor16M:
attribList [1] = 24;
break;
default:
attribList [1] = 32;
}

上述列表要以EGL_NONE常量结尾。


生成OpenGL ES context

iEglContext = eglCreateContext (iEglDisplay, iEglConfig, EGL_NO_CONTEXT, 0);
 
if (iEglContext == 0)
User::Panic (_L("Unable to create EGL context"), 0);

第三个参数表明用来共享texture对象的context。这里,我们使用了EGL_NO_CONTEXT,它表明没有context可用。最后一个函数表明映射到新context上的属性列表。但在这里没有这样的一个列表存在。

激活context

要让OpenGL ES命令生效,我们需要激活context,让它成为当前可使用的。在OpenGL ES,一次只能有一个context生效。

eglMakeCurrent (iEglDisplay, iEglSurface, iEglSurface, iEglContext);

关闭OpenGL ES

当我们使用OpenGL ES后,需要释放所有资源,记住,这个非常重要!

CGLRender::~CGLRender()
{
eglMakeCurrent (iEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface (iEglDisplay, iEglSurface);
eglDestroyContext (iEglDisplay, iEglContext);
eglTerminate (iEglDisplay);
}

第一行代码用来关闭当前context。这样surface和context就可以被释放。最后一行表明结束OpenGL ES。

OpenGL和OpenGL ES的一些区别

缺省,OpenGL ES使用双缓冲,这里是处理代码:

void CGLRender::SwapBuffers ()
{
eglSwapBuffers (iEglDisplay, iEglSurface);
}

因为内嵌设备的限制,OpenGL ES不能包括很多OpenGL的多余操作,如不能在OpenGL ES使用一个表明几何形式的临时模式。因此,如下代码在OpenGL ES是无效的:

glBegin (GL_TRIANGLES);
glVertex3f (0,1,0);
glVertex3f (-1,0,0);
glVertex3f (1,0,0);
glEnd();

OpenGL ES从vertex数组中渲染所有的几何模型。因此如果要在OpenGL ES中渲染一个三角形,需要如下代码:

const GLbyte KVertices []=
{
0,1,0,
-1,0,0,
1,0,0
};
 
...
 
glEnableClientState (GL_VERTEX_ARRAY);
glVertexPointer (3, GL_BYTE , 0, KVertices);
glDrawArrays (GL_TRIANGLES, 0, 3);

和大多数没有FPU(浮点运算处理器)的设备一样,OpenGL ES profile只定义了可以接收整型数值的函数。 整型运算是将浮点数用整数表示的一种技巧。当我们使用整型数时,这个整数会被分成2个部分:一个用来存储真正的整数部分,而剩余的用来存储小数部分。OpenGL ES工作在16:16的32位字节数上。它表明有16位是整数,而剩余的16位是小数。更多相关信息请参考here.

这里有是一个可接收整型或浮点型函数的示例:

glTranslatex (20 << 16, 0, 0, 1 << 16);
 
// same as
// glTranslatef (20.0f, 0.0f, 0.0f, 1.0f);

只能接收整型参数的函数,会有一个'x'后缀。此外,OpenGL ES还引进了GLfixed类型用来表示整型数。有一些值得一提的区别是:

  • OpenGL ES中不存在哪些有所有的相同作用的,但有不同的缩写(如glVertex3{fsidv})的函数。但是有些如glColor4{fx}还是存在的;
  • OpenGL ES只支持RGBA颜色模式(你不需要选择)
  • OpenGL ES不绘制线框或点(只绘制实心填充);
  • 无需询问OpenGL ES 1.0中的动态属性(如当前颜色);
  • 没有GLU(OpenGL Utility Library),但可以找到GLU函数的内部完成,这些是OpenGL ES才有的;
  • GL_QUADS, GL_QUAD_STRIPGL_POLYGON是不支持的。

OpengL ES贴士

  • eglSwapBuffers函数不应该在View类的Draw方法中调用。实际的渲染将会被另一个方法处理。应该由一个timer类或活动对话来唤醒这个方法,如一个回调函数。
  • 建议使用全屏模式来显示OpenGL ES程序,因为如果不采用全屏,有时程序可能只能刷新一半(手机上)。我们用以通过在UI中生成view时传递ApplicationRect()参数指明全屏模式。
  • 使用整型数指明几何模型,因为很多手机都没有FPU的。
  • 要注意避免很多状态的改变,例如混合了有纹理映射和没有纹理映射的多边形将会降低性能,这种情况下,最好的解决方案是生成2组多边形,分别绘制。
  • 远距离物体不用进行透视矫正,因为这样效果并不明显。
  • 尽量减少或不要使用太远而不能被注意到的特效。
  • 光照很酷,但会增加处理负荷,所以要小心使用。
  • 如果有可能将多幅图片达到一个素材中,这样素材的变化将会最小。
  • 如果可能,尽量在一次单独调用中提交多个多边形,要比针多次调用好。

附录:完整的类

下面是完整的实现文件

#include "CGLRender.h"
 
//_____________________________________________________________________________
 
CGLRender::CGLRender (RWindow & aWindow)
: iWindow (aWindow)
{}
 
//_____________________________________________________________________________
 
CGLRender::~CGLRender()
{
eglMakeCurrent (iEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface (iEglDisplay, iEglSurface);
eglDestroyContext (iEglDisplay, iEglContext);
eglTerminate (iEglDisplay);
}
 
//_____________________________________________________________________________
 
CGLRender* CGLRender::NewL (RWindow & aWindow)
{
CGLRender* instance = new (ELeave) CGLRender (aWindow);
CleanupStack::PushL (instance);
 
instance->ConstructL();
CleanupStack::Pop();
 
return instance;
}
 
//_____________________________________________________________________________
 
void CGLRender::ConstructL()
{
// attribute list
EGLint attribList [] =
{
EGL_BUFFER_SIZE, 0,
EGL_DEPTH_SIZE, 15,
EGL_NONE
};
 
 
// get device color depth
switch (iWindow.DisplayMode() )
{
case EColor4K:
attribList [1] = 12;
break;
case EColor64K:
attribList [1] = 16;
break;
 
case EColor16M:
attribList [1] = 24;
break;
default:
attribList [1] = 32;
}
 
// step 1
iEglDisplay = eglGetDisplay (EGL_DEFAULT_DISPLAY);
 
if (iEglDisplay == EGL_NO_DISPLAY)
User::Panic (_L("Unable to find a suitable EGLDisplay"), 0);
 
// step 2
if (!eglInitialize (iEglDisplay, 0, 0) )
User::Panic (_L("Unable to initialize EGL"), 0);
 
// step 3
EGLint numConfigs;
 
if (!eglChooseConfig (iEglDisplay, attribList, &iEglConfig, 1, &numConfigs) )
User::Panic (_L("Unable to choose EGL config"), 0);
 
// step 4
iEglContext = eglCreateContext (iEglDisplay, iEglConfig, EGL_NO_CONTEXT, 0);
 
if (iEglContext == 0)
User::Panic (_L("Unable to create EGL context"), 0);
 
 
// step 5
iEglSurface = eglCreateWindowSurface (iEglDisplay, iEglConfig, &iWindow, 0);
 
if (iEglSurface == NULL)
User::Panic (_L("Unable to create EGL surface"), 0);
 
// step 6
eglMakeCurrent (iEglDisplay, iEglSurface, iEglSurface, iEglContext);
}
 
//_____________________________________________________________________________
 
void CGLRender::EnforceContext ()
{
eglMakeCurrent (iEglDisplay, iEglSurface, iEglSurface, iEglContext);
}
 
//_____________________________________________________________________________
 
void CGLRender::SwapBuffers ()
{
eglSwapBuffers (iEglDisplay, iEglSurface);
}
This page was last modified on 22 December 2011, at 08:10.
223 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×