Android 开发使用OpenGL ES绘制三棱锥并进行纹理贴图

Kathy ·
更新时间:2024-09-21
· 994 次阅读

效果图:
在这里插入图片描述
直接上代码
MainActivity.java的代码

package com.zzu.shiyan3; import androidx.appcompat.app.AppCompatActivity; import android.opengl.GLSurfaceView; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override protected 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, //1 0.5f, -0.5f, -0.2f, //2 0.0f, 0.5f, 0.0f,//3 -0.5f, -0.5f, -0.2f,//4 0.0f, -0.2f, 0.5f,//5 0.0f, 0.5f, 0.0f,//6 0.5f, -0.5f, -0.2f,//7 0.0f, -0.2f, 0.5f,//8 -0.5f, -0.5f, -0.2f, //9 0.5f, -0.5f, -0.2f, //10 0.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 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); } @Override public 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; } @Override public 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); } ///绘制图形的方法 @Override public 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[]转换为intBuffer public IntBuffer intBufferUtil(int[] arr) { IntBuffer mBuffer; //初始化ByteBuffer,长度为arr的数组的长度*4,一个int占4字节 ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4); //数组的排列用nativeOrder qbb.order(ByteOrder.nativeOrder()); mBuffer = qbb.asIntBuffer(); mBuffer.put(arr); mBuffer.position(0); return mBuffer; } //float[]数组转化为FloatBuffer private FloatBuffer floatBufferUtil(float[] arr) { FloatBuffer mBuffer; //初始化ByteBuffer,长度为arr数组的长度*4, ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4); //数组排列用nativeOrder qbb.order(ByteOrder.nativeOrder()); mBuffer = qbb.asFloatBuffer(); mBuffer.put(arr); mBuffer.position(0); return mBuffer; } }
作者:技动



棱锥 三棱锥 opengl Android

需要 登录 后方可回复, 如果你还没有账号请 注册新账号