OpenGL ES系列(5--3D立体图形)
转自:http://www.guidebee.info/wordpress/archives/1554
前面的例子尽管使用了OpenGL ES 3D图形库,但绘制的还是二维图形(平面上的正方形)。Mesh(网格,三角面)是构成空间形体的基本元素,前面的正方形也是有两个Mesh构成的。本篇将介绍使用Mesh构成四面体,椎体等基本空间形体。
Design设计
在使用OpenGL 框架时一个好的设计原则是使用“Composite Pattern”,本篇采用如下设计:
Mesh
首先定义一个基类 Mesh,所有空间形体最基本的构成元素为Mesh(三角形网格) ,其基本定义如下:
3 | private FloatBuffer verticesBuffer = null ; |
6 | private ShortBuffer indicesBuffer = null ; |
9 | private int numOfIndices = - 1 ; |
13 | = new float [] { 1 .0f, 1 .0f, 1 .0f, 1 .0f }; |
16 | private FloatBuffer colorBuffer = null ; |
32 | public void draw(GL10 gl) { |
34 | gl.glFrontFace(GL10.GL_CCW); |
36 | gl.glEnable(GL10.GL_CULL_FACE); |
38 | gl.glCullFace(GL10.GL_BACK); |
42 | gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); |
46 | gl.glVertexPointer( 3 , GL10.GL_FLOAT, 0 , verticesBuffer); |
48 | gl.glColor4f(rgba[ 0 ], rgba[ 1 ], rgba[ 2 ], rgba[ 3 ]); |
50 | if (colorBuffer != null ) { |
53 | gl.glEnableClientState(GL10.GL_COLOR_ARRAY); |
54 | gl.glColorPointer( 4 , GL10.GL_FLOAT, 0 , colorBuffer); |
57 | gl.glTranslatef(x, y, z); |
58 | gl.glRotatef(rx, 1 , 0 , 0 ); |
59 | gl.glRotatef(ry, 0 , 1 , 0 ); |
60 | gl.glRotatef(rz, 0 , 0 , 1 ); |
63 | gl.glDrawElements(GL10.GL_TRIANGLES, numOfIndices, |
64 | GL10.GL_UNSIGNED_SHORT, indicesBuffer); |
66 | gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); |
68 | gl.glDisable(GL10.GL_CULL_FACE); |
71 | protected void setVertices( float [] vertices) { |
76 | = ByteBuffer.allocateDirect(vertices.length * 4 ); |
77 | vbb.order(ByteOrder.nativeOrder()); |
78 | verticesBuffer = vbb.asFloatBuffer(); |
79 | verticesBuffer.put(vertices); |
80 | verticesBuffer.position( 0 ); |
83 | protected void setIndices( short [] indices) { |
88 | = ByteBuffer.allocateDirect(indices.length * 2 ); |
89 | ibb.order(ByteOrder.nativeOrder()); |
90 | indicesBuffer = ibb.asShortBuffer(); |
91 | indicesBuffer.put(indices); |
92 | indicesBuffer.position( 0 ); |
93 | numOfIndices = indices.length; |
96 | protected void setColor( float red, float green, |
97 | float blue, float alpha) { |
105 | protected void setColors( float [] colors) { |
108 | = ByteBuffer.allocateDirect(colors.length * 4 ); |
109 | cbb.order(ByteOrder.nativeOrder()); |
110 | colorBuffer = cbb.asFloatBuffer(); |
111 | colorBuffer.put(colors); |
112 | colorBuffer.position( 0 ); |
- setVertices 允许子类重新定义顶点坐标。
- setIndices 允许子类重新定义顶点的顺序。
- setColor /setColors允许子类重新定义颜色。
- x,y,z 定义了平移变换的参数。
- rx,ry,rz 定义旋转变换的参数。
Plane
有了Mesh定义之后,再来构造Plane,plane可以有宽度,高度和深度,宽度定义为沿X轴方向的长度,深度定义为沿Z轴方向长度,高度为Y轴方向。
Segments为形体宽度,高度,深度可以分成的份数。 Segments在构造一个非均匀分布的Surface特别有用,比如在一个游戏场景中,构造地貌,使的Z轴的值随机分布在-0.1到0.1之间,然后给它渲染好看的材质就可以造成地图凹凸不平的效果。
上面图形中Segments为一正方形,但在OpenGL中我们需要使用三角形,所有需要将Segments分成两个三角形。为Plane 定义两个构造函数:
// Let you decide the size of the plane but still only one segment.
public Plane(float width, float height)
// For alla your settings.
public Plane(float width, float height, int widthSegments, int heightSegments)
比如构造一个1 unit 宽和 1 unit高,并分成4个Segments,使用图形表示如下:
左边的图显示了segments ,右边的图为需要创建的Face(三角形)。
Plane类的定义如下:
1 | public class Plane extends Mesh { |
6 | public Plane( float width, float height) { |
7 | this (width, height, 1 , 1 ); |
10 | public Plane( float width, float height, int widthSegments, |
13 | = new float [(widthSegments + 1 ) |
14 | * (heightSegments + 1 ) * 3 ]; |
16 | = new short [(widthSegments + 1 ) |
17 | * (heightSegments + 1 )* 6 ]; |
19 | float xOffset = width / - 2 ; |
20 | float yOffset = height / - 2 ; |
21 | float xWidth = width / (widthSegments); |
22 | float yHeight = height / (heightSegments); |
23 | int currentVertex = 0 ; |
25 | short w = ( short ) (widthSegments + 1 ); |
26 | for ( int y = 0 ; y < heightSegments + 1 ; y++) { |
27 | for ( int x = 0 ; x < widthSegments + 1 ; x++) { |
28 | vertices[currentVertex] = xOffset + x * xWidth; |
29 | vertices[currentVertex + 1 ] = yOffset + y * yHeight; |
30 | vertices[currentVertex + 2 ] = 0 ; |
33 | int n = y * (widthSegments + 1 ) + x; |
35 | if (y < heightSegments && x < widthSegments) { |
37 | indices[currentIndex] = ( short ) n; |
38 | indices[currentIndex + 1 ] = ( short ) (n + 1 ); |
39 | indices[currentIndex + 2 ] = ( short ) (n + w); |
41 | indices[currentIndex + 3 ] = ( short ) (n + 1 ); |
42 | indices[currentIndex + 4 ] = ( short ) (n + 1 + w); |
43 | indices[currentIndex + 5 ] = ( short ) (n + 1 + w - 1 ); |
51 | setVertices(vertices); |
Cube
下面来定义一个正方体(Cube),为简单起见,这个四面体只可以设置宽度,高度,和深度,没有和Plane一样提供Segments支持。
1 | public class Cube extends Mesh { |
2 | public Cube( float width, float height, float depth) { |
7 | float vertices[] = { -width, -height, -depth, |
8 | width, -height, -depth, |
10 | -width, height, -depth, |
11 | -width, -height, depth, |
12 | width, -height, depth, |
14 | -width, height, depth, |
17 | short indices[] = { 0 , 4 , 5 , |
31 | setVertices(vertices); |
Group
Group可以用来管理多个空间几何形体,如果把Mesh比作Android的View ,Group可以看作Android的ViewGroup,Android的View的设计也是采用的“Composite Pattern”。
Group的主要功能是把针对Group的操作(如draw)分发到Group中的每个成员对应的操作(如draw)。
Group定义如下:
1 | public class Group extends Mesh { |
2 | private Vector<Mesh> children = new Vector<Mesh>(); |
5 | public void draw(GL10 gl) { |
6 | int size = children.size(); |
7 | for ( int i = 0 ; i < size; i++) |
8 | children.get(i).draw(gl); |
14 | * @see java.util.Vector#add(int, java.lang.Object) |
16 | public void add( int location, Mesh object) { |
17 | children.add(location, object); |
23 | * @see java.util.Vector#add(java.lang.Object) |
25 | public boolean add(Mesh object) { |
26 | return children.add(object); |
31 | * @see java.util.Vector#clear() |
40 | * @see java.util.Vector#get(int) |
42 | public Mesh get( int location) { |
43 | return children.get(location); |
49 | * @see java.util.Vector#remove(int) |
51 | public Mesh remove( int location) { |
52 | return children.remove(location); |
58 | * @see java.util.Vector#remove(java.lang.Object) |
60 | public boolean remove(Object object) { |
61 | return children.remove(object); |
66 | * @see java.util.Vector#size() |
69 | return children.size(); |
其它建议
上面我们定义里Mesh, Plane, Cube等基本空间几何形体,对于构造复杂图形(如人物),可以预先创建一些通用的几何形体,如果在组合成较复杂的形体。除了上面的基本形体外,可以创建如Cone,Pryamid, Cylinder等基本形体以备后用。
本例示例代码下载,显示结果如下: