1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > Android Studio OpenGL ES绘制三棱锥/四面体的多纹理贴图 每个面使用一张图片渲染

Android Studio OpenGL ES绘制三棱锥/四面体的多纹理贴图 每个面使用一张图片渲染

时间:2021-08-24 02:10:00

相关推荐

Android Studio OpenGL ES绘制三棱锥/四面体的多纹理贴图 每个面使用一张图片渲染

本文参考了王刚的《疯狂Android讲义(第3版)》P554-P559

要求:利用OpenGL ES绘制一个三棱锥,并对每个面进行纹理贴图,每个面使用不同的图片进行渲染。

环境:Android Studio 3.6.1,gradle版本为5.6.4,OpenGL使用1x版本,新建工程后需在res/drawable目录下放4张图片img1、img2、img3、img4(建议图片长宽均是2的n次方,如果不符合则系统会自动调节,我使用的图片长宽是256×256)。

运行结果:

三棱锥多纹理贴图运行结果

主要步骤:

1.在MainActivity.java文件自定义内部公共类MyRenderer实现接口Renderer(也可新建Java文件来实现)。

2.定义三棱锥的顶点坐标、纹理坐标、几个需要用到的Buffer及其他数据等。

// 定义三棱椎的4个顶点private float[] taperVertices = new float[]{0.0f, 0.5f, 0.0f, //0-0.5f, -0.5f, -0.2f, //10.5f, -0.5f, -0.2f, //20.0f, -0.2f, 0.2f //3};// 定义纹理贴图的坐标数据private float[] taperTextures = {0.5000f, 0.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f,0.0000f, 1.0000f, 1.0000f, 1.0000f, 0.5000f, 0.0000f,0.0000f, 1.0000f, 1.0000f, 1.0000f, 0.5000f, 0.0000f,1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.5000f, 0.0000f,};private Context context;private FloatBuffer taperVerticesBuffer;private ByteBuffer[] taperFacts_indices;private FloatBuffer taperTexturesBuffer;// 定义本程序所使用的纹理private int[] textures = new int[4];// 旋转角度float rotate = 0;

定义纹理坐标数据要注意:Android使用的OpenGL ES的纹理坐标系跟官方的OpenGL纹理坐标系统不一样。官方的OpenGL ES纹理坐标为左下角是(0, 0)右上角是(1, 1);而Android的纹理坐标左上角为(0, 0)右下角为(1, 1)。

3.实现MyRenderer(Context main)方法

public MyRenderer(Context main) {this.context = main;// 将三棱椎的顶点位置数据数组包装成FloatBuffertaperVerticesBuffer = floatBufferUtil(taperVertices);// 将三棱椎的4个面的数组indices[1:4]包装成ByteBuffer并加入一个数组中ByteBuffer indices1 = ByteBuffer.wrap(new byte[]{0, 1, 2,0, 0, 0,0, 0, 0,0, 0, 0});ByteBuffer indices2 = ByteBuffer.wrap(new byte[]{0, 0, 0,0, 1, 3,0, 0, 0,0, 0, 0});ByteBuffer indices3 = ByteBuffer.wrap(new byte[]{0, 0, 0,0, 0, 0,1, 2, 3,0, 0, 0});ByteBuffer indices4 = ByteBuffer.wrap(new byte[]{0, 0, 0,0, 0, 0,0, 0, 0,0, 2, 3});taperFacts_indices = new ByteBuffer[]{indices1, indices2, indices3, indices4};// 将立方体的纹理贴图的坐标数据包装成FloatBuffertaperTexturesBuffer = floatBufferUtil(taperTextures);}

4.纹理映射功能默认是关闭的,所以需要在onSurfaceCreated内启用纹理映射功能。

// 启用2D纹理贴图gl.glEnable(GL10.GL_TEXTURE_2D);// 装载纹理loadTexture(gl);

loadTexture()是自定义的一个装载纹理的方法,实现方法如下:

private void loadTexture(GL10 gl) {Bitmap[] bitmap = new Bitmap[4];try {// 加载位图bitmap[0] = BitmapFactory.decodeResource(context.getResources(),R.drawable.img1);bitmap[1] = BitmapFactory.decodeResource(context.getResources(),R.drawable.img2);bitmap[2] = BitmapFactory.decodeResource(context.getResources(),R.drawable.img3);bitmap[3] = BitmapFactory.decodeResource(context.getResources(),R.drawable.img4);// 创建纹理对象,指定生成N个纹理(第一个参数指定生成几个纹理)// textures数组将负责存储所有纹理的代号gl.glGenTextures(4, textures, 0);for (int i = 0; i < textures.length; i++) {// 通知OpenGL将texture纹理绑定到GL10.GL_TEXTURE_2D目标中gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[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[i], 0);}} finally {// 生成纹理之后,回收位图for (int i = 0; i < bitmap.length; i++) bitmap[i].recycle();}}

其中几个函数的含义如下:

void glGenTextures(int n,int[] textures,int offset) //创建纹理对象

n:需要生成的纹理对象个数;textures:保存生成所有纹理对象的数组;offset:纹理对象数组的偏移(生成的纹理从数组什么位置开始赋值)。

void glBindTexture (int target, int texture) //绑定纹理,绑定后的纹理才处于活动状态

target:纹理类型,使用 2D 纹理则参数设为GL_TEXTURE_2D;texture:纹理对象的 ID。

void glTexParameterf(int target,int pname,float param) //对绑定的纹理进行基本属性设置

target:纹理类型,表示我们激活的纹理单元对应了什么样的操作类型,比如GL_TEXTURE_1D、GL_TEXTURE_2D等;pname:属性名称;param:属性值。

void texImage2D (int target, int level, Bitmap bitmap, int border) //指定纹理

target:操作的目标类型,本程序均设为 GL_TEXTURE_2D 即可level:纹理的级别,表示多级分辨率的纹理图象的级数。若只有一种分辨率,level为0。bitmap:使用的图像border:边框,一般设为0

Bitmap decodeResource(Resources res, int id) //加载位图

res:包含图像数据的Resources对象id:图像数据的资源id

5.绘制图像及纹理

//绘制图形的方法@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();// 把绘图中心移入屏幕2个单位gl.glTranslatef(0f, 0.0f, -2.0f);// 旋转图形gl.glRotatef(rotate, -0.1f, -0.1f, 0.05f); // 旋转总坐标系// 设置顶点的位置数据gl.glVertexPointer(3, GL10.GL_FLOAT, 0, taperVerticesBuffer);// 设置贴图的坐标数据gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, taperTexturesBuffer); // ②//使用for循环给三棱锥每个面都贴上相应的纹理图for(int i = 0; i < taperFacts_indices.length; i++) {// 绑定纹理,执行纹理贴图gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[i]);// 按taperFacts_indices[i]指定的面绘制三角形gl.glDrawElements(GL10.GL_TRIANGLES, 3*(i+1), GL10.GL_UNSIGNED_BYTE, taperFacts_indices[i]);}// 绘制结束gl.glFinish();// 禁用顶点、纹理坐标数组gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);//角度+1使绘制的图形旋转显示其他面rotate += 1;}

void glTexCoordPointer(int size, int type, int stride, Buffer pointer) //设置顶点数组为纹理坐标缓存

size:纹理顶点坐标的分量个数,本程序使用纹理坐标的2个分量(s, t)映射顶点坐标的3个分量(x, y, z);type:纹理坐标的数据类型,short, int, float, double都可以;stride:位图的宽度,可以理解为相邻的两个纹理之间跨多少个字节,一般为0,因为一般不会在纹理中再添加其他的信息;pointer:存放纹理坐标的数组。

总结:如果想多面体各个面的纹理不一样,则在画每个面之前要加上void glBindTexture (int target, int texture)函数绑定纹理。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。