效果图:
直接上代码
MainActivity.java的代码
package com.zzu.shiyan3;import androidx.appcompat.app.AppCompatActivity;import android.opengl.GLSurfaceView;import android.os.Bundle;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//定义GLSurfaceView,绘制的三棱锥在里边显示GLSurfaceView gview = new GLSurfaceView(this);//自定义的的Render类实现了GLSurfaceView.Renderer接口MyRender render = new MyRender(this);gview.setRenderer(render);setContentView(gview);}}
自定义类MyRender.java的代码:
package com.zzu.shiyan3;import java.util.ArrayList;import java.nio.FloatBuffer;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.opengl.GLSurfaceView;import android.opengl.GLUtils;import java.nio.ByteBuffer;import java.nio.ByteOrder;import java.nio.IntBuffer;import java.util.List;import javax.microedition.khronos.egl.EGLConfig;import javax.microedition.khronos.opengles.GL10;public class MyRender implements GLSurfaceView.Renderer {float rotate = 0;//旋转角度//定义三棱锥4个面,每个顶点重复设置3次,表示同一个顶点的每个下标只能在表示一个面,这样定义是为了贴图时使用,这12行对应的是12个顶点的下标,在taperFacts中要使用。float[] taperDate = new float[]{0.0f, 0.5f, 0.0f, //0-0.5f, -0.5f, -0.2f, //10.5f, -0.5f, -0.2f, //20.0f, 0.5f, 0.0f,//3-0.5f, -0.5f, -0.2f,//40.0f, -0.2f, 0.5f,//50.0f, 0.5f, 0.0f,//60.5f, -0.5f, -0.2f,//70.0f, -0.2f, 0.5f,//8-0.5f, -0.5f, -0.2f, //90.5f, -0.5f, -0.2f, //100.0f, -0.2f, 0.5f //11};/**这里的4个taperFacts定义的是绘制三棱锥的4个面存放的下标,调用绘制三棱锥的方法 gl.glDrawElements(GL10.GL_TRIANGLES, 3 * (i + 1),GL10.GL_UNSIGNED_BYTE, taperFacetsBuffer[i]);时使用。这里分成了4个是为了每个面贴不同的图使用。*/private byte[] taperFacts1 = new byte[]{0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0};private byte[] taperFacts2 = new byte[]{0, 0, 0, 3, 4, 5, 0, 0, 0, 0, 0, 0};private byte[] taperFacts3 = new byte[]{0, 0, 0, 0, 0, 0, 6, 7, 8, 0, 0, 0};private byte[] taperFacts4 = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10, 11};//定义纹理贴图坐标数组,这里分为四组(四个面),每组3对(面的三个坐标),要贴的图的每个坐标与每个面的顶点是一一对应关系private float[] taperTextures = new float[]{0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f,0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f};//将上边定义的数组均转换为FloatBuffer,ByteBuffer以便于OpenGL ES的方法调用。FloatBuffer taperDataBuffer;ByteBuffer[] taperFacetsBuffer = new ByteBuffer[4];FloatBuffer taperTexturesBuffer;MainActivity context;//定义List将taperFacts放入其中List<byte[]> list = new ArrayList<>();int[] texs;//存放纹理贴图对象的ID/**贴图用的图片,建议width和height都是2的n次方,如果贴图尺寸不符合上述规定的话,系统会自动调节它们的大小,这样就有可能是占用比图片本身尺寸还要大的图片。*/int[] images = new int[]{R.drawable.img1, R.drawable.img2, R.drawable.img3, R.drawable.img4};public MyRender(MainActivity mainActivity) {this.context = mainActivity;list.add(taperFacts1);list.add(taperFacts2);list.add(taperFacts3);list.add(taperFacts4);for (int i = 0; i < 4; ++i) {//将byte型的数组taperFacts转换为ByteBuffer,下边的类似taperFacetsBuffer[i] = ByteBuffer.wrap(list.get(i));}taperDataBuffer = floatBufferUtil(taperDate);taperTexturesBuffer = floatBufferUtil(taperTextures);}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {gl.glDisable(GL10.GL_DITHER);gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);gl.glClearColor(0, 0, 0, 0);//设置阴影平滑模式gl.glShadeModel(GL10.GL_SMOOTH);//启用深度测试gl.glEnable(GL10.GL_DEPTH_TEST);//设置深度类型gl.glDepthFunc(GL10.GL_LEQUAL);//启用2D纹理贴图gl.glEnable(GL10.GL_TEXTURE_2D);//加载纹理贴图对象ID的数组texs = loadTexture(gl, images);}//自定义的创建按纹理贴图对象和设置贴图属性的方法private int[] loadTexture(GL10 gl, int[] images) {int[] texs;//存放纹理对象ID的数组Bitmap bitmap = null;try {IntBuffer textureBuffer = IntBuffer.allocate(4);// 创建N个纹理对象(下边第一个参数指定生成4个纹理纹理对象)// textureBuffer数组将负责存储所有纹理对象的ID号gl.glGenTextures(4, textureBuffer);texs = textureBuffer.array();//将纹理对象的ID号放入texs//设置4个纹理对象的属性for (int i = 0; i < texs.length; i++) {// 加载位图bitmap = BitmapFactory.decodeResource(context.getResources(),images[i]);// 通知OpenGL将texture纹理绑定到GL10.GL_TEXTURE_2D目标中gl.glBindTexture(GL10.GL_TEXTURE_2D, texs[i]);// 设置纹理被缩小(距离视点很远时被缩小)时的滤波方式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);// 设置在横向、纵向上都是平铺纹理gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,GL10.GL_REPEAT);gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,GL10.GL_REPEAT);// 加载位图生成纹理GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);}} finally {// 生成纹理之后,回收位图if (bitmap != null)bitmap.recycle();}return texs;}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {//设置3D视窗的大小及位置gl.glViewport(0, 0, width, height);//将当前矩阵设置为投影矩阵gl.glMatrixMode(GL10.GL_PROJECTION);//初始化单位矩阵gl.glLoadIdentity();//计算透视窗的宽度、高度比float ratio = (float) width / height;//设置透视窗的空间大小gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);}///绘制图形的方法@Overridepublic void onDrawFrame(GL10 gl) {//清除屏幕缓存和深度缓存gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);//启动顶点坐标数据gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);//启用贴图坐标数据gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);//设置当前矩阵堆栈为模型堆栈gl.glMatrixMode(GL10.GL_MODELVIEW);//重置当前的模型视图矩阵gl.glLoadIdentity();//设置当前绘制的几何体的中心位置gl.glTranslatef(0f, 0f, -1.7f);//设置几何体旋转的角度gl.glRotatef(rotate, 0f, 0.2f, 0f);//设置几何体的顶点坐标矩阵gl.glVertexPointer(3, GL10.GL_FLOAT, 0, taperDataBuffer);// 设置贴图的顶点坐标矩阵gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, taperTexturesBuffer);for (int i = 0; i < texs.length; i++) {//贴图gl.glBindTexture(GL10.GL_TEXTURE_2D, texs[i]);//绘制taperFacetsBuffer指定的三角形gl.glDrawElements(GL10.GL_TRIANGLES, 3 * (i + 1),GL10.GL_UNSIGNED_BYTE, taperFacetsBuffer[i]);}gl.glFinish();gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);//改变角度使绘制的图形旋转rotate += 1;}//将int[]转换为intBufferpublic IntBuffer intBufferUtil(int[] arr) {IntBuffer mBuffer;//初始化ByteBuffer,长度为arr的数组的长度*4,一个int占4字节ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);//数组的排列用nativeOrderqbb.order(ByteOrder.nativeOrder());mBuffer = qbb.asIntBuffer();mBuffer.put(arr);mBuffer.position(0);return mBuffer;}//float[]数组转化为FloatBufferprivate FloatBuffer floatBufferUtil(float[] arr) {FloatBuffer mBuffer;//初始化ByteBuffer,长度为arr数组的长度*4,ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);//数组排列用nativeOrderqbb.order(ByteOrder.nativeOrder());mBuffer = qbb.asFloatBuffer();mBuffer.put(arr);mBuffer.position(0);return mBuffer;}}