刚进公司,第一个项目是一个完全free的card game。项目要求:
平台:symbian s60
工具:carbide C++ 2.0
这是一个3D的game,所以选择使用OpenGL ES。
对pc的opengl我还接触过,以前做毕业设计的时候用过。对于嵌入式OpenGL就完全没底了。所以得从新开始学,但是在公司是不会有太多时间给你完全地去学习,需要边学边做。。。。因此,我打算把整个项目中我的学习过程记录下来。。。。
先了解一下OpenGL ES:
以下是小弟在这半个多月学习OpenGL的一点心得,请各位多指教!!!!!
OpenGL介绍:
OpenGL(Open Graphic s Library)开放图形程序库,是一个专业的3D程序接口,是一个功能强大、调用方便的底层3D图形库。
OpenGL 是个与硬件无关的软件接口,所以可以在不同的平台进行移植。由于OpenGL是3D图形的底层图形库,没有提供几何实体图元,不能直接用以描述场景。但是,通过一些转换程序,可以很方便地将AutoCAD、3DS等3D图形设计软件制作的DFX和3DS模型文件转换成OpenGL的顶点数组, 再通过OpenGL的函数将其绘制并显示到相关的设备。
OpenGL ES介绍:
OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL 三维图形 API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计。针对OpenGL API做了一些删减,因为OpenGL的一些API (比如:glBegin()、glEnd())在内存狭小的一些嵌入式设备(比如:手机)上面执行 很低,所以OpenGL ES 用了另外一些API进行取代(比如:glDrawElements)。
EGL 介绍:
EGL是OpenGL ES 和底层Native 平台视窗系统之间的接口。EGL是为OpenGL ES提供平台独立性而设计的 。OpenGL ES 为附加功能和可能的平台特性开发提供了扩展机制,但仍然需要一个可以让 OpenGL ES 和本地视窗系统交互且平台无关的层。 OpenGL ES 本质上是一个图形渲染管线的状态机,而 EGL 则是用于监控这些状态以及维护 Frame buffer 和其他渲染 Surface 的外部层。以下是一个典型的EGL系统布局图:
OpenGL数据类型:
typedef unsigned int GLenum;
typedef unsigned char GLboolean;
typedef unsigned int GLbitfield;
typedef signed char GLbyte;
typedef short GLshort;
typedef int GLint;
typedef int GLsizei;
typedef unsigned char GLubyte;
typedef unsigned short GLushort;
typedef unsigned int GLuint;
typedef float GLfloat;
typedef void GLvoid;
typedef int GLfixed;
如何在s60平台使用OpenGL ES:
首先需要创建绘图所需的渲染表面,渲染表面的初始化是由EGL API 实现的,每次要使用OpenGL时都需要初始化渲染表面。具体实现看如下代码:
#include<GLES/gl.h> //OpenGL ES的头文件
以下定义三个成员变量for display
EGLDisplay iEglDisplay; //Display where the graphics are drawn
注:(EGLDisplay 是一个关联系统物理屏幕的通用数据类型。对于 PC 来说, Display 就是显示器的句柄。不管是嵌入式系统或 PC ,都可能有多个物理显示设备。为了使用系统的显示设备, EGL 提供了 EGLDisplay 数据类型,以及一组操作设备显示的 API 。)
EGLContext iEglContext; //rendering context
EGLSurface iEglSurface; //window where the graphics are blitted
iEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
//Initialize display
eglInitialize(iEglDisplay,NULL,NULL);
EGLConfig config; //Config describing properties of EGLSurface
EGLConfig* configList = NULL; //Pointer for EGLConfigs
int configSize = 0; //Num of configs we want EGL to return
int numOfConfigs = 0; //Num of configs actually return
//Get the number of all possible EGLConfigs
eglGetConfigs(iEglDisplay,configList,configSize,&numOfConfigs);
configSize = numOfConfigs;
//Allocate memory for configList
configList = (EGLConfig*)User::Alloc(sizeof(EGLConfig)*configSize);
/* Define properties for the wanted EGLSurface.
*To get the best possible performance, choose
*an EGLConfig with a buffer size matching
*the current window's display mode*/
TDisplayMode DMode = Window().DisplayMode();
TInt BufferSize = 0;
switch ( DMode )
{
case(EColor4K):
BufferSize = 12;
break;
case(EColor64K):
BufferSize = 16;
break;
case(EColor16M):
BufferSize = 24;
break;
case(EColor16MU):
case(EColor16MA):
BufferSize = 32;
break;
default:
_LIT(KDModeError,"unsupported displaymode");
User::Panic( KDModeError, 0 );
break;
}
const EGLint attrib_list[] = {EGL_BUFFER_SIZE,BufferSize,EGL_DEPTH_SIZE,16,EGL_NONE};
//Choose configs that fullfill the requirement in attrib_list
if(eglChooseConfig(iEglDisplay, attrib_list, configList, configSize,&numOfConfigs)==EGL_FALSE)
{
_LIT(KChooseConfig,"choose config failed!");
User::Panic(KChooseConfig,0);
}
/* Choose the ‘best’ config and use that in the future */
config = configList[0];
User::Free(configList);
/* Create a surface where the graphics are blitted */
iEglSurface = eglCreateWindowSurface(iEglDisplay,config,&Window(),NULL);
/* Create a rendering context */
iEglContext = eglCreateContext(iEglDisplay,config,NULL,NULL);
/* Make the context current. Binds to the current rendering thread and surface.
* Use the same surface for both drawing and reading */
eglMakeCurrent(iEglDisplay,iEglSurface,iEglSurface,iEglContext);
iPeriodic = CPeriodic::NewL( CActive::EPriorityIdle ); // Create an active object for
// animating the scene
iPeriodic->Start( 10, 1, TCallBack( DrawCallBack, this ) );
注:
当不使用时,记得释放资源:
eglMakeCurrent(iEglDisplay,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT);
eglDestroySurface(iEglDisplay,iEglSurface);
eglDestroySurface(iEglDisplay,iEglContext);
eglTerminate(iEglDisplay);
Cperiodic用来创建一个活动对象不断的启动TcallBack()回调函数进行刷新,你的绘图程序也就在这个毁掉函数里面实现。
以上完成了EGL的初始化,之后便可以开始创建3D图形,通过OpenGL ES API绘制你所需要的图形,或者可以导入3ds文件获得其定点数组,将其绘制并显示。
注:每次绘制完后需要调用EGL API eglSwapBuffers(EGLDisplay display,EGLSurface surface)交换缓冲区,将surface的内容显示。
接下来就可以进入绘图。OpenGL ES 使用定点数组来存储对象坐标,因此定义一个几何体也就相当于定义一些数组(定点、法向量、索引等)。
这里以绘制一个简单的红色三角形为例:
首先定义三角形的坐标,三角形是三个定点,而且在3D世界里用的是三维坐标;
static const GLbyte vertices[3 * 3] =
{
-1, -1, 0,
1, -1, 0,
0, 2, 0
}; 也就相当于x(-1,-1,0) y(1,-1,0) z(0,2,0)
static const GLubyte triangles[1 * 3] =
{
/* front */
0,1,2
}; 这个是定义颜色的索引值,三角形的颜色和最后一个定点的颜色一样,当然也可以绘制颜色渐变的三角形,只需要把三个定点的颜色设置成不同就会有渐变的效果。
绘制三角形的数组已经准备好,接下来就需要对OpenGl ES的一些初始状态进行设置,主要包括背景色的清除、投影矩阵、阴影等。
glClearColor( 0.f, 0.f, 0.f, 1.f ); // 设置模式窗口的背景颜色,颜色采用的是RGBA值
glViewport( 0, 0, iScreenWidth, iScreenHeight );//设置视口的大小以及位置,
视口:也就是图形最终显示到屏幕的区域,前两个参数是视口的位置,后两个参数是视口的宽和长。
glMatrixMode( GL_PROJECTION ); // 设置矩阵模式为投影矩阵,之后的变换将影响投影矩阵。
OpenGL属于状态管理机制,比如:设置当前矩阵为投影矩阵过后,在没有重新调用glMatrixMode()之前,任何矩阵变换都将影响投影矩阵。
glFrustumf( -1.f, 1.f, -1.f, 1.f, 3.f, 1000.f ); //该函数创建一个透视投影矩阵,其中的参数定义了视景体,可以理解为用相机的时候,眼睛的可视范围。就像一个三棱锥,参数1、3、5和2、4、6分别定义了近裁面和远裁面的左下和右上的(x、y、z)坐标。
OpenGL 投影有两种模式,一种是透视投影,也就是通过上述函数创建一个三棱锥视景体,这种模式下观看三维模型是近大远小。另外一种模式是正交模式,视景体是一个平行六面体,离相机的距离不会影响物体的大小。
glMatrixMode( GL_MODELVIEW ); //设置当前矩阵为模式矩阵
glVertexpointer( 3, GL_BYTE, 0, vertices ); //指定从哪里存取空间坐标数据
OpenGL 一共有8个这样的函数可以存取不同的坐标数据:
glColorPointer();
glIndexPointer();
glNormalPointer();
glTexCoordPointer();等
glShadeModel( GL_FLAT ); //设置阴影模式为GL_FLAT,默认是GL_SMOOTH
阴影模式一共有两种,GL_SMOOTH和GL_FLAT,在有关照的情况下会有不同的效果。
glClear( GL_COLOR_BUFFER_BIT ); //清除颜色缓存
glLoadIdentity(); //设置当前矩阵为单位矩阵
OpenGL里面的位置大小都是用矩阵来表示的,比如:glScanf()放大或缩小,其实就是用一个矩阵去乘当前的矩阵,为了使变换不受当前矩阵的影响,所以把当前矩阵设置为单位矩阵。
glTranslatex(0, 0, -100 << 16 ); //将坐标向z轴负方向移动100
glColor4f( 1.f, 0.f, 0.f, 1.f ); 设置颜色为红色
/* Scale the geometry */
glScalex( 15 << 16, 15 << 16, 15 << 16 ); // 将物体沿xyz者分别放大15倍
/* Draw the triangle */
glDrawElements( GL_TRIANGLES, 1 * 3, GL_UNSIGNED_BYTE, indices ); //绘制图形,GL_TRIANGLES说明要绘制的图形是三角形,3表示一共有三个定点,GL_UNSIGNED_BYTE表示indices存储的数据类型
A call to eglSwapBuffers() can then be made.
这样便成功地绘制了一个红色的三角形。
一些常用的OpenGL ES API:
void glTranslatef(GLfloat x, GLfloat y, GLfloat z)
void glTranslatex(GLfixed x, GLfixed y, GLfixed z)
功能:沿x、y、z平移
void glScalef(GLfloat x, GLfloat y, GLfloat z)
void glScalex(GLfixed x, GLfixed y, GLfixed z)
功能:在x、y、z轴进行缩放,参数x、y、z为你想要的大小。
void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
void glRotatex(GLfixed angle, GLfixed x, GLfixed y, GLfixed z)
功能:沿x、y、z轴进行旋转。Angle表示将要旋转的角度。
void glClear(GLbitfield mask)
功能:用mask清除缓存可以有三种模式进行清除:GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, and GL_STENCIL_BUFFER_BIT.
void glClearDepthf(GLclampf depth)
void glClearDepthx(GLclampx depth)
功能:设置深度缓存,参数为0到1,使用glClear清除缓存。3D场景OpenGL程序都使用深度缓存。它的排序决定那个物体先画。这样您就不会将一个圆形后面的正方形画到圆形上来。
void glClearColor(GLclampf red,
GLclampf green,
GLclampf blue,
GLclampf alpha)
void glClearColorx(GLclampx red,
GLclampx green,
GLclampx blue,
GLclampx alpha)
功能:用指定的颜色值(RGBA)清除颜色缓存
void glColorPointer(GLint size,
GLenum type,
GLsizei stride,
const GLvoid * pointer)
功能:指定颜色的存储空间,size在OpenGL ES默认为4,表示(RGBA);type为pointer内容的类型;stride为数据在pointer内存中的偏移量;pointer为第一个元素的地址。
void glEnableClientState(GLenum array)
void glDisableClientState(GLenum array)
功能:启用或者禁止array,array有:GL_COLOR_ARRAY, GL_MATRIX_INDEX_ARRAY_OES, GL_NORMAL_ARRAY, GL_POINT_SIZE_ARRAY_ARRAY_OES, GL_TEXTURE_COORD_ARRAY, GL_VERTEX_ARRAY, and GL_WEIGHT_ARRAY_OES。
void glDrawElements(GLenum mode,
GLsizei count,
GLenum type,
const GLvoid * indices)
功能:按照参数给定的值绘制图形,mode指定要绘制的类型:GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, and GL_TRIANGLES;count指定要绘制多少个;type指定indices为数组首地址。
以上这些是比较常用的API,就不一一介绍了。可以查阅相关资料。