1)首先你要定义自己的Polygon类,当然你一可以直接在Renderer的子类中直接绘制,为了更加符合面向对象,还是自定义一个Polygon类更好,这样代码更加清晰,Polygon类中主要有Polygon的顶点坐标,和绘制Polygon对象的draw方法,例如下面的代码:
public class Polygon{
/** The buffer holding the Polygon‘s vertices
保存Polygon对象顶点坐标的FloatBuffer
*/
private FloatBuffer vertexBuffer;
/** The initial vertex definition
保存Polygon对象顶点坐标的的float数组
*/
private float vertices[] = {
0.0f, 1.0f, 0.0f, //Top
-1.0f, -1.0f, 0.0f, //Bottom Left
1.0f, -1.0f, 0.0f //Bottom Right
};
/**
* The Triangle constructor.
*
* Initiate the buffers.
*/
public Triangle() {
//this is the common method to initiate the FloatBuffer
//下面是一种常用的初始化FloatBuffer的方法,本人还见到过一种方法,如下:
//vertexBuffer = FloatBuffer.wrap(vertices)
//但是如果用这种方法在运行的时候会报错,指出你的FloatBuffer没有序列化,
//不明白原因,如有明白的高手帮解释一下,不胜感激
ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
vertexBuffer = byteBuf.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
}
/**
* The object own drawing function.
* Called from the renderer to redraw this instance
* with possible changes in values.
*
* @param gl – The GL context
*/
public void draw(GL10 gl) {
//这就是Polygon被绘制的方法,下面都是一些在draw方法中经常用到的简单的设置,
//在此不一一解释了
//Set the face rotation
gl.glFrontFace(GL10.GL_CW);
//Point to our vertex buffer
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
//Enable vertex buffer
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//Draw the vertices as triangle strip
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
//Disable the client state before leaving
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}
}
2)下面就是Renderer子类,它就是一个OpenGL渲染器,就是真正绘制3D图形的地方,主要是重写三个方法(三个方法将在下面一一标出),设置一些属性,来完成我们想要达到的效果,代码如下:
public class Myrenderer implements Renderer {
public Myrenderer () {
triangle = new Triangle();
square = new Square();
}
/**
* The Surface is created/init()
这个方法是当surface创建时调用的方法,主要是设置一些属性
*/
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glShadeModel(GL10.GL_SMOOTH); //Enable Smooth Shading
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); //Black Background
gl.glClearDepthf(1.0f); //Depth Buffer Setup
gl.glEnable(GL10.GL_DEPTH_TEST); //Enables Depth Testing
gl.glDepthFunc(GL10.GL_LEQUAL); //The Type Of Depth Testing To Do
//Really Nice Perspective Calculations
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
}
/**
* Here we do our drawing
*/
public void onDrawFrame(GL10 gl) {
//这个方法就是真正绘制3D图形的方法,系统会根据机器的性能在固定的时间间隔自动调用这个方法
//这里通过设置gl的属性,和我们定义的Polygon类的顶点坐标来绘制我们想要达到的效果
//Clear Screen And Depth Buffer
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity(); //Reset The Current Modelview Matrix
gl.glTranslatef(0.0f, -1.2f, -6.0f); //Move down 1.2 Unit And Into The Screen 6.0
square.draw(gl); //Draw the square
gl.glTranslatef(0.0f, 2.5f, 0.0f); //Move up 2.5 Units
triangle.draw(gl); //Draw the triangle
}
/**
* If the surface changes, reset the view
*/
public void onSurfaceChanged(GL10 gl, int width, int height) {
//这个方法是当surface改变时调用的方法,也是设置一些gl的属性,
//大体的设置没有太大变化,所以这基本上是一个通用的写法
if(height == 0) { //Prevent A Divide By Zero By
height = 1; //Making Height Equal One
}
gl.glViewport(0, 0, width, height); //Reset The Current Viewport
gl.glMatrixMode(GL10.GL_PROJECTION); //Select The Projection Matrix
gl.glLoadIdentity(); //Reset The Projection Matrix
//Calculate The Aspect Ratio Of The Window
GLU.gluPerspective(gl, 45.0f, (float)width / (float)height, 0.1f, 100.0f);
gl.glMatrixMode(GL10.GL_MODELVIEW); //Select The Modelview Matrix
gl.glLoadIdentity(); //Reset The Modelview Matrix
}
}
3)最后就是把我们的GLSurfaceView通过activity的setContentView()方法加载到屏幕上,代码如下:
public class Run extends Activity {
/** The OpenGL View */
private GLSurfaceView glSurface;
/**
* Initiate the OpenGL View and set our own
* Renderer */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Create an Instance with this Activity
glSurface = new GLSurfaceView(this);
//Set our own Renderer
glSurface.setRenderer(new Lesson02());
//Set the GLSurface as View to this Activity
setContentView(glSurface);
}
/**
* Remember to resume the glSurface
*/
@Override
protected void onResume() {
super.onResume();
glSurface.onResume();
}
/**
* Also pause the glSurface
*/
@Override
protected void onPause() {
super.onPause();
glSurface.onPause();
}
}
希望给刚刚入门的同学一些帮助,也希望和高手们交流一下经验。
第一课里是一个简单的正方形的绘制,现在我们要为这个正方形添加颜色。唯一的不同(和上一课比较,以后同意)就是在Polygon类的draw方法中添加了如下内容(红色字体):
public void draw(GL10 gl) {
//Set the face rotation
gl.glFrontFace(GL10.GL_CW);
//Point to our vertex buffer
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
//Enable vertex buffer
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//Set The Color To Blue
gl.glColor4f(0.5f, 0.5f, 1.0f, 1.0f);
//Draw the vertices as triangle strip
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
//Disable the client state before leaving
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}
当然也可以通过下面的方法为Polygon添加颜色,和使用顶点数组一样,我们可以建立一个颜色的数组,具体实现如下:
/** The buffer holding the colors */
private FloatBuffer colorBuffer;
/** The initial color definition */
private float colors[] = {
1.0f, 0.0f, 0.0f, 1.0f, //Set The Color To Red, last value 100% luminance
0.0f, 1.0f, 0.0f, 1.0f, //Set The Color To Green, last value 100% luminance
0.0f, 0.0f, 1.0f, 1.0f, //Set The Color To Blue, last value 100% luminance
0.5f, 0.5f, 0.5f, 1.0f, //Set The Color To combination, last value 100% luminance
};
//在我们的Polygon类构造方法中为colorBuffer初始化
public Polygon() {
byteBuf = ByteBuffer.allocateDirect(colors.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
colorBuffer = byteBuf.asFloatBuffer();
colorBuffer.put(colors);
colorBuffer.position(0);
}
//在draw方法中添加对color的权限(红色部分为新加内容)
public void draw(GL10 gl) {
//Set the face rotation
gl.glFrontFace(GL10.GL_CW);
//Point to our buffers
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer);
//Enable the vertex and color state
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
//Draw the vertices as triangles
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vertices.length / 3);
//Disable the client state before leaving
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
}
这样我们就绘制了一个带有颜色的Polygon。
这一课和前面的唯一不同在于MyRenderer类中的onDrawFrame(GL10 gl)方法,在这个方法里我们添加了对Polygon翻转的操作,
代码如下(红色部分为新添加的部分):
//定义一个控制翻转角度的变量
private float rquad;
public void onDrawFrame(GL10 gl) {
//Clear Screen And Depth Buffer
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity(); //Reset The Current Modelview Matrix
//Drawing
gl.glTranslatef(0.0f, -1.2f, -6.0f); //Move down 1.0 Unit And Into The Screen 6.0
gl.glRotatef(rquad, 1.0f, 0.0f, 0.0f); //Rotate The PolygonOn The X axis ( NEW ) 沿x轴翻转
square.draw(gl); //Draw the Polygon
rquad -= 0.15f; //Decrease The Rotation Variable For The Polygon( NEW ) 改变翻转角度
}
前几课我们绘制的是平面的图形,下面我们要绘制一个立体的图形——六棱柱,这里我们使用了顶点索引,这是一个新的概念,它是一个指明了顶点绘制顺序的ByteBuffer,还有就是这里我们使用的是二维的顶点数组,其中第二维中所有的顶点是同一个平面上的点,所以我们是一个面一个面的绘制,MyPolygon 类改动的代码较多,其他类基本不变,下面一一描述:
public class MyPolygon {
//保存每一个平面的顶点索引的ByteBuffer数组
private ByteBuffer[] indexBuffer;
//保存每一个平面的顶点坐标的FloatBuffer数组
private FloatBuffer[] faceVertexBuffer;
//保存每一个平面的顶点坐标的二维数组
private float[][] faceVertices = {
new float[] {
-0.5f, -0.816f, 1.0f,// bottom left 0
0.5f, -0.816f, 1.0f,// bottom right 1
-1.0f, 0.0f, 1.0f,// middle left 2
1.0f, 0.0f, 1.0f,// middle right 3
-0.5f, 0.816f, 1.0f, // top left 4
0.5f, 0.816f, 1.0f // top right 5
},// front face
new float[] {
-0.5f, -0.816f, -1.0f,// bottom left 6
0.5f, -0.816f, -1.0f,// bottom right 7
-1.0f, 0.0f, -1.0f,// middle left 8
1.0f, 0.0f, -1.0f,// middle right 9
-0.5f, 0.816f, -1.0f, // top left 10
0.5f, 0.816f, -1.0f // top right 11
},// back face
new float[] {
-0.5f, -0.816f, -1.0f,// bottom left 6
0.5f, -0.816f, -1.0f,// bottom right 7
-0.5f, -0.816f, 1.0f,// bottom left 0
0.5f, -0.816f, 1.0f // bottom right 1
},// bottom face
new float[] {
-1.0f, 0.0f, -1.0f,// middle left 8
-0.5f, -0.816f, -1.0f,// bottom left 6
-1.0f, 0.0f, 1.0f,// middle left 2
-0.5f, -0.816f, 1.0f // bottom left 0
},// bottom left face
new float[] {
0.5f, -0.816f, -1.0f,// bottom right 7
1.0f, 0.0f, -1.0f,// middle right 9
0.5f, -0.816f, 1.0f,// bottom right 1
1.0f, 0.0f, 1.0f // middle right 3
},// bottom right face
new float[] {
0.5f, 0.816f, -1.0f, // top right 11
1.0f, 0.0f, -1.0f,// middle right 9
0.5f, 0.816f, 1.0f,// top right 5
1.0f, 0.0f, 1.0f // middle right 3
},// top right face
new float[] {
-0.5f, 0.816f, -1.0f, // top left 10
0.5f, 0.816f, -1.0f, // top right 11
-0.5f, 0.816f, 1.0f,// top left 4
0.5f, 0.816f, 1.0f // top right 5
},// top face
new float[] {
-0.5f, 0.816f, -1.0f, // top left 10
-1.0f, 0.0f, -1.0f,// middle left 8
-0.5f, 0.816f, 1.0f, // top left 4
-1.0f, 0.0f, 1.0f // middle left 2
} // top left face
};
//定义每一个平面颜色的数组
private float[] colors = { 1.0f, 0.0f, 0.0f, 1.0f, // 0
0.0f, 1.0f, 0.0f, 1.0f,// 1
0.0f, 0.0f, 1.0f, 1.0f, // 2
1.0f, 1.0f, 1.0f, 1.0f, // 3
1.0f, 1.0f, 0.0f, 1.0f,// 4
1.0f, 0.0f, 1.0f, 1.0f,// 5
0.0f, 1.0f, 1.0f, 1.0f,// 6
0.5f, 0.5f, 0.5f, 1.0f, // 7
};
//保存每一个平面的顶点索引的二维数组
private byte[][] indies = {
new byte[] {
0, 1, 2,
1, 3, 2,
2, 3, 4,
3, 5, 4
},// front face
new byte[] {
0, 1, 2,
1, 3, 2,
2, 3, 4,
3, 5, 4
},// back face
new byte[] {
0, 1, 2,
1, 3, 2
},// bottom face
new byte[] {
0, 1, 2,
1, 3, 2
},// bottom left face
new byte[] {
0, 1, 2,
1, 3, 2
},// bottom right face
new byte[] {
0, 1, 2,
1, 3, 2
},// top right face
new byte[] {
0, 1, 2,
1, 3, 2
},// top face
new byte[] {
0, 1, 2,
1, 3, 2
} // top left face
};
public MyPolygon() {
//利用循环初始化顶点坐标faceVertexBuffer数组和顶点索引indexBuffer数组
ByteBuffer bb;
faceVertexBuffer = new FloatBuffer[8];
for (int i = 0; i < faceVertices.length; i++) {
bb = ByteBuffer.allocateDirect(faceVertices[i].length * 4);
bb.order(ByteOrder.nativeOrder());
faceVertexBuffer[i] = bb.asFloatBuffer();
faceVertexBuffer[i].put(faceVertices[i]);
faceVertexBuffer[i].position(0);
}
indexBuffer = new ByteBuffer[8];
for (int i = 0; i < indies.length; i++) {
indexBuffer[i] = ByteBuffer.allocateDirect(indies[i].length);
indexBuffer[i].put(indies[i]);
indexBuffer[i].position(0);
}
}
public void onDraw(GL10 gl) {
//利用循环绘制六棱柱的每一个面,并给不同的面设置不同的颜色
gl.glFrontFace(GL10.GL_CW);
for (int i = 0; i < 8; i++) {
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, faceVertexBuffer[i]);
gl.glColor4f(colors[4 * i + 0], colors[4 * i + 1],
colors[4 * i + 2], colors[4 * i + 3]);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDrawElements(GL10.GL_TRIANGLES, indies[i].length,
GL10.GL_UNSIGNED_BYTE, indexBuffer[i]);//另一种绘制的方法glDrawElements
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}
}
}
这里我们将加入纹理来代替颜色设置不同表面的内容,和上一课不同的地方是在Polygon类中加入了一些纹理的设置,下面将一一描述,代码如下:
public class MyPolygon {
//保存纹理的FloatBuffer
private FloatBuffer[] textureBuffer;
//用来加载纹理的数组
private int [] textures = new int[8];
//保存纹理顶点坐标的数组
private float[][] texture = {
new float[]{
0.0f,0.0f,
1.0f,0.0f,
0.0f,0.5f,
1.0f,0.5f,
0.0f,1.0f,
1.0f,1.0f,
},
new float[]{
0.0f,0.0f,
1.0f,0.0f,
0.0f,0.5f,
1.0f,0.5f,
0.0f,1.0f,
1.0f,1.0f,
},
new float[]{
0.0f,0.0f,
1.0f,0.0f,
0.0f,1.0f,
1.0f,1.0f,
},
new float[]{
0.0f,0.0f,
1.0f,0.0f,
0.0f,1.0f,
1.0f,1.0f,
},
new float[]{
0.0f,0.0f,
1.0f,0.0f,
0.0f,1.0f,
1.0f,1.0f,
},
new float[]{
0.0f,0.0f,
1.0f,0.0f,
0.0f,1.0f,
1.0f,1.0f,
},
new float[]{
0.0f,0.0f,
1.0f,0.0f,
0.0f,1.0f,
1.0f,1.0f,
},
new float[]{
0.0f,0.0f,
1.0f,0.0f,
0.0f,1.0f,
1.0f,1.0f,
}
};
public MyPolygon() {
ByteBuffer bb;
//初始化保存纹理的FloatBuffer
textureBuffer = new FloatBuffer[8];
for (int i = 0; i < 8; i++) {
bb = ByteBuffer.allocateDirect(texture[i].length * 4);
bb.order(ByteOrder.nativeOrder());
textureBuffer[i] = bb.asFloatBuffer();
textureBuffer[i].put(texture[i]);
textureBuffer[i].position(0);
}
}
public void onDraw(GL10 gl) {
gl.glFrontFace(GL10.GL_CW);
for (int i = 0; i < 8; i++) {
// Generate one texture pointer…
// …and bind it to our array
//生成一个纹理引用,并把它和当前的数组绑定
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[i]);
//Point to our buffers
//设置纹理坐标
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer[i]);
//Enable the texture state
//加入纹理坐标的权限
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
//Disable the client state before leaving
//取消纹理坐标的权限
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
}
//加载和绑定纹理的方法
public void loadTexture(GL10 gl, Context context) {
//define the resourcesId
int[] resourcesIds = { R.drawable.gou, R.drawable.hou,
R.drawable.laohu, R.drawable.laoshu, R.drawable.tuzi,
R.drawable.xiaolong, R.drawable.xiaoniu, R.drawable.zhu };
gl.glGenTextures(8, textures, 0);
// Get the texture from the Android resource directory
for (int i = 0; i < 8; i++) {
Bitmap bitmap = loadBitmap(context, resourcesIds[i]);
// Generate one texture pointer…
// …and bind it to our array
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[i]);
// Create Nearest Filtered Texture
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
GL10.GL_LINEAR);
// Use the Android GLUtils to specify a two-dimensional texture
// image from our bitmap
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
// Clean up
bitmap.recycle();
}
}
//加载bitmap
public Bitmap loadBitmap(Context context, int resourceid) {
InputStream is = context.getResources().openRawResource(resourceid);
try {
// BitmapFactory is an Android graphics utility for images
return BitmapFactory.decodeStream(is);
} finally {
// Always clear and close
try {
is.close();
is = null;
} catch (IOException e) {
}
}
可以到网上找一些256×256的图片代替文中的图片资源。