OpenGL ES是 OpenGL三维图形API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计。 Ophone目前支持OpenGL ES 1.0 ,OpenGL ES 1.0 是以 OpenGL 1.3 规范为基础的,OpenGL ES 1.1 是以 OpenGL 1.5 规范为基础的。本文主要介绍利用OpenGL ES绘制图形方面的基本步骤。
本文内容由三部分构成。首先通过EGL获得OpenGL ES的编程接口;其次介绍构建3D程序的基本概念;最后是一个应用程序示例。
- 查询设备可以支持的显示句柄,并初始化。
- 创建渲染面,绘制OpenGL ES图形。
- 创建渲染上下文。EGL需要创建OpenGL ES渲染上下文用于关联到某个渲染面。
1
2
3
4
5
6
7
|
// EGLContext的静态方法getEGL获得EGL实例
EGL10 egl = (EGL10)EGLContext.getEGL();
//创建EGLDisplay, EGL_DEFAULT_DISPLAY获得缺省的本地窗口系统类型
EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
//初始化EGLDispla的同时获得版本号
int
[] version =
new
int
[
2
];
egl.eglInitialize(dpy, version);
|
每个 EGLDisplay 在使用前都需要初始化。初始化 EGLDisplay 的同时能够得到系统中 EGL 的实现版本号。通过版本号,合理运用相应OpenGL ES API,可以编写兼容性良好的程序,以适应更多的设备以及提供最大限度的移植性。初始化函数原型:
1
|
boolean
eglInitialize(EGLDisplay display,
int
[] major_minor)
|
其中的display是一个有效的 EGLDisplay实例。函数调用完成时, major_minor将被赋予当前 EGL 版本号。比如 EGL1.0 , major_minor[0]为1,major_minor[1]为0。EGLSurface包含了EGL渲染面相关的所有信息。查询EGLSurface配置信息有两种方法,一是查询所有的配置信息,从中选择一个最为适合的;二是指定好配置信息,由系统给出最佳匹配结果。一般采用第二种方法。用户通过configSpec指定出希望获得的配置,函数eglChooseConfig通过参数Configs返回最佳的配置列表。之后利用已获得的Configs,调用eglCreateContext创建一个渲染上下文,该函数返回EGLContext结构。渲染面EGLSurface的创建通过函数eglCreateWindowSurface完成。一个应用程序可以创建多个EGLContext。 eglMakeCurrent就是将某个渲染上下文绑定到渲染面。查询函数 eglGetCurrentContext, eglGetCurrentDisplay和eglGetCurrentSurface 分别用于获得当前系统的渲染上下文、显示句柄和渲染面。最后EGLContext的静态方法getGL获得OpenGL ES的编程接口。下面的程序片段总结了上述内容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
EGL10 egl = (EGL10)EGLContext.getEGL();
EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
int
[] version =
new
int
[
2
];
egl.eglInitialize(dpy, version);
int
[] configSpec = {
EGL10.EGL_RED_SIZE,
5
,
EGL10.EGL_GREEN_SIZE,
6
,
EGL10.EGL_BLUE_SIZE,
5
,
EGL10.EGL_DEPTH_SIZE,
16
,
EGL10.EGL_NONE
};
EGLConfig[] configs =
new
EGLConfig[
1
];
int
[] num_config =
new
int
[
1
];
egl.eglChooseConfig(dpy, configSpec, configs,
1
, num_config);
EGLConfig config = configs[
0
];
EGLContext context = egl.eglCreateContext(dpy, config,
EGL10.EGL_NO_CONTEXT,
null
);
EGLSurface surface = egl.eglCreateWindowSurface(dpy, config,
sHolder,
null
);
egl.eglMakeCurrent(dpy, surface, surface, context);
GL10 gl = (GL10)context.getGL();
|
构建3D图形的点
点是构建3D模型的基础。 OpenGL ES的内部计算是基于点的。 用点也可以表示光源的位置,物体的位置。一般我们用一组浮点数来表示点。 例如一个正方形的4个顶点可表示为:
1
2
3
4
5
6
|
float
vertices[] = {
-
1
.0f,
1
.0f,
0
.0f,
//左上
-
1
.0f, -
1
.0f,
0
.0f,
//左下
1
.0f, -
1
.0f,
0
.0f,
//右下
1
.0f,
1
.0f,
0
.0f,
//右上
};
|
为了提高性能, 需要将浮点数组存入一个字节缓冲中。 所以有了下面的操作:
1
2
3
4
5
|
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length *
4
);
vbb.order(ByteOrder.nativeOrder());
FloatBuffer vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(
0
);
|
其中ByteOrder.nativeOrder()是获取本机字节顺序。OpenGL ES有操作图形渲染管线的函数,在默认情况下这些函数功能的使用状态是处于关闭的。 启用和关闭这些函数可以用glEnableClientState、glDisableClientState来完成。
1
2
3
4
5
6
|
// 指定需要启用定点数组
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 说明启用数组的类型和字节缓冲,类型为GL_FLOAT
gl.glVertexPointer(
3
, GL10.GL_FLOAT,
0
, vertexBuffer);
// 不再需要时,关闭顶点数组
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
|
边
边是连接两个点的一条线,是多边形面的边缘。
多边形是由边构成的单闭合环。 OpenGL ES中的多边形必须是凸多边形,即在多边形的内部任意取两点, 如果连接这两个点的线段都在多变的内部,这个多边形就是凸多边形。 绘制多边形时需要指定渲染的方向, 分为顺时针和逆时针。 因为方向决定了多边形的朝向, 即正面和背面。 避免渲染那些被遮挡的部分可以了有效提高程序性能。 函数glFrontFace定义了渲染顶点的方向。
1
2
3
4
|
// 设置CCW方向为“正面”,CCW即CounterClockWise,逆时针
glFrontFace(GL_CCW);
// 设置CW方向为“正面”,CW即ClockWise,顺时针
glFrontFace(GL_CW);
|
渲染
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
static
private
FloatBuffer vertex;
//顶点对应的字节缓冲
static
private
FloatBuffer normal;
//法向量对应的字节缓冲
float
[] lightPos =
new
float
[] {
10
.0f,
10
.0f,
10
.0f,
1
.0f };
//光源的坐标
private
static
final
int
STEP =
24
;
//
private
static
final
float
RADIUS =
1
.0f;
//半径
protected
void
init(GL10 gl) {
gl.glClearColor(
0
.0f,
0
.0f,
0
.0f,
1
.0f);
//设置背景颜色
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPos,
0
);
gl.glEnable(GL10.GL_LIGHTING);
//启用光照
gl.glEnable(GL10.GL_LIGHT0);
//打开光源
gl.glClearDepthf(
1
.0f);
//设置深度缓存
gl.glDepthFunc(GL10.GL_LEQUAL);
//设置深度缓存比较函数,GL_LEQUAL表示新的像素的深度缓存值小于等于当前像素的深度缓存值时通过深度测试
gl.glEnable(GL10.GL_DEPTH_TEST);
//启用深度缓存
gl.glEnable(GL10.GL_CULL_FACE);
gl.glShadeModel(GL10.GL_SMOOTH);
//设置阴影模式GL_SMOOTH
}
protected
void
drawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT |
GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
GLU.gluLookAt(gl,
0
,
0
, 7f, 0f, 0f, 0f, 0f,
1
.0f,
0
.0f);
//
drawSphere(gl, RADIUS, STEP, STEP);
//绘制球形
}
|
public static void gluLookAt (GL10 gl, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
private
static
void
drawSphere(GL10 gl,
float
radius,
int
stacks,
int
slices) {
vertex=allocateFloatBuffer(
4
*
6
* stacks * (slices+
1
) );
normal=allocateFloatBuffer(
4
*
6
* stacks * (slices+
1
) );
int
i, j, triangles;
float
slicestep, stackstep;
stackstep = ((
float
)Math.PI) / stacks;
slicestep =
2
.0f * ((
float
)Math.PI) / slices;
for
(i =
0
; i < stacks; ++i)
{
float
a = i * stackstep;
float
b = a + stackstep;
float
s0 = (
float
)Math.sin(a);
float
s1 = (
float
)Math.sin(b);
float
c0 = (
float
)Math.cos(a);
float
c1 = (
float
)Math.cos(b);
float
nv;
for
(j =
0
; j <= slices; ++j)
{
float
c = j * slicestep;
float
x = (
float
)Math.cos(c);
float
y = (
float
)Math.sin(c);
nv=x * s0;
normal.put(nv);
vertex.put( nv * radius);
nv=y * s0;
normal.put(nv);
vertex.put( nv * radius);
nv=c0;
normal.put(nv);
vertex.put( nv * radius);
nv=x * s1;
normal.put(nv);
vertex.put( nv * radius);
nv=y * s1;
normal.put(nv);
vertex.put( nv * radius);
nv=c1;
normal.put(nv);
vertex.put( nv * radius);
}
}
normal.position(
0
);
vertex.position(
0
);
gl.glVertexPointer(
3
, GL10.GL_FLOAT,
0
, vertex);
gl.glNormalPointer(GL10.GL_FLOAT,
0
, normal);
gl.glEnableClientState (GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState (GL10.GL_NORMAL_ARRAY);
triangles = (slices +
1
) *
2
;
for
(i =
0
; i < stacks; i++)
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,
i * triangles, triangles);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
}
private
static
FloatBuffer allocateFloatBuffer(
int
capacity){
ByteBuffer vbb = ByteBuffer.allocateDirect(capacity);
vbb.order(ByteOrder.nativeOrder());
return
vbb.asFloatBuffer();
}
|
总结: