本节书摘来自异步社区《Android 应用案例开发大全(第二版)》一书中的第2章,第2.4节壁纸的实现,作者 吴亚峰 , 于复兴 , 杜化美,更多章节内容可以访问云栖社区“异步社区”公众号查看
2.4 壁纸的实现Android 应用案例开发大全(第二版)上一节介绍了壁纸的框架,让读者对壁纸的整体框架有了初步认识,本节将要对壁纸实现服务类GLWallpaperService、动态壁纸类LiveWallpaper、自定义渲染器类TDRender的开发进行详细介绍。
2.4.1 壁纸服务类——GLWallpaperService该类是本项目中最基础的一个类,没有这个类就不可能使用壁纸这个功能。这个类为开发人员提供了壁纸服务,开发人员可以通过继承该类,重写此类中的方法来实现壁纸的后续开发。本小节只对该类中的两个地方进行介绍,一个是setRender方法,另一个是触控的响应事件onTouchEvent。
(1)首先对setRender方法进行介绍,具体代码如下所示。
1 public void setRenderer(Renderer renderer) { 2 checkRenderThreadState(); // 检查线程是否启动 3 if (mEGLConfigChooser == null) { // 创建EGLConfigChooser 4 mEGLConfigChooser = new SimpleEGLConfigChooser(true); 5 } 6 if (mEGLContextFactory == null) { // 创建EGLContextFactory 7 mEGLContextFactory = new DefaultContextFactory(); 8 } 9 if (mEGLWindowSurfaceFactory == null) { // 创建EGLWindowSurfaceFactory 10 mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory(); 11 } 12 mGLThread = new GLThread(renderer, mEGLConfigChooser, 13 mEGLContextFactory, mEGLWindowSurfaceFactory, mGLWrapper); 14 mGLThread.start(); // 启动线程 15 this.renderer=(TDRender) renderer; // 设置渲染器 16 }第2~8行为检查渲染器线程是否启动,然后判断是否已经创建了EGLConfigChooser对象,如果没有则创建,再判断是否已创建EGLContextFactory对象,同样如果没有则创建。第9~16行先判断是否已经创建了EGLWindowSurfaceFactory对象,如果没有则创建。然后启动渲染线程,再将渲染器设置为自定义的渲染器。(2)下面将对屏幕触控的相应事件进行介绍,屏幕的触控事件分为3部分:第一部分是滑动屏幕使背景图跟着屏幕移动,第二部分是点击屏幕下方修改标志位,最后一部分是手指抬起时判断是否进行喂食,具体代码如下所示。
1 private float mPreviousX; // 上次的触控位置_y_坐标 2 private TDRender renderer; // 场景渲染器 3 @Override 4 public void onTouchEvent(MotionEvent e) { 5 float y = e.getY(); // 获得触控点的_y_坐标 6 float x = e.getX(); // 获得触控点的_x_坐标 7 switch (e.getAction()) { 8 case MotionEvent.ACTION_DOWN: 9 Constant.feeding = true; // 将喂食标志位设为true 10 break; 11 case MotionEvent.ACTION_MOVE: 12 float dy = y - mPreviousY; // 计算触控笔_y_位移 13 float dx = x - mPreviousX; // 计算触控笔_y_位移 14 if (dx < 0){ // 触摸左边_x_为正,触摸右边_x_为负 15 if (Constant.CameraX < Constant.MaxCameraMove) {// 摄像机移动最大值 16 if(dx<- Constant.Thold){ 17 Constant.feeding = false; // 喂食标志位 18 } 19 Constant.CameraX = Constant.cameraX - dx / Constant.Camera ove_SCALE ; 20 Constant.TargetX=Constant.CameraX; 21 }} else { 22 if (Constant.CameraX < -Constant.MaxCameraMove {// 摄像机移动最大值 23 if(dx> Constant.Thold){ 24 Constant.feeding = false; // 喂食标志位 25 } 26 Constant.CmeraX = Constant.CameraX - dx / Constant.CameraMove _SCALE ; 27 Constant.TargetX=Constant.CameraX; 28 }} 29 MatrixUtil.setCamera ( // 将摄像机的位置信息存入到矩阵中 30 Constant.CameraX, // 摄像机_x_位置 31 Constant.CameraY, // 摄像机_y_位置 32 Constant.CameraZ, // 摄像机_y_位置 33 Constant.TargetX, // 观测点的_x_位置 34 Constant.TargetY, // 观测点的_y_位置 35 Constant.TargetZ, // 观测点的_z_位置 36 Constant.UpX, // UP向量的x分量 37 Constant.UpY, // UP向量的y分量 38 Constant.UpZ); // UP向量的z分量 39 break; 40 case MotionEvent.ACTION_UP: 41 if (Constant.feeding) { // 标志位,如果开始喂食 42 if (Constant.isFeed) { 43 Constant.isFeed = false; // 把标志位置为false 44 Vector3f[]AB=IntersectantUtil 45 .calculateABPosition( // 通过变换获得世界坐标系中的点 46 x, // 触控点_x_坐标 47 y, // 触控点_y_坐标 48 Constant.SCREEN_WIDTH,// 屏幕宽度 49 Constant.SCREEN_HEGHT, // 屏幕长度 50 Constant.leftABS, // 视角left绝对值 51 Constant.topABS, // 视角top绝对值 52 Constant.nearABS, // 视角near值 53 Constant.farABS); // 视角far值 54 Vector3f Start = AB[0]; // 获得拾取后世界坐标系中被触控到的近平面的点 55 Vector3f End = AB[1];// 获得拾取后世界坐标系中被触控到的远平面的点 56 if (renderer.feedFish != null) { // 判断不为空,则开始喂食 57 renderer.feedFish.startFeed(Start, End); // 开始喂食 58 }}} 59 requestRender(); // 重绘画面 60 break; 61 } 62 mPreviousX = x; // 记录触控笔位置 63 super.onTouchEvent(e); 64 }}第1~10行定义上次触控点的x、y坐标,创建渲染器对象,获得触控点的x坐标和y坐标,对ACTION_DOWN事件进行监听,当触发时将喂食标志位设为true。第11~21行获得手指在屏幕上的触控点,从而得到手指在屏幕上的移动距离,然后按照一定比例移动摄像机x坐标,同时,如果摄像机x坐标达到阈值,则摄像机不会向滑动方向移动。第22~32行将摄像机的位置信息存入到矩阵中,设置摄像机位置的坐标、观测点的坐标和up向量,最后再次记录触控笔的x、y位置。第33~46行为判断喂食的标志位,喂食有两个标志位,一个是在点击喂食的时候为true,另一个是在没有喂食之前为true,因为滑动屏幕不能喂食,当前喂的食物在没有消失之前也不能喂食,所以,用了两个标志位对其进行控制。然后通过矩阵变化获取触控点的世界坐标系坐标。第47~57行通过拾取计算得到触控点在世界坐标系中的起点(近平面点)、终点(远平面点)坐标,并且开始进行喂食。然后重绘画面,记录触控笔的位置,回调父类的方法。
2.4.2 动态壁纸类——LiveWallpaper只有壁纸的服务类还远远不够,还要新建一个类继承自壁纸服务类,然后重写壁纸服务类的onCreateEngine方法才行,具体代码如下所示。
1 package com.bn.ld.wallpaper; 2 import com.bn.gl.GLWallpaperService; 3 public class LiveWallpaper extends GLWallpaperService{ 4 private TDRender renderer; // 场景渲染器 5 @Override 6 public Engine onCreateEngine() { // 重写onCreateEngine方法 7 renderer=new TDRender(this); 8 return new GLEngine() { { 9 setRenderer(renderer); // 设置渲染器 10 setRenderMode(RENDERMODE_CONTINUOUSLY); // 设置主动渲染 11 }};}}说明定义渲染器类,继承自GLWallpaperService类 ,创建私有的场景渲染器对象。然后重写onCreateEngine方法,将渲染器设置为自定义的渲染器,并且把渲染器的渲染模式设置为主动渲染。
2.4.3 自定义渲染器类——TDRender下面将介绍自定义的渲染器代码,在自定义的渲染器类里,可以进行鱼、鱼群、气泡、水草、背景图、石头、鱼食的绘制和初始化纹理等。
(1)由于该类中绘制代码比较多,在此就先介绍整个渲染器的框架,具体代码如下所示。
1 package com.bn.ld.wallpaper; 2 ……//此处省略部分类和包的引入代码,读者可自行查阅光盘的源代码 3 public class TDRender extends GLSurfaceView implements GLSurfaceView.Renderer, 4 GLWallpaperService.Renderer { 5 public TDRender(Context context) { 6 super(context); // 获得上下文对象 7 } 8 ……//此处省略相关成员变量的声明代码,读者可自行查阅光盘的源代码 9 public void onDrawFrame(GL10 gl) { 10 gl.glEnable(GL10.GL_CULL_FACE); // 设置为打开背面剪裁 11 gl.glShadeModel(GL10.GL_SMOOTH); // 设置着色模型为平滑着色 12 gl.glFrontFace(GL10.GL_CCW); // 设置为默认卷绕顺序逆时针 13 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | 14 GL10.GL_DEPTH_BUFFER_BIT); // 清除缓存 15 gl.glMatrixMode(GL10.GL_MODELVIEW); // 设置当前矩阵为模式矩阵 16 gl.glLoadIdentity(); // 设置当前矩阵为单位矩阵 17 GLU.gluLookAt( // 设置摄像机 18 gl, 19 Constant.CameraX, // 摄像机_x_位置 20 Constant.CameraY, // 摄像机_y_位置 21 Constant.CameraZ, // 摄像机_z_位置 22 Constant.TargetX, // 观测点的_x_位置 23 Constant.TargetY, // 观测点的_y_位置 24 Constant.TargetZ, // 观测点的_z_位置 25 Constant.UpX, // UP向量x分量 26 Constant.UpY, // UP向量y分量 27 Constant.UpZ); // UP向量z分量 28 ……此处代码是绘制代码,将在后面给出 29 } 30 public void onSurfaceChanged(GL10 gl, int width, int height) { 31 gl.glViewport(0, 0, width, height); // 设置视窗大小及位置 32 gl.glMatrixMode(GL10.GL_PROJECTION); // 设置当前矩阵为投影矩阵 33 gl.glLoadIdentity(); // 设置当前矩阵为单位矩阵 34 float ratio = (float) width / height; // 计算透视投影的比例 35 Constant.SCREEN_HEGHT=height; // 获取手机的高 36 Constant.SCREEN_WIDTH=width; // 获取手机的宽 37 Constant.leftABS=ratio*Constant.View_SCALE; // 透视投影的left绝对值 38 Constant.topABS=1 * Constant.View_SCALE; // 透视投影的top绝对 39 Constant.SCREEN_SCALEX=Constant.View_SCALE*((ratio>1)?ratio:(1/ratio)); 40 gl.glFrustumf(-Constant.leftABS, Constant.leftABS, -Constant.topABS, // 产生透视投影矩阵 41 Constant.topABS, Constant.nearABS,Constant.farABS); 42 MatrixUtil.setCamera ( // 将摄像机的位置信息存入到矩阵中 43 Constant.CameraX, // 摄像机_x_位置 44 Constant.CameraY, // 摄像机_y_位置 45 Constant.CameraZ, // 摄像机_z_位置 46 Constant.TargetX, // 观测点的_x_位置 47 Constant.TargetY, // 观测点的_y_位置 48 Constant.TargetZ, // 观测点的_z_位置 49 Constant.UpX, // UP向量的x分量 50 Constant.UpY, // UP向量的y分量 51 Constant.UpZ); // UP向量的z分量 52 if (backgrounds == null) { 53 backgrounds = new BackGround(); // 初始化背景图 54 }} 55 public void onSurfaceCreated(GL10 gl, EGLConfig config) { 56 gl.glDisable(GL10.GL_DITHER); // 关闭抗抖动 57 gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, 58 GL10.GL_FASTEST); // 快速模式 59 gl.glClearColor(0, 0, 0, 0); // 设置屏幕背景色黑色RGBA 60 gl.glEnable(GL10.GL_DEPTH_TEST); // 启用深度测试 61 gl.glEnable(GL10.GL_CULL_FACE); // 设置为打开背面剪裁 62 ……此处代码是加载图片、OBJ文件、创建各种对象,将在后面介绍 63 }}第1~8行为部分类和包的引入代码、相关成员变量的声明,这些代码在此处省略,读者可自行查阅光盘代码。创建构造器并获得上下文对象。第9~29行为进行绘制时的各种设置,如打开背面剪裁、设置平滑着色、设置默认卷绕方式、设置模式矩阵和单位矩阵,最后设置摄像机位置,初始化背景图对象。第30~54行重写onSurfaceChanged方法设置视口的大小,将当前矩阵设为投影矩阵、设置单位矩阵、产生透视投影矩阵、并且生成摄像机观察矩阵。第55~63行重写onSurfaceCreated方法。关闭抗抖动、设置Hint模式为快速模式、设置屏幕背景颜色、启用深度检测、打开背面剪裁。(2)下面开始对本案例中鱼类、气泡、鱼食、石头等对象的创建以及本案例中用到的纹理ID的初始化进行详细介绍,具体代码如下所示。
1 public void onSurfaceCreated(GL10 gl, EGLConfig config) { 2 gl.glDisable(GL10.GL_DITHER); // 关闭抗抖动 3 gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, 4 GL10.GL_FASTEST); // 快速模式 5 gl.glClearColor(0, 0, 0, 0); // 设置屏幕背景色 6 gl.glEnable(GL10.GL_DEPTH_TEST); // 启用深度测试 7 gl.glEnable(GL10.GL_CULL_FACE); 8 if (fishAl.size() == 0) { 9 bubbles = initTexture(gl, "bubble.png"); // 气泡的纹理ID 10 fish0 = initTexture(gl, "fish0.png"); // 鱼的纹理ID 11 fish1 = initTexture(gl, "fish1.png"); 12 fish2 = initTexture(gl, "fish2.png"); 13 fish3 = initTexture(gl, "fish3.png"); 14 fish4 = initTexture(gl, "fish4.png"); 15 fish5 = initTexture(gl, "fish5.png"); 16 fishfood = initTexture(gl, "fishfood.png"); // 鱼食的纹理ID 17 waterweeds = initTexture(gl, "waterweeds.png");// 水草的纹理ID 18 background = initTexture(gl, "background.png");// 背景的纹理ID 19 stone=initTexture(gl, "stone.png"); // 石头的纹理 20 fishAl.add(new SingleFish(fish1, TDRender.this, "fish1.obj", // 将单个鱼加入鱼类列表 21 new Vector3f(-1, 2, 0), // 初始位置 22 new Vector3f(-0.02f, -0.02f, 0.00f), new Vector3f(0, 0, 0), // 初始速度和外力 23 new Vector3f(0, 0, 0), 70)); // 初始吸引力和鱼的质量 24 fishAl.add(new SingleFish(fish0, TDRender.this, "fish0.obj", 25 new Vector3f(-7, 5, 0), // 初始位置 26 new Vector3f(-0.04f, 0.01f, -0.04f),new Vector3f(0, 0, 0), // 初始速度和外力 27 new Vector3f(0, 0, 0), 150)); // 初始吸引力和鱼的质量 28 fishAl.add(new SingleFish(fish2, TDRender.this, "fish2.obj", 29 new Vector3f(-0, 3, 0), // 初始位置 30 new Vector3f(0.02f, 0.01f, -0.01f),new Vector3f(0, 0, 0), // 初始速度和外力 31 new Vector3f(0, 0, 0), 70)); // 初始吸引力和鱼的质量 32 fishAl.add(new SingleFish(fish4, TDRender.this, "fish4.obj", 33 new Vector3f(-1, 0, 0), // 初始位置 34 new Vector3f(-0.03f, 0.02f, -0.02f), new Vector3f(0, 0, 0), // 初始速度和外力 35 new Vector3f(0, 0, 0), 90)); // 初始吸引力和鱼的质量 36 fishAl.add(new SingleFish(fish4, TDRender.this, "fish4.obj", 37 new Vector3f(-5, 0, 0), // 初始位置 38 new Vector3f(-0.02f, 0.03f, -0.02f), new Vector3f(0, 0, 0), // 初始速度和外力 39 new Vector3f(0, 0, 0), 50)); // 初始吸引力和鱼的质量 40 fishAl.add(new SingleFish(fish4, TDRender.this, "fish4.obj", 41 new Vector3f(-5, 3, 0), // 初始位置 42 new Vector3f(-0.01f, 0.02f, -0.04f), new Vector3f(0, 0, 0), // 初始速度和外力 43 new Vector3f(0, 0, 0), 60)); // 初始吸引力和鱼的质量 44 fishAl.add(new SingleFish(fish5, TDRender.this, "fish5.obj", 45 new Vector3f(1, -3, 0), // 初始位置 46 new Vector3f(0.03f, 0.01f, -0.03f),new Vector3f(0, 0, 0f), // 初始速度和外力 47 new Vector3f(0, 0, 0), 80)); // 初始吸引力和鱼的质量 48 fishAl.add(new SingleFish(fish5, TDRender.this, "fish5.obj", 49 new Vector3f(-4, -1, 0), // 初始位置 50 new Vector3f(0.045f, 0.02f, -0.05f),new Vector3f(0, 0, 0f), // 初始速度和外力 51 new Vector3f(0, 0, 0), 40)); // 初始吸引力和鱼的质量 52 } 53 if (waterweeds1 == null) { // 创建水草类对象 54 waterweeds1 = LoadUtil.loadFromFileVertexOnly("waterweeds.obj", 55 this.getResources()); // 加载水草模型 56 } 57 if(stones==null){ // 创建石头类对象 58 stones=LoadUtil.loadFromFileVertexOnly("stone.obj", 59 this.getResources()); // 加载石头模型 60 } 61 if (fishfoods == null) { // 创建鱼食对象 62 fishfoods = LoadUtil.loadFromFileVertexOnly("fishfood.obj", 63 this.getResources()); // 加载鱼食模型 64 } 65 if (bubbleControl == null) { // 创建气泡的Control对象 66 bubbleControl = new BubbleControl(bubbles); 67 } 68 if (fishControl == null) { // 创建鱼类的Control对象 69 fishControl = new FishControl(fishAl, TDRender.this); 70 } 71 if (fishSchool == null) { // 创建鱼群的Control 72 fishSchool = new FishSchoolControl(fish3, TDRender.this); 73 } 74 if (singleFood == null) { // 食物类 75 singleFood = new FeedFish(fishfoods, TDRender.this); 76 } 77 if (feedFish == null) { // 创建控制喂食的对象 78 feedFish = new SingleFood(TDRender.this); 79 }}第2~7行为关闭抗抖动,将Hint的模式设置为快速模式,设置屏幕背景颜色为黑色,启用深度检测,打开背面剪裁。第8~52行获得图片的纹理ID ,对单个鱼进行初始化设置鱼的初始位置、初始速度、受到的外力和吸引力,以及鱼的质量(力的缩放比)。第53~79行创建背景图、水草、石头、气泡、鱼食、群鱼、鱼群对象。并且只有当这些对象为空的时候才会对这些对象进行实例化。(3)下面开始对3D水族馆动态壁纸案例中各个对象的绘制进行介绍,绘制代码在onDrawFrame方法中,具体代码如下所示。
1 public void onDrawFrame(GL10 gl) { 2 gl.glEnable(GL10.GL_CULL_FACE); // 设置为打开背面剪裁 3 gl.glShadeModel(GL10.GL_SMOOTH); // 设置着色模型为平滑着色 4 gl.glFrontFace(GL10.GL_CCW); // 设置为默认卷绕顺序逆时针为正 5 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // 清除颜色缓存 6 gl.glMatrixMode(GL10.GL_MODELVIEW); // 设置当前矩阵为模式矩阵 7 gl.glLoadIdentity(); // 设置矩阵为单位矩阵 8 GLU.gluLookAt( gl, // 设置摄像机 9 Constant.CameraX, // 摄像机_x_位置 10 Constant.CameraY, // 摄像机_y_位置 11 Constant.CameraZ, // 摄像机_z_位置 12 Constant.TargetX, // 观测点的_x_位置 13 Constant.TargetY, // 观测点的_y_位置 14 Constant.TargetZ, // 观测点的_z_位置 15 Constant.UpX, // UP向量x分量 16 Constant.UpY, // UP向量y分量 17 Constant.UpZ); // UP向量z分量 18 gl.glPushMatrix(); // 保护矩阵 19 if (backgrounds != null) { // 背景图不为空就绘制 20 backgrounds.drawSelf(gl,background); // 绘制背景图 21 } 22 gl.glPopMatrix(); // 恢复矩阵 23 gl.glPushMatrix(); // 保护矩阵 24 if (waterweeds1 != null&&stones!=null) { // 如果没有绘制过水草和石头则绘制 25 gl.glPushMatrix(); // 左后边的水草、石头 26 gl.glTranslatef(-17, -5.5f,-14); // 平移坐标系 27 stones.drawSelf(gl,stone); // 绘制石头 28 gl.glTranslatef(1.5f, 0, 1); // 平移坐标系 29 waterweeds1.drawSelf(gl,waterweeds); // 绘制水草 30 gl.glTranslatef(2, 0, 1); // 平移坐标系 31 gl.glPushMatrix(); // 保护矩阵 32 gl.glScalef(1.5f, 1.5f, 1.5f); // 设置缩放 33 stones.drawSelf(gl,stone); // 绘制石头 34 gl.glPopMatrix(); // 恢复矩阵 35 ……下面的水草和石头的绘制和上面的类似就不再给出程序,读者可自行查看源代码 36 } 37 gl.glPopMatrix(); // 恢复矩阵 38 gl.glPushMatrix(); // 保护矩阵 39 if (fishControl != null) { 40 fishControl.drawSelf(gl); // 绘制鱼 41 } 42 if (fishSchool != null) { 43 fishSchool.drawSelf(gl); // 绘制鱼群 44 } 45 gl.glPopMatrix(); // 恢复矩阵 46 gl.glPushMatrix(); // 保护矩阵 47 if (singleFood != null) { 48 singleFood.drawSelf(gl); // 绘制鱼食 49 } 50 gl.glPopMatrix(); // 恢复矩阵 51 gl.glPushMatrix(); // 保护矩阵 52 gl.glEnable(GL10.GL_BLEND); // 开启混合 53 gl.glBlendFunc(GL10.GL_SRC_ALPHA, 54 GL10.GL_ONE_MINUS_SRC_ALPHA); // 设置源混合因子与目标混合因子 55 gl.glTranslatef(0, 0, 15f); // 平移 56 if (bubbleControl != null) { 57 bubbleControl.drawSelf(gl); // 绘制气泡 58 } 59 gl.glDisable(GL10.GL_BLEND); // 关闭混合 60 gl.glPopMatrix(); // 恢复矩阵 61 }第1~17行设置背面剪裁、卷绕方式、平滑着色、清除颜色缓存和深度缓存,设置当前矩阵为模式矩阵,设置当前矩阵为单位矩阵,设置摄像机位置。第18~36行绘制背景图、水草和石头。由于水草和石头要重复绘制很多次,在此就不一一列举了,只给了出其中的部分代码,其余代码读者可自行查看光盘中的源代码。第37~61行绘制群鱼、鱼群、鱼食和气泡。因为气泡是半透明的,所以要开启混合(绘制气泡时要根据气泡的位置对气泡进行排序),并且要将气泡在最后绘制,最后不要忘了关闭混合。(4)最后将对初始化纹理的initTexture方法进行介绍,具体代码如下所示。
1 public int initTexture(GL10 gl, String pname){ // 初始化纹理 2 int[] textures = new int[1]; // 纹理ID 3 gl.glGenTextures(1, textures, 0); // 生成纹理ID 4 int currTextureId = textures[0]; 5 gl.glBindTexture(GL10.GL_TEXTURE_2D, currTextureId); // 绑定纹理 6 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, 7 GL10.GL_NEAREST); // 最近点采样 8 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, 9 GL10.GL_LINEAR); // 线性纹理过滤 10 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, 11 GL10.GL_CLAMP_TO_EDGE); // 纹理的横向拉伸方式 12 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, 13 GL10.GL_CLAMP_TO_EDGE); // 纹理的纵向拉伸方式 14 InputStream in = null; // 创建输入流 15 try { 16 in = this.getResources().getAssets().open(pname); // 加载纹理图片 17 } catch (IOException e1) { 18 e1.printStackTrace(); // 异常处理 19 } 20 Bitmap bitmapTmp; // 创建bitmap对象 21 try { 22 bitmapTmp = BitmapFactory.decodeStream(in); // 对获取的图片解码 23 } finally { 24 try { 25 in.close(); // 关闭输入流 26 } catch (IOException e) { 27 e.printStackTrace(); // 异常处理 28 }} 29 GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmapTmp, 0); // 指定纹理 30 bitmapTmp.recycle(); // 释放bitmap 31 return currTextureId; 32 }第2~13行定义纹理ID、生成纹理ID、绑定纹理、设置纹理的过滤方式分为最近点采样和线性纹理过滤,设置纹理的拉伸方式分为横向拉伸方式和纵向拉伸方式。第14~32行创建输入流,读取纹理图片,进行异常处理。创建bitmap对象,对获取的图片进行解码,关闭输入流,异常处理,最后释放bitmap。
相关资源:Android 3D游戏开发技术宝典——OpenGL ES 2.0 (吴亚峰) 源代码