OpenGL ES系列(5--3D立体图形)
转自:http://www.guidebee.info/wordpress/archives/1554
前面的例子尽管使用了OpenGL ES 3D图形库,但绘制的还是二维图形(平面上的正方形)。Mesh(网格,三角面)是构成空间形体的基本元素,前面的正方形也是有两个Mesh构成的。本篇将介绍使用Mesh构成四面体,椎体等基本空间形体。
Design设计
在使用OpenGL 框架时一个好的设计原则是使用“Composite Pattern”,本篇采用如下设计:
Mesh
首先定义一个基类 Mesh,所有空间形体最基本的构成元素为Mesh(三角形网格) ,其基本定义如下:
|
private FloatBuffer verticesBuffer = null ; |
|
private ShortBuffer indicesBuffer = null ; |
|
// The number of indices. |
|
private int numOfIndices = - 1 ; |
|
= new float [] { 1 .0f, 1 .0f, 1 .0f, 1 .0f }; |
|
private FloatBuffer colorBuffer = null ; |
|
public void draw(GL10 gl) { |
|
// Counter-clockwise winding. |
|
gl.glFrontFace(GL10.GL_CCW); |
|
gl.glEnable(GL10.GL_CULL_FACE); |
|
// What faces to remove with the face culling. |
|
gl.glCullFace(GL10.GL_BACK); |
|
// Enabled the vertices buffer for writing and |
|
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); |
|
// Specifies the location and data format |
|
// coordinates to use when rendering. |
|
gl.glVertexPointer( 3 , GL10.GL_FLOAT, 0 , verticesBuffer); |
|
gl.glColor4f(rgba[ 0 ], rgba[ 1 ], rgba[ 2 ], rgba[ 3 ]); |
|
if (colorBuffer != null ) { |
|
// Enable the color array buffer to be |
|
gl.glEnableClientState(GL10.GL_COLOR_ARRAY); |
|
gl.glColorPointer( 4 , GL10.GL_FLOAT, 0 , colorBuffer); |
|
gl.glTranslatef(x, y, z); |
|
gl.glRotatef(rx, 1 , 0 , 0 ); |
|
gl.glRotatef(ry, 0 , 1 , 0 ); |
|
gl.glRotatef(rz, 0 , 0 , 1 ); |
|
// Point out the where the color buffer is. |
|
gl.glDrawElements(GL10.GL_TRIANGLES, numOfIndices, |
|
GL10.GL_UNSIGNED_SHORT, indicesBuffer); |
|
// Disable the vertices buffer. |
|
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); |
|
gl.glDisable(GL10.GL_CULL_FACE); |
|
protected void setVertices( float [] vertices) { |
|
// a float is 4 bytes, therefore |
|
//we multiply the number if |
|
= ByteBuffer.allocateDirect(vertices.length * 4 ); |
|
vbb.order(ByteOrder.nativeOrder()); |
|
verticesBuffer = vbb.asFloatBuffer(); |
|
verticesBuffer.put(vertices); |
|
verticesBuffer.position( 0 ); |
|
protected void setIndices( short [] indices) { |
|
// short is 2 bytes, therefore we multiply |
|
= ByteBuffer.allocateDirect(indices.length * 2 ); |
|
ibb.order(ByteOrder.nativeOrder()); |
|
indicesBuffer = ibb.asShortBuffer(); |
|
indicesBuffer.put(indices); |
|
indicesBuffer.position( 0 ); |
|
numOfIndices = indices.length; |
|
protected void setColor( float red, float green, |
|
float blue, float alpha) { |
|
// Setting the flat color. |
|
protected void setColors( float [] colors) { |
|
= ByteBuffer.allocateDirect(colors.length * 4 ); |
|
cbb.order(ByteOrder.nativeOrder()); |
|
colorBuffer = cbb.asFloatBuffer(); |
- 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类的定义如下:
|
public class Plane extends Mesh { |
|
public Plane( float width, float height) { |
|
this (width, height, 1 , 1 ); |
|
public Plane( float width, float height, int widthSegments, |
|
= new float [(widthSegments + 1 ) |
|
* (heightSegments + 1 ) * 3 ]; |
|
= new short [(widthSegments + 1 ) |
|
* (heightSegments + 1 )* 6 ]; |
|
float xOffset = width / - 2 ; |
|
float yOffset = height / - 2 ; |
|
float xWidth = width / (widthSegments); |
|
float yHeight = height / (heightSegments); |
|
short w = ( short ) (widthSegments + 1 ); |
|
for ( int y = 0 ; y < heightSegments + 1 ; y++) { |
|
for ( int x = 0 ; x < widthSegments + 1 ; x++) { |
|
vertices[currentVertex] = xOffset + x * xWidth; |
|
vertices[currentVertex + 1 ] = yOffset + y * yHeight; |
|
vertices[currentVertex + 2 ] = 0 ; |
|
int n = y * (widthSegments + 1 ) + x; |
|
if (y < heightSegments && x < widthSegments) { |
|
indices[currentIndex] = ( short ) n; |
|
indices[currentIndex + 1 ] = ( short ) (n + 1 ); |
|
indices[currentIndex + 2 ] = ( short ) (n + w); |
|
indices[currentIndex + 3 ] = ( short ) (n + 1 ); |
|
indices[currentIndex + 4 ] = ( short ) (n + 1 + w); |
|
indices[currentIndex + 5 ] = ( short ) (n + 1 + w - 1 ); |
Cube
下面来定义一个正方体(Cube),为简单起见,这个四面体只可以设置宽度,高度,和深度,没有和Plane一样提供Segments支持。
|
public class Cube extends Mesh { |
|
public Cube( float width, float height, float depth) { |
|
float vertices[] = { -width, -height, -depth, // 0 |
|
width, -height, -depth, // 1 |
|
width, height, -depth, // 2 |
|
-width, height, -depth, // 3 |
|
-width, -height, depth, // 4 |
|
width, -height, depth, // 5 |
|
width, height, depth, // 6 |
|
-width, height, depth, // 7 |
|
short indices[] = { 0 , 4 , 5 , |