Android 3D系列之纹理篇

Android 3D系列之纹理篇

[日期:2011-09-25] 来源:Linux社区  作者:hmg25
Android 3D系列之纹理篇_第1张图片

之前的例子都是纯色的3D模型。今天我们就开始给这些模型贴上华丽丽的纹理,让他们看起来更加接近我们印象中的真实物体。

相关阅读:

Android 3D 系列之光效篇 http://www.linuxidc.com/Linux/2011-09/43749.htm

Android 3D系列之入门实践篇 http://www.linuxidc.com/Linux/2011-09/43751.htm

Android 3D 系列之纹理篇 http://www.linuxidc.com/Linux/2011-09/43753.htm

Android 3D系列之基本概念篇 http://www.linuxidc.com/Linux/2011-09/43752.htm

功能启动

为使用纹理,我们需要打开OpenGL的一些开关以启动我们需要的一些功能:

 gl.glEnable(GL10.GL_TEXTURE_2D);
。这个调用是必不可缺的;如果你没有打开此功能,那么你就无法将图像映射到多边形上。它可以在需要时打开和关闭,通常在初始化时打开。

创建纹理

生成纹理

OpenGL 中的纹理通过一个唯一号引用,通过函数 glBindTexture() 实现。你 可以自己指定这个唯一号,或者通过调用 glGenTextures () 函数生成一个唯一 号。

int[] tmp_tex = new int[1];//尽管只有一个纹理,但使用一个元素的数组
//glGenTextures(申请个数,存放数组,偏移值)
gl.glGenTextures(1, tmp_tex, 0); //向系统申请可用的,用于标示纹理的ID
               int texture = tmp_tex[0];

纹理绑定

在为纹理生成名称后,在为纹理提供图像数据之前,我们必须绑定纹理。绑定使得指定纹理处于活动状态。一次只能激活一个纹理。活动的或“被绑定”的纹理是绘制多边形时使用的纹理,也是新纹理数据将加载其上纹理,所以在提供图像数据前必须绑定纹理。

 gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);

绑定纹理数据,传入指定图片
   GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);   

纹理限制

   用于纹理的图像宽和高必须为乘方,比如 2, 4, 8, 16, 32, 64, 128, 256, 512, 或 1024。例如图像可能为 64×128 或 512×512。

纹理坐标

当纹理映射启动后绘图时,你必须为OpenGL ES提供其他数据,即顶点数组中各顶点的 纹理坐标。纹理坐标定义了图像的哪一部分将被映射到多边形。它的工作方式有点奇怪。与我们顶点坐标方向不一致,假设你有一个正方形或长方形的纹理,其左下角为二维平面的原点,高和宽的单位为一。像这样:

Android 3D系列之纹理篇_第2张图片

 

这就是我们的“纹理坐标系统”,不使用x 和 y 来代表二维空间,我们使用 s 和 t 作为纹理坐标轴,但原理上是一样的。

除了 s 和 t 轴外,被映射的纹理在多边形同样有两个轴,它们称为 u 和 v轴。这是源于许多3D图像程序中的UV 映射 的术语。

Android 3D系列之纹理篇_第3张图片

 

好,我们明白了纹理坐标系统,我们现在讨论怎样使用这些纹理坐标。当我们指定顶点数组中的顶点时,我们需要在另一个数组中提供纹理坐标,它称为纹理坐标数组。 每个顶点,我们使用float 来指定顶点在上图所示坐标系统的位置。让我们看看一个可能是最为简单的例子,将整个图像映射到一个由三角形条组成的正方形上。首先,我们创建一个由四个顶点组成的顶点数组:

Android 3D系列之纹理篇_第4张图片

 

现在将两个框图叠在一起,所使用的坐标数组的值变得很明显:

Android 3D系列之纹理篇_第5张图片

将其转化为坐标数组:

 float texCoords[] = new float[]{ 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f };


   
   
   
   
纹理坐标对应纹理在物体上的图片位置和方向。
我们还需要传递纹理坐标给系统
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texBuff); gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); //开启纹理坐标数组
我们今天实例的效果:

Android 3D系列之纹理篇_第6张图片

代码:
 
  1. public class CubeRenderer implements Renderer {  
  2.           
  3.     Bitmap bmp;     
  4.     float box[] = new float[] {  
  5.             // FRONT   
  6.             -0.5f, -0.5f,  0.5f,  
  7.              0.5f, -0.5f,  0.5f,  
  8.             -0.5f,  0.5f,  0.5f,  
  9.              0.5f,  0.5f,  0.5f,  
  10.             // BACK   
  11.             -0.5f, -0.5f, -0.5f,  
  12.             -0.5f,  0.5f, -0.5f,  
  13.              0.5f, -0.5f, -0.5f,  
  14.              0.5f,  0.5f, -0.5f,  
  15.             // LEFT   
  16.             -0.5f, -0.5f,  0.5f,  
  17.             -0.5f,  0.5f,  0.5f,  
  18.             -0.5f, -0.5f, -0.5f,  
  19.             -0.5f,  0.5f, -0.5f,  
  20.             // RIGHT   
  21.              0.5f, -0.5f, -0.5f,  
  22.              0.5f,  0.5f, -0.5f,  
  23.              0.5f, -0.5f,  0.5f,  
  24.              0.5f,  0.5f,  0.5f,  
  25.             // TOP   
  26.             -0.5f,  0.5f,  0.5f,  
  27.              0.5f,  0.5f,  0.5f,  
  28.              -0.5f,  0.5f, -0.5f,  
  29.              0.5f,  0.5f, -0.5f,  
  30.             // BOTTOM   
  31.             -0.5f, -0.5f,  0.5f,  
  32.             -0.5f, -0.5f, -0.5f,  
  33.              0.5f, -0.5f,  0.5f,  
  34.              0.5f, -0.5f, -0.5f,  
  35.         };  
  36.       
  37.     float lightAmbient[] = new float[] { 0.5f, 0.5f, 0.6f, 1.0f };  //环境光   
  38.     float lightDiffuse[] = new float[] { 0.6f, 0.6f, 0.6f, 1.0f };//漫反射光   
  39.     float[] lightPos = new float[] {0,0,3,1};  //光源位置   
  40. /* 
  41.  * 因为进行光照处理,你必须告知系统你定义的模型各个面的方向,以便系统计算光影情况,方向的描述是通过向量点来描述的 
  42.  */   
  43.     float norms[] = new float[] { //法向量数组,用于描述个顶点的方向,以此说明各个面的方向   
  44.             // FRONT   
  45.             0f,  0f,  1f, //方向为(0,0,0)至(0,0,1)即Z轴正方向   
  46.             0f,  0f,  1f,  
  47.             0f,  0f,  1f,  
  48.             0f,  0f,  1f,  
  49.             // BACK   
  50.             0f,  0f,  -1f,  
  51.             0f,  0f,  -1f,  
  52.             0f,  0f,  -1f,  
  53.             0f,  0f,  -1f,  
  54.             // LEFT   
  55.             -1f,  0f,  0f,  
  56.             -1f,  0f,  0f,  
  57.             -1f,  0f,  0f,  
  58.             -1f,  0f,  0f,  
  59.             // RIGHT   
  60.             1f, 0f, 0f,  
  61.             1f, 0f, 0f,  
  62.             1f, 0f, 0f,  
  63.             1f, 0f, 0f,  
  64.             // TOP   
  65.             0f,  1f, 0f,  
  66.             0f,  1f, 0f,  
  67.             0f,  1f, 0f,  
  68.             0f,  1f, 0f,  
  69.             // BOTTOM   
  70.             0f,  -1f, 0f,  
  71.             0f,  -1f, 0f,  
  72.             0f,  -1f, 0f,  
  73.             0f,  -1f, 0f  
  74.         };  
  75.   
  76.       
  77.     float texCoords[] = new float[] { //纹理坐标对应数组   
  78.             // FRONT   
  79.              0.0f, 0.0f,  
  80.              1.0f, 0.0f,  
  81.              0.0f, 1.0f,  
  82.              1.0f, 1.0f,  
  83.             // BACK   
  84.              1.0f, 0.0f,  
  85.              1.0f, 1.0f,  
  86.              0.0f, 0.0f,  
  87.              0.0f, 1.0f,  
  88.             // LEFT   
  89.              1.0f, 0.0f,  
  90.              1.0f, 1.0f,  
  91.              0.0f, 0.0f,  
  92.              0.0f, 1.0f,  
  93.             // RIGHT   
  94.              1.0f, 0.0f,  
  95.              1.0f, 1.0f,  
  96.              0.0f, 0.0f,  
  97.              0.0f, 1.0f,  
  98.             // TOP   
  99.              0.0f, 0.0f,  
  100.              1.0f, 0.0f,  
  101.              0.0f, 1.0f,  
  102.              1.0f, 1.0f,  
  103.             // BOTTOM   
  104.              1.0f, 0.0f,  
  105.              1.0f, 1.0f,  
  106.              0.0f, 0.0f,  
  107.              0.0f, 1.0f  
  108.         };  
  109.   
  110.       
  111.     FloatBuffer cubeBuff;  
  112.     FloatBuffer normBuff;  
  113.     FloatBuffer texBuff;  
  114.       
  115.     float xrot = 0.0f;  
  116.     float yrot = 0.0f;  
  117.       
  118.     /** 
  119.      * 将float数组转换存储在字节缓冲数组 
  120.      * @param arr 
  121.      * @return 
  122.      */  
  123.     public FloatBuffer makeFloatBuffer(float[] arr) {  
  124.         ByteBuffer bb = ByteBuffer.allocateDirect(arr.length * 4);//分配缓冲空间,一个float占4个字节   
  125.         bb.order(ByteOrder.nativeOrder()); //设置字节顺序, 其中ByteOrder.nativeOrder()是获取本机字节顺序   
  126.         FloatBuffer fb = bb.asFloatBuffer(); //转换为float型   
  127.         fb.put(arr);        //添加数据   
  128.         fb.position(0);      //设置数组的起始位置   
  129.         return fb;  
  130.     }  
  131.       
  132.     public CubeRenderer(Context c) {  
  133.         // TODO Auto-generated constructor stub   
  134.         cubeBuff = makeFloatBuffer(box);//转换float数组   
  135.         normBuff = makeFloatBuffer(norms);  
  136.         texBuff = makeFloatBuffer(texCoords);     
  137.         bmp = BitmapFactory.decodeResource(c.getResources(), R.drawable.face);  
  138.     }  
  139.       
  140.       
  141.     protected void init(GL10 gl) {  
  142.         gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);//设置清屏时背景的颜色,R,G,B,A   
  143.           
  144.         gl.glEnable(GL10.GL_LIGHTING); //启用光照   
  145.         gl.glEnable(GL10.GL_LIGHT0);  //开启光源0   
  146.         //设置光照参数,也可以使用默认的,不设置   
  147.         gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbient, 0);  
  148.         gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuse, 0);  
  149.         gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPos, 0);  
  150.           
  151.         gl.glNormalPointer(GL10.GL_FLOAT, 0, normBuff);  
  152.         gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);  
  153.           
  154.         //使用纹理步骤:   
  155.         // 1.开启贴图   
  156.         gl.glEnable(GL10.GL_TEXTURE_2D);  
  157.           
  158.         // 2.生成纹理ID   
  159.         int[] tmp_tex = new int[1];//尽管只有一个纹理,但使用一个元素的数组   
  160.         //glGenTextures(申请个数,存放数组,偏移值)   
  161.         gl.glGenTextures(1, tmp_tex, 0); //向系统申请可用的,用于标示纹理的ID   
  162.         int texture = tmp_tex[0];  
  163.           
  164.         //3.绑定纹理,使得指定纹理处于活动状态。一次只能激活一个纹理   
  165.         gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);  
  166.           
  167.         //4.绑定纹理数据,传入指定图片   
  168.         GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);    
  169.           
  170.         //5.传递各个顶点对应的纹理坐标   
  171.         gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texBuff);  
  172.         gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); //开启纹理坐标数组   
  173.           
  174.         //6.设置纹理参数 (可选)   
  175.         /*下面的两行参数告诉OpenGL在显示图像时,当它比放大得原始的纹理大  
  176.         *( GL_TEXTURE_MAG_FILTER )或缩小得比原始得纹理小( GL_TEXTURE_MIN_FILTER ) 
  177.         *时OpenGL采用的滤波方式。通常这两种情况下我都采用 GL_LINEAR 。这使得纹理从很远处 
  178.         *到离屏幕很近时都平滑显示。使用 GL_LINEAR 需要CPU和显卡做更多的运算。如果您的机器很慢, 
  179.         *您也许应该采用 GL_NEAREST 。过滤的纹理在放大的时候,看起来马赛克的很。您也可以结合这 
  180.         *两种滤波方式。在近处时使用 GL_LINEAR ,远处时 GL_NEAREST 。 
  181.         **/  
  182.         gl.glTexParameterx(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_MAG_FILTER,GL10.GL_LINEAR);     
  183.         gl.glTexParameterx(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_MIN_FILTER,GL10.GL_LINEAR);  
  184.               
  185.           
  186.         gl.glEnable(GL10.GL_DEPTH_TEST); //启用深度缓存   
  187.         gl.glEnable(GL10.GL_CULL_FACE);  //启用背面剪裁   
  188.         gl.glClearDepthf(1.0f);    // 设置深度缓存值   
  189.         gl.glDepthFunc(GL10.GL_LEQUAL);  // 设置深度缓存比较函数,GL_LEQUAL表示新的像素的深度缓存值小于等于当前像素的深度缓存值(通过gl.glClearDepthf(1.0f)设置)时通过深度测试      
  190.         gl.glShadeModel(GL10.GL_SMOOTH);// 设置阴影模式GL_SMOOTH   
  191.     }  
  192.       
  193.     @Override  
  194.     public void onSurfaceCreated(GL10 gl, EGLConfig config) {  
  195.         // TODO Auto-generated method stub   
  196.         init(gl);  
  197.     }  
  198.       
  199.     @Override  
  200.     public void onSurfaceChanged(GL10 gl, int w, int h) {  
  201.         // TODO Auto-generated method stub   
  202.         gl.glViewport(00, w, h); //设置视窗   
  203.         gl.glMatrixMode(GL10.GL_PROJECTION); // 设置投影矩阵   
  204.         gl.glLoadIdentity();  //设置矩阵为单位矩阵,相当于重置矩阵          
  205.         GLU.gluPerspective(gl, 45.0f, ((float) w) / h, 0.1f, 10f);//设置透视范围     
  206.     }  
  207.       
  208.     @Override  
  209.     public void onDrawFrame(GL10 gl) {  
  210.         // TODO Auto-generated method stub   
  211.         gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);// 清除屏幕和深度缓存   
  212.           
  213.         gl.glMatrixMode(GL10.GL_MODELVIEW);   //切换至模型观察矩阵   
  214.         gl.glLoadIdentity();// 重置当前的模型观察矩阵   
  215.         GLU.gluLookAt(gl, 003000010);//设置视点和模型中心位置   
  216.       
  217.         gl.glVertexPointer(3, GL10.GL_FLOAT, 0, cubeBuff);//设置顶点数据   
  218.         gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);  
  219.       
  220.         gl.glRotatef(xrot, 100);  //绕着(0,0,0)与(1,0,0)即x轴旋转   
  221.         gl.glRotatef(yrot, 010);  
  222.           
  223.         gl.glColor4f(1.0f, 001.0f);   //设置颜色,红色   
  224.         gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 04);  //绘制正方型FRONT面   
  225.         gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 44);  
  226.       
  227.         gl.glColor4f(01.0f, 01.0f);  
  228.         gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 84);  
  229.         gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 124);  
  230.           
  231.         gl.glColor4f(001.0f, 1.0f);  
  232.         gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 164);  
  233.         gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 204);  
  234.       
  235.         xrot += 0.5f;  
  236.         yrot += 0.5f;  
  237.     }  
  238.   
  239. }  

Android 3D系列之纹理篇工程源码地址:

免费下载地址在 http://linux.linuxidc.com/

用户名与密码都是www.linuxidc.com

具体下载目录在 /pub/Android源码集锦/2011年/9月/Android 3D系列之纹理篇工程源码/

你可能感兴趣的:(Android 3D系列之纹理篇)