《Android 应用案例开发大全(第二版)》——2.4节壁纸的实现

Android动态壁纸开发
本文介绍Android平台上动态壁纸的实现过程,包括服务类GLWallpaperService、动态壁纸类LiveWallpaper和自定义渲染器类TDRender的开发细节。涵盖触控响应、纹理初始化及各类对象的绘制。

本节书摘来自异步社区《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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值